diff --git a/docs/examples/objective-c.md b/docs/examples/objective-c.md new file mode 100644 index 0000000..584e214 --- /dev/null +++ b/docs/examples/objective-c.md @@ -0,0 +1,30 @@ +--- +# Automatically generated from tests/objective-c/cmake.toml - DO NOT EDIT +layout: default +title: Objective-C +permalink: /examples/objective-c +parent: Examples +nav_order: 11 +--- + +# Objective-C + +Add Objective-C sources on Apple platforms: + +```toml +[project] +name = "objective-c" +description = "Objective-C" +languages = ["C"] +apple.languages = ["OBJC"] + +[target.hello] +type = "executable" +sources = ["src/main.c"] +apple.sources = ["src/apple.m"] +apple.link-libraries = ["$"] +``` + + + +This page was automatically generated from [tests/objective-c/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/objective-c/cmake.toml). 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()) { + 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", lang.c_str()); + printf("[warning] Unknown language '%s' specified\n", language.c_str()); } else { - throw std::runtime_error("Unknown language '" + lang + "' specified"); + 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); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 22aa4b4..a4422a5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -122,3 +122,12 @@ if(WIN32) # windows ) endif() +add_test( + NAME + objective-c + WORKING_DIRECTORY + "${CMAKE_CURRENT_LIST_DIR}/objective-c" + COMMAND + "$" + build +) diff --git a/tests/cmake.toml b/tests/cmake.toml index 338b37d..177fedd 100644 --- a/tests/cmake.toml +++ b/tests/cmake.toml @@ -65,3 +65,9 @@ name = "relative-paths" working-directory = "relative-paths" command = "$" arguments = ["build"] + +[[test]] +name = "objective-c" +working-directory = "objective-c" +command = "$" +arguments = ["build"] diff --git a/tests/objective-c/cmake.toml b/tests/objective-c/cmake.toml new file mode 100644 index 0000000..4cddcfc --- /dev/null +++ b/tests/objective-c/cmake.toml @@ -0,0 +1,13 @@ +# Add Objective-C sources on Apple platforms: + +[project] +name = "objective-c" +description = "Objective-C" +languages = ["C"] +apple.languages = ["OBJC"] + +[target.hello] +type = "executable" +sources = ["src/main.c"] +apple.sources = ["src/apple.m"] +apple.link-libraries = ["$"] diff --git a/tests/objective-c/src/apple.m b/tests/objective-c/src/apple.m new file mode 100644 index 0000000..0453f46 --- /dev/null +++ b/tests/objective-c/src/apple.m @@ -0,0 +1,7 @@ +#import + +void platform_specific() { + NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput]; + NSString *message = @"Hello from Objective-C!\n"; + [stdout writeData:[message dataUsingEncoding:NSUTF8StringEncoding] error:nil]; +} diff --git a/tests/objective-c/src/main.c b/tests/objective-c/src/main.c new file mode 100644 index 0000000..a034389 --- /dev/null +++ b/tests/objective-c/src/main.c @@ -0,0 +1,14 @@ +#include + +#ifdef __APPLE__ +void platform_specific(); +#else +void platform_specific() { + puts("This is not an Apple platform."); +} +#endif + +int main() { + platform_specific(); + return 0; +}