88#include < fstream>
99#include < sstream>
1010#include < stdexcept>
11+ #include < memory>
1112
1213namespace cmkr {
1314namespace 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