Skip to content

Commit 233cada

Browse files
authored
Merge pull request #90 from mike1k/sanity
Error on unknown language & unacceptable source file.
2 parents e69427b + 58a5a93 commit 233cada

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

include/project_parser.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ struct Project {
180180
std::string project_version;
181181
std::string project_description;
182182
std::vector<std::string> project_languages;
183+
bool project_allow_unknown_languages = false;
183184
MsvcRuntimeType project_msvc_runtime = msvc_last;
184185
Condition<std::string> cmake_before;
185186
Condition<std::string> cmake_after;

src/cmake_generator.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,35 @@
1414
namespace cmkr {
1515
namespace gen {
1616

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+
1746
static std::string format(const char *format, tsl::ordered_map<std::string, std::string> variables) {
1847
std::string s = format;
1948
for (const auto &itr : variables) {
@@ -510,6 +539,17 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
510539
auto is_root_project = parent_project == nullptr;
511540

512541
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+
513553
Generator gen(project);
514554

515555
// Helper lambdas for more convenient CMake generation
@@ -837,6 +877,31 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
837877
gen.conditional_cmake(subdir.cmake_after);
838878
}
839879

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+
840905
if (!project.targets.empty()) {
841906
auto project_root = project.root();
842907
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) {
9461011
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
9471012
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
9481013
}
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+
9491030
if (sources_with_set) {
9501031
// This is a sanity check to make sure the unconditional sources are first
9511032
if (!condition.empty()) {

src/project_parser.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
289289
project.optional("version", project_version);
290290
project.optional("description", project_description);
291291
project.optional("languages", project_languages);
292+
project.optional("allow-unknown-languages", project_allow_unknown_languages);
292293
project.optional("cmake-before", cmake_before);
293294
project.optional("cmake-after", cmake_after);
294295
project.optional("include-before", include_before);

0 commit comments

Comments
 (0)