|
14 | 14 | namespace cmkr { |
15 | 15 | namespace gen { |
16 | 16 |
|
| 17 | +/* |
| 18 | +Location: CMake/share/cmake-3.26/Modules |
| 19 | +rg "set\(CMAKE_(.+)_SOURCE_FILE_EXTENSIONS" |
| 20 | +
|
| 21 | +Links: |
| 22 | +- https://gitlab.kitware.com/cmake/cmake/-/issues/24340 |
| 23 | +- https://cmake.org/cmake/help/latest/command/enable_language.html |
| 24 | +*/ |
| 25 | + |
| 26 | +static tsl::ordered_map<std::string, std::vector<std::string>> known_languages = { |
| 27 | + {"ASM", {".s", ".S", ".asm", ".abs", ".msa", ".s90", ".s43", ".s85", ".s51"}}, |
| 28 | + {"ASM-ATT", {".s", ".asm"}}, |
| 29 | + {"ASM_MARMASM", {".asm"}}, |
| 30 | + {"ASM_MASM", {".asm"}}, |
| 31 | + {"ASM_NASM", {".nasm", ".asm"}}, |
| 32 | + {"C", {".c", ".m"}}, |
| 33 | + {"CSharp", {".cs"}}, |
| 34 | + {"CUDA", {".cu"}}, |
| 35 | + {"CXX", {".C", ".M", ".c++", ".cc", ".cpp", ".cxx", ".m", ".mm", ".mpp", ".CPP", ".ixx", ".cppm"}}, |
| 36 | + {"Fortran", {".f", ".F", ".fpp", ".FPP", ".f77", ".F77", ".f90", ".F90", ".for", ".For", ".FOR", ".f95", ".F95", ".cuf", ".CUF"}}, |
| 37 | + {"HIP", {".hip"}}, |
| 38 | + {"ISPC", {".ispc"}}, |
| 39 | + {"Java", {".java"}}, |
| 40 | + {"OBJC", {".m"}}, |
| 41 | + {"OBJCXX", {".M", ".m", ".mm"}}, |
| 42 | + {"RC", {".rc", ".RC"}}, |
| 43 | + {"Swift", {".swift"}}, |
| 44 | +}; |
| 45 | + |
17 | 46 | static std::string format(const char *format, tsl::ordered_map<std::string, std::string> variables) { |
18 | 47 | std::string s = format; |
19 | 48 | for (const auto &itr : variables) { |
@@ -510,6 +539,17 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { |
510 | 539 | auto is_root_project = parent_project == nullptr; |
511 | 540 |
|
512 | 541 | parser::Project project(parent_project, path, false); |
| 542 | + |
| 543 | + for (auto const &lang : project.project_languages) { |
| 544 | + if (known_languages.find(lang) == known_languages.end()) { |
| 545 | + if (project.project_allow_unknown_languages) { |
| 546 | + printf("Unknown language '%s' specified\n", lang.c_str()); |
| 547 | + } else { |
| 548 | + throw std::runtime_error("Unknown language '" + lang + "' specified"); |
| 549 | + } |
| 550 | + } |
| 551 | + } |
| 552 | + |
513 | 553 | Generator gen(project); |
514 | 554 |
|
515 | 555 | // Helper lambdas for more convenient CMake generation |
@@ -837,6 +877,31 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { |
837 | 877 | gen.conditional_cmake(subdir.cmake_after); |
838 | 878 | } |
839 | 879 |
|
| 880 | + // The implicit default is ["C", "CXX"], so make sure this list isn't |
| 881 | + // empty or projects without languages explicitly defined will error. |
| 882 | + auto project_languages = project.project_languages; |
| 883 | + if (project_languages.empty()) |
| 884 | + project_languages = {"C", "CXX"}; |
| 885 | + |
| 886 | + // All acceptable extensions based off our given languages. |
| 887 | + tsl::ordered_set<std::string> project_extensions; |
| 888 | + for (const auto &language : project_languages) { |
| 889 | + auto itr = known_languages.find(language); |
| 890 | + if (itr != known_languages.end()) { |
| 891 | + project_extensions.insert(itr->second.begin(), itr->second.end()); |
| 892 | + } |
| 893 | + } |
| 894 | + |
| 895 | + auto contains_language_source = [&project_extensions](const std::vector<std::string>& sources) { |
| 896 | + for (const auto &source : sources) { |
| 897 | + auto extension = fs::path(source).extension().string(); |
| 898 | + if (project_extensions.count(extension) > 0) { |
| 899 | + return true; |
| 900 | + } |
| 901 | + } |
| 902 | + return false; |
| 903 | + }; |
| 904 | + |
840 | 905 | if (!project.targets.empty()) { |
841 | 906 | auto project_root = project.root(); |
842 | 907 | for (size_t i = 0; i < project.targets.size(); i++) { |
@@ -946,6 +1011,22 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { |
946 | 1011 | auto source_key = condition.empty() ? "sources" : (condition + ".sources"); |
947 | 1012 | throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files"); |
948 | 1013 | } |
| 1014 | + |
| 1015 | + // Make sure there are source files for the languages used by the project |
| 1016 | + switch (target.type) { |
| 1017 | + case parser::target_executable: |
| 1018 | + case parser::target_library: |
| 1019 | + case parser::target_shared: |
| 1020 | + case parser::target_static: |
| 1021 | + case parser::target_object: |
| 1022 | + if (!contains_language_source(sources)) { |
| 1023 | + throw std::runtime_error("There were no source files linked within the target " + target.name); |
| 1024 | + } |
| 1025 | + break; |
| 1026 | + default: |
| 1027 | + break; |
| 1028 | + } |
| 1029 | + |
949 | 1030 | if (sources_with_set) { |
950 | 1031 | // This is a sanity check to make sure the unconditional sources are first |
951 | 1032 | if (!condition.empty()) { |
|
0 commit comments