Skip to content

Commit 2b7ee72

Browse files
committed
Separate template parsing and generation
* Reuse type for specifying template to use * Add support for template add-function * Add support for template pass-sources-to-add-function
1 parent 61d5e64 commit 2b7ee72

File tree

8 files changed

+423
-168
lines changed

8 files changed

+423
-168
lines changed

include/enum_helper.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <iostream>
55
#include <sstream>
66
#include <string>
7+
#include <vector>
78

89
// This is the type that will hold all the strings.
910
// Each enumeration type will declare its own specialization.
@@ -12,7 +13,7 @@
1213
// be no definition of a generic version).
1314
template <typename T>
1415
struct enumStrings {
15-
static char const *data[];
16+
static std::vector<std::string> data;
1617
};
1718

1819
// This is a utility type.
@@ -43,8 +44,8 @@ std::istream &operator>>(std::istream &str, enumRefHolder<T> const &data) {
4344
// These two can be made easier to read in C++11
4445
// using std::begin() and std::end()
4546
//
46-
static auto begin = std::begin(enumStrings<T>::data);
47-
static auto end = std::end(enumStrings<T>::data);
47+
auto begin = std::begin(enumStrings<T>::data);
48+
auto end = std::end(enumStrings<T>::data);
4849

4950
auto find = std::find(begin, end, value);
5051
if (find != end) {

include/project_parser.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ enum TargetType {
5656
target_interface,
5757
target_custom,
5858
target_object,
59+
target_template,
60+
target_COUNT,
5961
};
6062

6163
struct Target {
6264
std::string name;
6365
TargetType type = {};
66+
std::string type_string;
6467

6568
ConditionVector headers;
6669
ConditionVector sources;
@@ -100,6 +103,12 @@ struct Target {
100103
ConditionVector include_after;
101104
};
102105

106+
struct Template {
107+
Target outline;
108+
std::string add_function;
109+
bool pass_sources_to_add_function = false;
110+
};
111+
103112
struct Test {
104113
std::string name;
105114
std::string condition;
@@ -160,12 +169,12 @@ struct Project {
160169
std::vector<Package> packages;
161170
Vcpkg vcpkg;
162171
std::vector<Content> contents;
172+
std::vector<Template> templates;
163173
std::vector<Target> targets;
164174
std::vector<Test> tests;
165175
std::vector<Install> installs;
166176
tsl::ordered_map<std::string, std::string> conditions;
167177
std::vector<Subdir> subdirs;
168-
std::vector<Target> templates;
169178

170179
Project(const Project *parent, const std::string &path, bool build);
171180
};

src/cmake_generator.cpp

Lines changed: 114 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <fstream>
99
#include <sstream>
1010
#include <stdexcept>
11+
#include <memory>
1112

1213
namespace cmkr {
1314
namespace gen {
@@ -771,12 +772,31 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
771772
}
772773

773774
const auto &target = project.targets[i];
775+
const parser::Template *tmplate = nullptr;
776+
std::unique_ptr<ConditionScope> tmplate_cs{};
777+
774778
comment("Target " + target.name);
775779

780+
// Check if this target is using a template.
781+
if (target.type == parser::target_template) {
782+
for (const auto &t : project.templates) {
783+
if (target.type_string == t.outline.name) {
784+
tmplate = &t;
785+
tmplate_cs = std::make_unique<ConditionScope>(gen, tmplate->outline.condition);
786+
}
787+
}
788+
}
789+
776790
ConditionScope cs(gen, target.condition);
777791

778792
cmd("set")("CMKR_TARGET", target.name);
779793

794+
if (tmplate != nullptr) {
795+
gen.handle_condition(tmplate->outline.include_before,
796+
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
797+
gen.handle_condition(tmplate->outline.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
798+
}
799+
780800
gen.handle_condition(target.include_before,
781801
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
782802
gen.handle_condition(target.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
@@ -785,6 +805,18 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
785805

786806
bool added_toml = false;
787807
cmd("set")(sources_var, RawArg("\"\"")).endl();
808+
809+
if (tmplate != nullptr) {
810+
gen.handle_condition(tmplate->outline.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
811+
auto sources = expand_cmake_paths(condition_sources, path);
812+
if (sources.empty()) {
813+
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
814+
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
815+
}
816+
cmd("list")("APPEND", sources_var, sources);
817+
});
818+
}
819+
788820
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
789821
auto sources = expand_cmake_paths(condition_sources, path);
790822
if (sources.empty()) {
@@ -798,66 +830,89 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
798830
cmd("list")("APPEND", sources_var, sources);
799831
});
800832

801-
if (!added_toml && target.type != parser::target_interface) {
833+
auto target_type = target.type;
834+
835+
if (tmplate != nullptr) {
836+
target_type = tmplate->outline.type;
837+
}
838+
839+
if (!added_toml && target_type != parser::target_interface) {
802840
cmd("list")("APPEND", sources_var, std::vector<std::string>{"cmake.toml"}).endl();
803841
}
804842
cmd("set")("CMKR_SOURCES", "${" + sources_var + "}");
805843

806844
std::string add_command;
807-
std::string target_type;
845+
std::string target_type_string;
808846
std::string target_scope;
809-
switch (target.type) {
847+
848+
switch (target_type) {
810849
case parser::target_executable:
811850
add_command = "add_executable";
812-
target_type = "";
851+
target_type_string = "";
813852
target_scope = "PRIVATE";
814853
break;
815854
case parser::target_library:
816855
add_command = "add_library";
817-
target_type = "";
856+
target_type_string = "";
818857
target_scope = "PUBLIC";
819858
break;
820859
case parser::target_shared:
821860
add_command = "add_library";
822-
target_type = "SHARED";
861+
target_type_string = "SHARED";
823862
target_scope = "PUBLIC";
824863
break;
825864
case parser::target_static:
826865
add_command = "add_library";
827-
target_type = "STATIC";
866+
target_type_string = "STATIC";
828867
target_scope = "PUBLIC";
829868
break;
830869
case parser::target_interface:
831870
add_command = "add_library";
832-
target_type = "INTERFACE";
871+
target_type_string = "INTERFACE";
833872
target_scope = "INTERFACE";
834873
break;
835874
case parser::target_custom:
836875
// TODO: add proper support, this is hacky
837876
add_command = "add_custom_target";
838-
target_type = "SOURCES";
877+
target_type_string = "SOURCES";
839878
target_scope = "PUBLIC";
840879
break;
841880
case parser::target_object:
842881
// NOTE: This is properly supported since 3.12
843882
add_command = "add_library";
844-
target_type = "OBJECT";
883+
target_type_string = "OBJECT";
845884
target_scope = "PUBLIC";
846885
break;
847886
default:
848887
throw std::runtime_error("Unimplemented enum value");
849888
}
850889

851-
cmd(add_command)(target.name, target_type).endl();
890+
// Handle custom add commands from templates.
891+
if (tmplate != nullptr && !tmplate->add_function.empty()) {
892+
add_command = tmplate->add_function;
893+
target_type_string = ""; // TODO: let templates supply options to the add_command here?
894+
895+
if (tmplate->pass_sources_to_add_function) {
896+
cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}");
897+
} else {
898+
// clang-format off
899+
cmd("if")(sources_var);
900+
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
901+
cmd("endif")().endl();
902+
// clang-format on
903+
}
904+
} else {
905+
cmd(add_command)(target.name, target_type_string).endl();
852906

853-
// clang-format off
854-
cmd("if")(sources_var);
855-
cmd("target_sources")(target.name, target.type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
856-
cmd("endif")().endl();
857-
// clang-format on
907+
// clang-format off
908+
cmd("if")(sources_var);
909+
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
910+
cmd("endif")().endl();
911+
// clang-format on
912+
}
858913

859914
// The first executable target will become the Visual Studio startup project
860-
if (target.type == parser::target_executable) {
915+
if (target_type == parser::target_executable) {
861916
cmd("get_directory_property")("CMKR_VS_STARTUP_PROJECT", "DIRECTORY", "${PROJECT_SOURCE_DIR}", "DEFINITION", "VS_STARTUP_PROJECT");
862917
// clang-format off
863918
cmd("if")("NOT", "CMKR_VS_STARTUP_PROJECT");
@@ -879,6 +934,34 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
879934
[&](const std::string &, const std::vector<std::string> &args) { cmd(command)(target.name, scope, args); });
880935
};
881936

937+
if (tmplate != nullptr) {
938+
const auto &outline = tmplate->outline;
939+
940+
target_cmd("target_compile_definitions", outline.compile_definitions, target_scope);
941+
target_cmd("target_compile_definitions", outline.private_compile_definitions, "PRIVATE");
942+
943+
target_cmd("target_compile_features", outline.compile_features, target_scope);
944+
target_cmd("target_compile_features", outline.private_compile_features, "PRIVATE");
945+
946+
target_cmd("target_compile_options", outline.compile_options, target_scope);
947+
target_cmd("target_compile_options", outline.private_compile_options, "PRIVATE");
948+
949+
target_cmd("target_include_directories", outline.include_directories, target_scope);
950+
target_cmd("target_include_directories", outline.private_include_directories, "PRIVATE");
951+
952+
target_cmd("target_link_directories", outline.link_directories, target_scope);
953+
target_cmd("target_link_directories", outline.private_link_directories, "PRIVATE");
954+
955+
target_cmd("target_link_libraries", outline.link_libraries, target_scope);
956+
target_cmd("target_link_libraries", outline.private_link_libraries, "PRIVATE");
957+
958+
target_cmd("target_link_options", outline.link_options, target_scope);
959+
target_cmd("target_link_options", outline.private_link_options, "PRIVATE");
960+
961+
target_cmd("target_precompile_headers", outline.precompile_headers, target_scope);
962+
target_cmd("target_precompile_headers", outline.private_precompile_headers, "PRIVATE");
963+
}
964+
882965
target_cmd("target_compile_definitions", target.compile_definitions, target_scope);
883966
target_cmd("target_compile_definitions", target.private_compile_definitions, "PRIVATE");
884967

@@ -903,8 +986,14 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
903986
target_cmd("target_precompile_headers", target.precompile_headers, target_scope);
904987
target_cmd("target_precompile_headers", target.private_precompile_headers, "PRIVATE");
905988

906-
if (!target.properties.empty()) {
907-
gen.handle_condition(target.properties, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &properties) {
989+
if (!target.properties.empty() || (tmplate != nullptr && !tmplate->outline.properties.empty())) {
990+
auto props = target.properties;
991+
992+
if (tmplate != nullptr) {
993+
props.insert(tmplate->outline.properties.begin(), tmplate->outline.properties.end());
994+
}
995+
996+
gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &properties) {
908997
cmd("set_target_properties")(target.name, "PROPERTIES", properties);
909998
});
910999
}
@@ -913,6 +1002,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
9131002
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
9141003
gen.handle_condition(target.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
9151004

1005+
if (tmplate != nullptr) {
1006+
gen.handle_condition(tmplate->outline.include_after,
1007+
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
1008+
gen.handle_condition(tmplate->outline.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
1009+
}
1010+
9161011
cmd("unset")("CMKR_TARGET");
9171012
cmd("unset")("CMKR_SOURCES");
9181013
}

0 commit comments

Comments
 (0)