|
|
|
@ -140,6 +140,13 @@ struct CommandEndl {
|
|
|
|
|
void endl() { ss << '\n'; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct RawArg {
|
|
|
|
|
RawArg() = default;
|
|
|
|
|
RawArg(std::string arg) : arg(std::move(arg)) {}
|
|
|
|
|
|
|
|
|
|
std::string arg;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Credit: JustMagic
|
|
|
|
|
struct Command {
|
|
|
|
|
std::stringstream &ss;
|
|
|
|
@ -246,8 +253,26 @@ struct Command {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool print_arg(const RawArg &arg) {
|
|
|
|
|
if (arg.arg.empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (had_newline) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
ss << '\n' << indent(depth + 1);
|
|
|
|
|
} else if (first_arg) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
} else {
|
|
|
|
|
ss << ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << arg.arg;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
bool print_arg(const T &value, bool indentation = true) {
|
|
|
|
|
bool print_arg(const T &value) {
|
|
|
|
|
std::stringstream tmp;
|
|
|
|
|
tmp << value;
|
|
|
|
|
auto str = tmp.str();
|
|
|
|
@ -255,15 +280,13 @@ struct Command {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (indentation) {
|
|
|
|
|
if (had_newline) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
ss << '\n' << indent(depth + 1);
|
|
|
|
|
} else if (first_arg) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
} else {
|
|
|
|
|
ss << ' ';
|
|
|
|
|
}
|
|
|
|
|
if (had_newline) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
ss << '\n' << indent(depth + 1);
|
|
|
|
|
} else if (first_arg) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
} else {
|
|
|
|
|
ss << ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << quote(str);
|
|
|
|
@ -276,21 +299,31 @@ struct Command {
|
|
|
|
|
ss << indent(depth) << command << '(';
|
|
|
|
|
(void)std::initializer_list<bool>{print_arg(values)...};
|
|
|
|
|
if (had_newline)
|
|
|
|
|
ss << '\n';
|
|
|
|
|
ss << '\n' << indent(depth);
|
|
|
|
|
ss << ")\n";
|
|
|
|
|
return CommandEndl(ss);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int generate_cmake(const char *path, bool root) {
|
|
|
|
|
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
|
|
|
|
throw std::runtime_error("No cmake.toml found!");
|
|
|
|
|
static std::string tolf(const std::string &str) {
|
|
|
|
|
std::string result;
|
|
|
|
|
for (char ch : str) {
|
|
|
|
|
if (ch != '\r') {
|
|
|
|
|
result += ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Helper lambdas for more convenient CMake generation
|
|
|
|
|
struct Generator {
|
|
|
|
|
Generator(cmake::CMake &cmake) : cmake(cmake) {}
|
|
|
|
|
Generator(const Generator &) = delete;
|
|
|
|
|
|
|
|
|
|
cmake::CMake &cmake;
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
int indent = 0;
|
|
|
|
|
auto cmd = [&ss, &indent](const std::string &command) {
|
|
|
|
|
|
|
|
|
|
Command cmd(const std::string &command) {
|
|
|
|
|
if (command.empty())
|
|
|
|
|
throw std::invalid_argument("command cannot be empty");
|
|
|
|
|
if (command == "if") {
|
|
|
|
@ -302,22 +335,16 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
indent--;
|
|
|
|
|
}
|
|
|
|
|
return Command(ss, indent, command);
|
|
|
|
|
};
|
|
|
|
|
auto comment = [&ss, &indent](const std::string &comment) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CommandEndl comment(const std::string &comment) {
|
|
|
|
|
ss << Command::indent(indent) << "# " << comment << '\n';
|
|
|
|
|
return CommandEndl(ss);
|
|
|
|
|
};
|
|
|
|
|
auto endl = [&ss]() { ss << '\n'; };
|
|
|
|
|
auto tolf = [](const std::string &str) {
|
|
|
|
|
std::string result;
|
|
|
|
|
for (char ch : str) {
|
|
|
|
|
if (ch != '\r') {
|
|
|
|
|
result += ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
auto inject_includes = [&cmd, &endl](const std::vector<std::string> &includes) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void endl() { ss << '\n'; }
|
|
|
|
|
|
|
|
|
|
void inject_includes(const std::vector<std::string> &includes) {
|
|
|
|
|
if (!includes.empty()) {
|
|
|
|
|
for (const auto &file : includes) {
|
|
|
|
|
if (!fs::is_regular_file(file)) {
|
|
|
|
@ -325,22 +352,74 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
}
|
|
|
|
|
cmd("include")(file);
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
auto inject_cmake = [&ss, &tolf](const std::string &cmake) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void inject_cmake(const std::string &cmake) {
|
|
|
|
|
if (!cmake.empty()) {
|
|
|
|
|
if (cmake.back() == '\"') {
|
|
|
|
|
throw std::runtime_error("Detected additional \" at the end of cmake block");
|
|
|
|
|
}
|
|
|
|
|
ss << tolf(cmake) << "\n\n";
|
|
|
|
|
auto cmake_lf = tolf(cmake);
|
|
|
|
|
while (cmake_lf.back() == '\n')
|
|
|
|
|
cmake_lf.pop_back();
|
|
|
|
|
bool did_indent = false;
|
|
|
|
|
for (char ch : cmake_lf) {
|
|
|
|
|
if (!did_indent) {
|
|
|
|
|
ss << Command::indent(indent);
|
|
|
|
|
did_indent = true;
|
|
|
|
|
} else if (ch == '\n') {
|
|
|
|
|
did_indent = false;
|
|
|
|
|
}
|
|
|
|
|
ss << ch;
|
|
|
|
|
}
|
|
|
|
|
ss << '\n';
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: add link with proper documentation
|
|
|
|
|
comment("This file is automatically generated from cmake.toml - DO NOT EDIT").endl();
|
|
|
|
|
template <typename T, typename Lambda>
|
|
|
|
|
void handle_condition(const cmake::Condition<T> &value, const Lambda &fn) {
|
|
|
|
|
if (!value.empty()) {
|
|
|
|
|
for (const auto &itr : value) {
|
|
|
|
|
const auto &condition = itr.first;
|
|
|
|
|
if (!condition.empty()) {
|
|
|
|
|
if (cmake.conditions.count(condition) == 0) {
|
|
|
|
|
// TODO: somehow print line number information here?
|
|
|
|
|
throw std::runtime_error("Unknown condition '" + condition + "'");
|
|
|
|
|
}
|
|
|
|
|
cmd("if")(RawArg(cmake.conditions[condition]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn(condition, itr.second);
|
|
|
|
|
|
|
|
|
|
if (!condition.empty()) {
|
|
|
|
|
cmd("endif")();
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int generate_cmake(const char *path, bool root) {
|
|
|
|
|
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
|
|
|
|
throw std::runtime_error("No cmake.toml found!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmake::CMake cmake(path, false);
|
|
|
|
|
Generator gen(cmake);
|
|
|
|
|
|
|
|
|
|
// Helper lambdas for more convenient CMake generation
|
|
|
|
|
auto &ss = gen.ss;
|
|
|
|
|
auto cmd = [&gen](const std::string &comment) { return gen.cmd(comment); };
|
|
|
|
|
auto comment = [&gen](const std::string &comment) { return gen.comment(comment); };
|
|
|
|
|
auto endl = [&gen]() { gen.endl(); };
|
|
|
|
|
auto inject_includes = [&gen](const std::vector<std::string> &includes) { gen.inject_includes(includes); };
|
|
|
|
|
auto inject_cmake = [&gen](const std::string &cmake) { gen.inject_cmake(cmake); };
|
|
|
|
|
|
|
|
|
|
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
|
|
|
|
|
comment("See https://github.com/MoAlyousef/cmkr for more information");
|
|
|
|
|
endl();
|
|
|
|
|
|
|
|
|
|
if (root) {
|
|
|
|
|
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
|
|
|
|
@ -397,8 +476,8 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inject_includes(cmake.include_before);
|
|
|
|
|
inject_cmake(cmake.cmake_before);
|
|
|
|
|
gen.handle_condition(cmake.include_before, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
|
|
|
|
gen.handle_condition(cmake.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
|
|
|
|
|
|
|
|
|
if (!cmake.project_name.empty()) {
|
|
|
|
|
auto languages = std::make_pair("LANGUAGES", cmake.project_languages);
|
|
|
|
@ -407,8 +486,8 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
cmd("project")(cmake.project_name, languages, version, description).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inject_includes(cmake.include_after);
|
|
|
|
|
inject_cmake(cmake.cmake_after);
|
|
|
|
|
gen.handle_condition(cmake.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
|
|
|
|
gen.handle_condition(cmake.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
|
|
|
|
|
|
|
|
|
if (!cmake.contents.empty()) {
|
|
|
|
|
cmd("include")("FetchContent").endl();
|
|
|
|
@ -513,21 +592,29 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
if (!cmake.targets.empty()) {
|
|
|
|
|
for (const auto &target : cmake.targets) {
|
|
|
|
|
comment("Target " + target.name);
|
|
|
|
|
inject_includes(target.include_before);
|
|
|
|
|
inject_cmake(target.cmake_before);
|
|
|
|
|
gen.handle_condition(target.include_before,
|
|
|
|
|
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
|
|
|
|
gen.handle_condition(target.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
|
|
|
|
|
|
|
|
|
if (!target.sources.empty()) {
|
|
|
|
|
auto sources = expand_cmake_paths(target.sources, path);
|
|
|
|
|
auto sources_var = target.name + "_SOURCES";
|
|
|
|
|
|
|
|
|
|
bool added_toml = false;
|
|
|
|
|
cmd("unset")(sources_var).endl();
|
|
|
|
|
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
|
|
|
|
|
auto sources = expand_cmake_paths(condition_sources, path);
|
|
|
|
|
if (sources.empty()) {
|
|
|
|
|
throw std::runtime_error(target.name + " sources wildcard found 0 files");
|
|
|
|
|
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
|
|
|
|
|
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
|
|
|
|
|
}
|
|
|
|
|
if (target.type != cmake::target_interface) {
|
|
|
|
|
// Do not add cmake.toml twice
|
|
|
|
|
if (std::find(sources.begin(), sources.end(), "cmake.toml") == sources.end()) {
|
|
|
|
|
sources.push_back("cmake.toml");
|
|
|
|
|
}
|
|
|
|
|
// Do not add cmake.toml twice
|
|
|
|
|
if (!added_toml && std::find(sources.begin(), sources.end(), "cmake.toml") != sources.end()) {
|
|
|
|
|
added_toml = true;
|
|
|
|
|
}
|
|
|
|
|
cmd("set")(target.name + "_SOURCES", sources).endl();
|
|
|
|
|
cmd("list")("APPEND", sources_var, sources);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!added_toml && target.type != cmake::target_interface) {
|
|
|
|
|
cmd("list")("APPEND", sources_var, std::vector<std::string>{"cmake.toml"}).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string add_command;
|
|
|
|
@ -589,10 +676,9 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
cmd("add_library")(target.alias, "ALIAS", target.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto target_cmd = [&](const char *command, const std::vector<std::string> &args) {
|
|
|
|
|
if (!args.empty()) {
|
|
|
|
|
cmd(command)(target.name, target_scope, args).endl();
|
|
|
|
|
}
|
|
|
|
|
auto target_cmd = [&](const char *command, const cmake::ConditionVector &cargs) {
|
|
|
|
|
gen.handle_condition(
|
|
|
|
|
cargs, [&](const std::string &, const std::vector<std::string> &args) { cmd(command)(target.name, target_scope, args); });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
target_cmd("target_compile_definitions", target.compile_definitions);
|
|
|
|
@ -607,8 +693,9 @@ int generate_cmake(const char *path, bool root) {
|
|
|
|
|
cmd("set_target_properties")(target.name, "PROPERTIES", target.properties).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inject_includes(target.include_after);
|
|
|
|
|
inject_cmake(target.cmake_after);
|
|
|
|
|
gen.handle_condition(target.include_after,
|
|
|
|
|
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
|
|
|
|
gen.handle_condition(target.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|