Skip to content

Commit eed1e38

Browse files
committed
Add support for target templates
1 parent 61851d2 commit eed1e38

File tree

4 files changed

+165
-66
lines changed

4 files changed

+165
-66
lines changed

include/project_parser.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ struct Project {
165165
std::vector<Install> installs;
166166
tsl::ordered_map<std::string, std::string> conditions;
167167
std::vector<Subdir> subdirs;
168+
std::vector<Target> templates;
168169

169170
Project(const Project *parent, const std::string &path, bool build);
170171
};

src/project_parser.cpp

Lines changed: 137 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,36 @@ class TomlChecker {
9696
visit(ky);
9797
}
9898

99+
template <typename T>
100+
void optional_append(const toml::key &ky, Condition<T> &destination) {
101+
// TODO: this algorithm in O(n) over the amount of keys, kinda bad
102+
const auto &table = m_v.as_table();
103+
for (const auto &itr : table) {
104+
const auto &key = itr.first;
105+
const auto &value = itr.second;
106+
T *dest = nullptr;
107+
if (value.is_table()) {
108+
if (value.contains(ky)) {
109+
dest = &destination[key];
110+
}
111+
} else if (key == ky) {
112+
dest = &destination[""];
113+
}
114+
if (dest != nullptr) {
115+
const auto &items = toml::find<T>(m_v, ky);
116+
dest->insert(dest->end(), items.begin(), items.end());
117+
}
118+
}
119+
120+
// Handle visiting logic
121+
for (const auto &itr : destination) {
122+
if (!itr.first.empty()) {
123+
m_conditionVisited.emplace(itr.first, true);
124+
}
125+
}
126+
visit(ky);
127+
}
128+
99129
template <typename T>
100130
void optional(const toml::key &ky, T &destination) {
101131
// TODO: this currently doesn't allow you to get an optional map<string, X>
@@ -105,6 +135,17 @@ class TomlChecker {
105135
visit(ky);
106136
}
107137

138+
template <typename T>
139+
void optional_append(const toml::key &ky, T &destination) {
140+
// TODO: this currently doesn't allow you to get an optional map<string, X>
141+
if (m_v.contains(ky)) {
142+
const auto &items = toml::find<T>(m_v, ky);
143+
destination.insert(destination.end(), items.begin(), items.end());
144+
}
145+
visit(ky);
146+
}
147+
148+
108149
template <typename T>
109150
void required(const toml::key &ky, T &destination) {
110151
destination = toml::find<T>(m_v, ky);
@@ -386,94 +427,124 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
386427
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
387428
}
388429

389-
if (toml.contains("target")) {
390-
const auto &ts = toml::find(toml, "target").as_table();
430+
if (toml.contains("target") || toml.contains("template")) {
431+
auto is_template = true;
391432

392-
for (const auto &itr : ts) {
393-
const auto &value = itr.second;
433+
for (const auto &ts : {&toml::find(toml, "template"), &toml::find(toml, "target")}) {
434+
for (const auto &itr : ts->as_table()) {
435+
const auto &value = itr.second;
436+
auto &t = checker.create(value);
437+
std::string template_name;
394438

395-
Target target;
396-
target.name = itr.first;
439+
if (!is_template) {
440+
t.optional("template", template_name);
441+
}
397442

398-
auto &t = checker.create(value);
399-
std::string type;
400-
t.required("type", type);
401-
target.type = to_enum<TargetType>(type, "target type");
443+
Target target;
444+
auto from_template = false;
402445

403-
t.optional("headers", target.headers);
404-
t.optional("sources", target.sources);
446+
if (!template_name.empty()) {
447+
for (const auto & template_ : templates) {
448+
if (template_name == template_.name) {
449+
from_template = true;
450+
target = template_;
451+
}
452+
}
405453

406-
t.optional("compile-definitions", target.compile_definitions);
407-
t.optional("private-compile-definitions", target.private_compile_definitions);
454+
if (!from_template) {
455+
throw std::runtime_error("Could not find template named " + template_name);
456+
}
457+
}
408458

409-
t.optional("compile-features", target.compile_features);
410-
t.optional("private-compile-features", target.private_compile_features);
459+
target.name = itr.first;
411460

412-
t.optional("compile-options", target.compile_options);
413-
t.optional("private-compile-options", target.private_compile_options);
461+
if (!from_template) {
462+
std::string type;
463+
t.required("type", type);
464+
target.type = to_enum<TargetType>(type, "target type");
465+
}
414466

415-
t.optional("include-directories", target.include_directories);
416-
t.optional("private-include-directories", target.private_include_directories);
467+
t.optional_append("headers", target.headers);
468+
t.optional_append("sources", target.sources);
417469

418-
t.optional("link-directories", target.link_directories);
419-
t.optional("private-link-directories", target.private_link_directories);
470+
t.optional_append("compile-definitions", target.compile_definitions);
471+
t.optional_append("private-compile-definitions", target.private_compile_definitions);
420472

421-
t.optional("link-libraries", target.link_libraries);
422-
t.optional("private-link-libraries", target.private_link_libraries);
473+
t.optional_append("compile-features", target.compile_features);
474+
t.optional_append("private-compile-features", target.private_compile_features);
423475

424-
t.optional("link-options", target.link_options);
425-
t.optional("private-link-options", target.private_link_options);
476+
t.optional_append("compile-options", target.compile_options);
477+
t.optional_append("private-compile-options", target.private_compile_options);
426478

427-
t.optional("precompile-headers", target.precompile_headers);
428-
t.optional("private-precompile-headers", target.private_precompile_headers);
479+
t.optional_append("include-directories", target.include_directories);
480+
t.optional_append("private-include-directories", target.private_include_directories);
429481

430-
if (!target.headers.empty()) {
431-
auto &sources = target.sources.nth(0).value();
432-
const auto &headers = target.headers.nth(0)->second;
433-
sources.insert(sources.end(), headers.begin(), headers.end());
434-
}
482+
t.optional_append("link-directories", target.link_directories);
483+
t.optional_append("private-link-directories", target.private_link_directories);
435484

436-
t.optional("condition", target.condition);
437-
t.optional("alias", target.alias);
485+
t.optional_append("link-libraries", target.link_libraries);
486+
t.optional_append("private-link-libraries", target.private_link_libraries);
438487

439-
if (t.contains("properties")) {
440-
auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) {
441-
if (v.is_array()) {
442-
std::string property_list;
443-
for (const auto &list_val : v.as_array()) {
444-
if (!property_list.empty()) {
445-
property_list += ';';
488+
t.optional_append("link-options", target.link_options);
489+
t.optional_append("private-link-options", target.private_link_options);
490+
491+
t.optional_append("precompile-headers", target.precompile_headers);
492+
t.optional_append("private-precompile-headers", target.private_precompile_headers);
493+
494+
if (!target.headers.empty()) {
495+
auto &sources = target.sources.nth(0).value();
496+
const auto &headers = target.headers.nth(0)->second;
497+
sources.insert(sources.end(), headers.begin(), headers.end());
498+
}
499+
500+
t.optional("condition", target.condition);
501+
t.optional("alias", target.alias);
502+
503+
if (t.contains("properties")) {
504+
auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) {
505+
if (v.is_array()) {
506+
std::string property_list;
507+
for (const auto &list_val : v.as_array()) {
508+
if (!property_list.empty()) {
509+
property_list += ';';
510+
}
511+
property_list += list_val.as_string();
446512
}
447-
property_list += list_val.as_string();
513+
target.properties[condition][k] = property_list;
514+
} else if (v.is_boolean()) {
515+
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
516+
} else {
517+
target.properties[condition][k] = v.as_string();
448518
}
449-
target.properties[condition][k] = property_list;
450-
} else if (v.is_boolean()) {
451-
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
452-
} else {
453-
target.properties[condition][k] = v.as_string();
454-
}
455-
};
456-
457-
const auto &props = t.find("properties").as_table();
458-
for (const auto &propKv : props) {
459-
const auto &k = propKv.first;
460-
const auto &v = propKv.second;
461-
if (v.is_table()) {
462-
for (const auto &condKv : v.as_table()) {
463-
store_property(condKv.first, condKv.second, k);
519+
};
520+
521+
const auto &props = t.find("properties").as_table();
522+
for (const auto &propKv : props) {
523+
const auto &k = propKv.first;
524+
const auto &v = propKv.second;
525+
if (v.is_table()) {
526+
for (const auto &condKv : v.as_table()) {
527+
store_property(condKv.first, condKv.second, k);
528+
}
529+
} else {
530+
store_property(k, v, "");
464531
}
465-
} else {
466-
store_property(k, v, "");
467532
}
468533
}
469-
}
470534

471-
t.optional("cmake-before", target.cmake_before);
472-
t.optional("cmake-after", target.cmake_after);
473-
t.optional("include-before", target.include_before);
474-
t.optional("include-after", target.include_after);
535+
t.optional("cmake-before", target.cmake_before);
536+
t.optional("cmake-after", target.cmake_after);
537+
t.optional_append("include-before", target.include_before);
538+
t.optional_append("include-after", target.include_after);
539+
540+
if (is_template) {
541+
templates.push_back(target);
542+
} else {
543+
targets.push_back(target);
544+
}
545+
}
475546

476-
targets.push_back(target);
547+
is_template = false;
477548
}
478549
}
479550

tests/templates/cmake.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# A project using templates.
2+
3+
[project]
4+
name = "templates"
5+
description = "Template example"
6+
7+
[template.app]
8+
type = "executable"
9+
sources = ["src/templates.cpp"]
10+
compile-definitions = ["IS_APP=true"]
11+
12+
[target.app-a]
13+
template = "app"
14+
compile-definitions = ["APP_A"]
15+
16+
[target.app-b]
17+
template = "app"
18+
compile-definitions = ["APP_B"]

tests/templates/src/templates.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <cstdio>
2+
3+
int main() {
4+
#if defined(APP_A)
5+
puts("Hello from app A!");
6+
#elif defined(APP_B)
7+
puts("Hello from app B!");
8+
#endif
9+
}

0 commit comments

Comments
 (0)