Skip to content

Commit 5a6ac44

Browse files
authored
Merge pull request #46 from build-cpp/expanded-conditions
Support conditions everywhere
2 parents 490869b + 9db82df commit 5a6ac44

File tree

4 files changed

+86
-51
lines changed

4 files changed

+86
-51
lines changed

docs/cmake-toml.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8"
4747
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4"
4848
```
4949

50+
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
51+
5052
You can also prefix most keys with `condition.` to represent a conditional:
5153

5254
```toml
@@ -56,7 +58,7 @@ sources = ["src/main.cpp"]
5658
windows.sources = ["src/windows_specific.cpp"]
5759
```
5860

59-
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions. The following conditions are predefined (you can override them if you desire):
61+
The following conditions are predefined (you can override them if you desire):
6062

6163
```toml
6264
[conditions]
@@ -74,7 +76,7 @@ msvc = "MSVC"
7476

7577
```toml
7678
[subdir.mysubdir]
77-
condition = "linux"
79+
condition = "mycondition"
7880
cmake-before = """
7981
message(STATUS "CMake injected before the add_subdirectory() call"
8082
"""
@@ -101,11 +103,8 @@ To specify package features you can use the following syntax: `imgui[docking-exp
101103
## Packages
102104

103105
```toml
104-
[find-package]
105-
mypackage = { version = "1.0", required = true, config = true, components = ["mycomponent"] }
106-
107-
# Alternative syntax
108106
[find-package.mypackage]
107+
condition = "mycondition"
109108
version = "1.0"
110109
required = true
111110
config = true
@@ -117,22 +116,27 @@ components = ["mycomponent"]
117116
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
118117

119118
```toml
120-
[fetch-content]
121-
gitcontent = { git = "https://github.com/myuser/gitcontent", tag = "v0.1" }
122-
svncontent = { svn = "https://svn-host.com/url", rev = "svn_rev" }
123-
urlcontent = { url = "https://content-host.com/urlcontent.zip", hash = "123123123123" }
124-
125-
# Alternative syntax
126119
[fetch-content.gitcontent]
120+
condition = "mycondition"
127121
git = "https://github.com/myuser/gitcontent"
128122
tag = "v0.1"
123+
124+
[fetch-content.svncontent]
125+
condition = "mycondition"
126+
svn = "https://svn-host.com/url"
127+
rev = "svn_rev"
128+
129+
[fetch-content.urlcontent]
130+
condition = "mycondition"
131+
url = "https://content-host.com/urlcontent.zip"
132+
hash = "123123123123"
129133
```
130134

131135
## Targets
132136

133137
```toml
134138
[target.mytarget]
135-
condition = "linux"
139+
condition = "mycondition"
136140
alias = "mytarget::mytarget"
137141
type = "static" # executable, shared (DLL), static, interface, object, library, custom
138142
headers = ["src/mytarget.h"]
@@ -180,6 +184,7 @@ FOLDER = "MyFolder"
180184
```toml
181185
# You can declare as many as you want like this, but the name has to be unique
182186
[[test]]
187+
condition = "mycondition"
183188
name = "mytest"
184189
command = "$<TARGET_FILE:mytest>"
185190
arguments = ["arg1", "arg2"]
@@ -189,6 +194,7 @@ working-directory = "mytest-dir"
189194

190195
```toml
191196
[[install]]
197+
condition = "mycondition"
192198
targets = ["mytarget", "mytest"]
193199
destination = ["bin"]
194200
files = ["content/my.png"]

include/project_parser.hpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
namespace cmkr {
99
namespace parser {
1010

11+
template <typename T>
12+
using Condition = tsl::ordered_map<std::string, T>;
13+
14+
using ConditionVector = Condition<std::vector<std::string>>;
15+
1116
struct Setting {
1217
std::string name;
1318
std::string comment;
@@ -24,6 +29,7 @@ struct Option {
2429

2530
struct Package {
2631
std::string name;
32+
std::string condition;
2733
std::string version;
2834
bool required = true;
2935
bool config = false;
@@ -52,11 +58,6 @@ enum TargetType {
5258
target_object,
5359
};
5460

55-
template <typename T>
56-
using Condition = tsl::ordered_map<std::string, T>;
57-
58-
using ConditionVector = Condition<std::vector<std::string>>;
59-
6061
struct Target {
6162
std::string name;
6263
TargetType type = {};
@@ -101,13 +102,15 @@ struct Target {
101102

102103
struct Test {
103104
std::string name;
105+
std::string condition;
104106
std::vector<std::string> configurations;
105107
std::string working_directory;
106108
std::string command;
107109
std::vector<std::string> arguments;
108110
};
109111

110112
struct Install {
113+
std::string condition;
111114
std::vector<std::string> targets;
112115
std::vector<std::string> files;
113116
std::vector<std::string> dirs;
@@ -126,6 +129,7 @@ struct Subdir {
126129

127130
struct Content {
128131
std::string name;
132+
std::string condition;
129133
tsl::ordered_map<std::string, std::string> arguments;
130134
};
131135

src/cmake_generator.cpp

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,10 @@ static std::string tolf(const std::string &str) {
316316
};
317317

318318
struct Generator {
319-
Generator(parser::Project &project) : project(project) {}
319+
Generator(const parser::Project &project) : project(project) {}
320320
Generator(const Generator &) = delete;
321321

322-
parser::Project &project;
322+
const parser::Project &project;
323323
std::stringstream ss;
324324
int indent = 0;
325325

@@ -383,11 +383,7 @@ struct Generator {
383383
for (const auto &itr : value) {
384384
const auto &condition = itr.first;
385385
if (!condition.empty()) {
386-
if (project.conditions.count(condition) == 0) {
387-
// TODO: somehow print line number information here?
388-
throw std::runtime_error("Unknown condition '" + condition + "'");
389-
}
390-
cmd("if", condition)(RawArg(project.conditions[condition]));
386+
cmd("if", condition)(RawArg(project.conditions.at(condition)));
391387
}
392388

393389
if (!itr.second.empty()) {
@@ -403,6 +399,27 @@ struct Generator {
403399
}
404400
};
405401

402+
struct ConditionScope {
403+
Generator &gen;
404+
bool endif = false;
405+
406+
ConditionScope(Generator &gen, const std::string &condition) : gen(gen) {
407+
if (!condition.empty()) {
408+
gen.cmd("if", condition)(RawArg(gen.project.conditions.at(condition)));
409+
endif = true;
410+
}
411+
}
412+
413+
ConditionScope(const ConditionScope &) = delete;
414+
ConditionScope(ConditionScope &&) = delete;
415+
416+
~ConditionScope() {
417+
if (endif) {
418+
gen.cmd("endif")();
419+
}
420+
}
421+
};
422+
406423
static bool vcpkg_valid_identifier(const std::string &name) {
407424
// prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default
408425
auto is_reserved = [](const std::string &s) {
@@ -679,7 +696,14 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
679696
if (!project.contents.empty()) {
680697
cmd("include")("FetchContent").endl();
681698
for (const auto &content : project.contents) {
682-
cmd("message")("STATUS", "Fetching " + content.name + "...");
699+
ConditionScope cs(gen, content.condition);
700+
std::string version_info = "";
701+
if (content.arguments.contains("GIT_TAG")) {
702+
version_info = " (" + content.arguments.at("GIT_TAG") + ")";
703+
} else if (content.arguments.contains("SVN_REVISION")) {
704+
version_info = " (" + content.arguments.at("SVN_REVISION") + ")";
705+
}
706+
cmd("message")("STATUS", "Fetching " + content.name + version_info + "...");
683707
ss << "FetchContent_Declare(\n\t" << content.name << "\n";
684708
for (const auto &arg : content.arguments) {
685709
ss << "\t" << arg.first << "\n\t\t" << arg.second << "\n";
@@ -698,6 +722,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
698722
auto required = dep.required ? "REQUIRED" : "";
699723
auto config = dep.config ? "CONFIG" : "";
700724
auto components = std::make_pair("COMPONENTS", dep.components);
725+
ConditionScope cs(gen, dep.condition);
701726
cmd("find_package")(dep.name, version, required, config, components).endl();
702727
}
703728
}
@@ -727,13 +752,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
727752
endl();
728753
}
729754
for (const auto &subdir : project.subdirs) {
730-
if (!subdir.condition.empty()) {
731-
const auto &condition = subdir.condition;
732-
if (project.conditions.count(condition) == 0) {
733-
throw std::runtime_error("Unknown condition '" + condition + "' for [subdir." + subdir.name + "]");
734-
}
735-
gen.cmd("if", condition)(RawArg(project.conditions[condition]));
736-
}
755+
ConditionScope cs(gen, subdir.condition);
737756

738757
gen.handle_condition(subdir.include_before,
739758
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
@@ -743,23 +762,18 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
743762

744763
gen.handle_condition(subdir.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
745764
gen.handle_condition(subdir.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
746-
747-
if (!subdir.condition.empty()) {
748-
cmd("endif")();
749-
}
750765
}
751766

752767
if (!project.targets.empty()) {
753-
for (const auto &target : project.targets) {
768+
for (size_t i = 0; i < project.targets.size(); i++) {
769+
if (i > 0) {
770+
endl();
771+
}
772+
773+
const auto &target = project.targets[i];
754774
comment("Target " + target.name);
755775

756-
if (!target.condition.empty()) {
757-
const auto &condition = target.condition;
758-
if (project.conditions.count(condition) == 0) {
759-
throw std::runtime_error("Unknown condition '" + condition + "' for [target." + target.name + "]");
760-
}
761-
gen.cmd("if", condition)(RawArg(project.conditions[condition]));
762-
}
776+
ConditionScope cs(gen, target.condition);
763777

764778
cmd("set")("CMKR_TARGET", target.name);
765779

@@ -901,13 +915,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
901915

902916
cmd("unset")("CMKR_TARGET");
903917
cmd("unset")("CMKR_SOURCES");
904-
905-
if (!target.condition.empty()) {
906-
cmd("endif")();
907-
}
908-
909-
endl();
910918
}
919+
920+
endl();
911921
}
912922

913923
if (!project.tests.empty()) {
@@ -922,6 +932,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
922932
auto working_directory = std::make_pair("WORKING_DIRECTORY", dir);
923933
auto command = std::make_pair("COMMAND", test.command);
924934
auto arguments = std::make_pair("", test.arguments);
935+
ConditionScope cs(gen, test.condition);
925936
cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
926937
}
927938
}
@@ -941,6 +952,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
941952
auto configs = std::make_pair("CONFIGURATIONS", inst.configs);
942953
auto destination = std::make_pair("DESTINATION", inst.destination);
943954
auto component = std::make_pair("COMPONENT", inst.targets.empty() ? "" : inst.targets.front());
955+
ConditionScope cs(gen, inst.condition);
944956
cmd("install")(targets, dirs, files, configs, destination, component);
945957
}
946958
}

src/project_parser.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ class TomlChecker {
145145
}
146146
}
147147
throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second));
148+
} else if (ky == "condition") {
149+
std::string condition = itr.second.as_string();
150+
if (!conditions.contains(condition)) {
151+
throw std::runtime_error(format_key_error("Unknown condition '" + condition + "'", condition, itr.second));
152+
}
148153
}
149154
}
150155
}
@@ -330,6 +335,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
330335
p.version = itr.second.as_string();
331336
} else {
332337
auto &pkg = checker.create(value);
338+
pkg.optional("condition", p.condition);
333339
pkg.optional("version", p.version);
334340
pkg.optional("required", p.required);
335341
pkg.optional("config", p.config);
@@ -347,6 +353,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
347353
content.name = itr.first;
348354
for (const auto &argItr : itr.second.as_table()) {
349355
auto key = argItr.first;
356+
if (key == "condition") {
357+
content.condition = argItr.second.as_string();
358+
continue;
359+
}
360+
350361
if (key == "git") {
351362
key = "GIT_REPOSITORY";
352363
} else if (key == "tag") {
@@ -469,6 +480,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
469480
auto &t = checker.create(value);
470481
Test test;
471482
t.required("name", test.name);
483+
t.optional("condition", test.condition);
472484
t.optional("configurations", test.configurations);
473485
t.optional("working-directory", test.working_directory);
474486
t.required("command", test.command);
@@ -482,6 +494,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
482494
for (const auto &value : is) {
483495
auto &i = checker.create(value);
484496
Install inst;
497+
i.optional("condition", inst.condition);
485498
i.optional("targets", inst.targets);
486499
i.optional("files", inst.files);
487500
i.optional("dirs", inst.dirs);

0 commit comments

Comments
 (0)