From 4772a2b683d06ed7c2eaebb574ee752ce079ae50 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Sun, 1 Feb 2026 16:12:27 +0100 Subject: [PATCH] Add support for conditional [project].languages Closes #184 --- include/project_parser.hpp | 2 +- src/cmake_generator.cpp | 85 +++++++++++++++++++++----------------- src/project_parser.cpp | 5 +++ 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/include/project_parser.hpp b/include/project_parser.hpp index f545bd6..43d40db 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -194,7 +194,7 @@ struct Project { std::string project_name; std::string project_version; std::string project_description; - std::vector project_languages; + ConditionVector project_languages; bool project_allow_unknown_languages = false; MsvcRuntimeType project_msvc_runtime = msvc_last; Condition cmake_before; diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index 6aa2138..0af5e7a 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -725,16 +725,41 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { parser::Project project(parent_project, path, false); - for (const auto &lang : project.project_languages) { - if (known_languages.find(lang) == known_languages.end()) { - if (project.project_allow_unknown_languages) { - printf("[warning] Unknown language '%s' specified\n", lang.c_str()); - } else { - throw std::runtime_error("Unknown language '" + lang + "' specified"); - } + tsl::ordered_set flat_project_languages; + for (const auto &itr : project.project_languages) { + for (const auto &language : itr.second) { + flat_project_languages.insert(language); } } + // Reference: https://gitlab.kitware.com/cmake/cmake/-/issues/24340#note_1304703 + flat_project_languages.insert("RC"); + + // All acceptable extensions based off our given languages. + tsl::ordered_set project_extensions; + for (const auto &language : flat_project_languages) { + auto itr = known_languages.find(language); + if (itr == known_languages.end()) { + if (project.project_allow_unknown_languages) { + printf("[warning] Unknown language '%s' specified\n", language.c_str()); + } else { + throw std::runtime_error("Unknown language '" + language + "' specified"); + } + } else { + project_extensions.insert(itr->second.begin(), itr->second.end()); + } + } + + auto contains_language_source = [&project_extensions](const std::vector &sources) { + for (const auto &source : sources) { + auto extension = fs::path(source).extension().string(); + if (project_extensions.count(extension) > 0) { + return true; + } + } + return false; + }; + Generator gen(project, path); // Helper lambdas for more convenient CMake generation @@ -912,17 +937,29 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { gen.conditional_cmake(project.cmake_before); if (!project.project_name.empty()) { - auto languages = std::make_pair("LANGUAGES", project.project_languages); + auto languages = std::make_pair("LANGUAGES", project.project_languages[""]); auto version = std::make_pair("VERSION", project.project_version); auto description = std::make_pair("DESCRIPTION", project.project_description); - cmd("project")(project.project_name, languages, version, description).endl(); + cmd("project")(project.project_name, languages, version, description); - for (const auto &language : project.project_languages) { + for (const auto &language : project.project_languages[""]) { if (language == "CSharp") { cmd("include")("CSharpUtilities").endl(); break; } } + + gen.handle_condition(project.project_languages, [&cmd](const std::string &condition, const std::vector &languages) { + if (!condition.empty()) { + cmd("enable_language")(languages).endl(); + for (const auto &language : languages) { + if (language == "CSharp") { + cmd("include")("CSharpUtilities").endl(); + break; + } + } + } + }); } gen.conditional_includes(project.include_after); @@ -1138,34 +1175,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { gen.conditional_cmake(subdir.cmake_after); } - // The implicit default is ["C", "CXX"], so make sure this list isn't - // empty or projects without languages explicitly defined will error. - auto project_languages = project.project_languages; - if (project_languages.empty()) - project_languages = {"C", "CXX"}; - - // Reference: https://gitlab.kitware.com/cmake/cmake/-/issues/24340#note_1304703 - project_languages.push_back("RC"); - - // All acceptable extensions based off our given languages. - tsl::ordered_set project_extensions; - for (const auto &language : project_languages) { - auto itr = known_languages.find(language); - if (itr != known_languages.end()) { - project_extensions.insert(itr->second.begin(), itr->second.end()); - } - } - - auto contains_language_source = [&project_extensions](const std::vector &sources) { - for (const auto &source : sources) { - auto extension = fs::path(source).extension().string(); - if (project_extensions.count(extension) > 0) { - return true; - } - } - return false; - }; - if (!project.targets.empty()) { auto project_root = project.root(); for (size_t i = 0; i < project.targets.size(); i++) { diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 9e57444..25ef31e 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -301,6 +301,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p project.required("name", project_name); project.optional("version", project_version); project.optional("description", project_description); + + // The implicit default is ["C", "CXX"], so make sure this list isn't + // empty or projects without languages explicitly defined will error. + project_languages[""] = {"C", "CXX"}; + project.optional("languages", project_languages); project.optional("allow-unknown-languages", project_allow_unknown_languages); project.optional("cmake-before", cmake_before);