|
|
|
@ -16,8 +16,6 @@
|
|
|
|
|
namespace cmkr {
|
|
|
|
|
namespace gen {
|
|
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
|
|
inline std::string to_upper(const std::string &str) {
|
|
|
|
|
std::string temp;
|
|
|
|
|
temp.reserve(str.size());
|
|
|
|
@ -65,7 +63,18 @@ static std::vector<std::string> expand_cmake_path(const fs::path &p) {
|
|
|
|
|
return temp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources) {
|
|
|
|
|
// TODO: add duplicate checking
|
|
|
|
|
std::vector<std::string> result;
|
|
|
|
|
for (const auto &src : sources) {
|
|
|
|
|
auto path = fs::path(src);
|
|
|
|
|
auto expanded = expand_cmake_path(path);
|
|
|
|
|
for (const auto &f : expanded) {
|
|
|
|
|
result.push_back(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int generate_project(const char *str) {
|
|
|
|
|
fs::create_directory("src");
|
|
|
|
@ -76,12 +85,12 @@ int generate_project(const char *str) {
|
|
|
|
|
std::string target;
|
|
|
|
|
std::string dest;
|
|
|
|
|
if (!strcmp(str, "executable")) {
|
|
|
|
|
mainbuf = detail::format(hello_world, "main");
|
|
|
|
|
mainbuf = format(hello_world, "main");
|
|
|
|
|
installed = "targets";
|
|
|
|
|
target = dir_name;
|
|
|
|
|
dest = "bin";
|
|
|
|
|
} else if (!strcmp(str, "static") || !strcmp(str, "shared") || !strcmp(str, "library")) {
|
|
|
|
|
mainbuf = detail::format(hello_world, "test");
|
|
|
|
|
mainbuf = format(hello_world, "test");
|
|
|
|
|
installed = "targets";
|
|
|
|
|
target = dir_name;
|
|
|
|
|
dest = "lib";
|
|
|
|
@ -94,7 +103,7 @@ int generate_project(const char *str) {
|
|
|
|
|
"! Supported types are: executable, library, shared, static, interface");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto tomlbuf = detail::format(cmake_toml, dir_name.c_str(), dir_name.c_str(), str, installed.c_str(), target.c_str(), dest.c_str());
|
|
|
|
|
const auto tomlbuf = format(cmake_toml, dir_name.c_str(), dir_name.c_str(), str, installed.c_str(), target.c_str(), dest.c_str());
|
|
|
|
|
|
|
|
|
|
if (strcmp(str, "interface")) {
|
|
|
|
|
std::ofstream ofs("src/main.cpp");
|
|
|
|
@ -121,15 +130,6 @@ struct CommandEndl {
|
|
|
|
|
void endl() { ss << '\n'; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct NamedArg {
|
|
|
|
|
std::string name;
|
|
|
|
|
T value;
|
|
|
|
|
|
|
|
|
|
NamedArg() = default;
|
|
|
|
|
NamedArg(std::string name, T value) : name(std::move(name)), value(std::move(value)) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Credit: JustMagic
|
|
|
|
|
struct Command {
|
|
|
|
|
std::stringstream &ss;
|
|
|
|
@ -182,11 +182,11 @@ struct Command {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
had_newline = true;
|
|
|
|
|
for (const auto &value : vec) {
|
|
|
|
|
ss << '\n' << indent(depth + 1) << quote(value);
|
|
|
|
|
print_arg(value);
|
|
|
|
|
}
|
|
|
|
|
had_newline = true;
|
|
|
|
|
first_arg = false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -197,25 +197,9 @@ struct Command {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto &itr : map) {
|
|
|
|
|
ss << '\n' << indent(depth + 1) << itr.first;
|
|
|
|
|
ss << '\n' << indent(depth + 2) << quote(itr.second);
|
|
|
|
|
print_arg(itr);
|
|
|
|
|
}
|
|
|
|
|
had_newline = true;
|
|
|
|
|
first_arg = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool print_arg(const std::string &value) {
|
|
|
|
|
if (value.empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (first_arg) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
} else {
|
|
|
|
|
ss << (had_newline ? '\n' : ' ');
|
|
|
|
|
}
|
|
|
|
|
ss << quote(value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -225,40 +209,57 @@ struct Command {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << '\n' << indent(depth + 1) << kv.first;
|
|
|
|
|
had_newline = true;
|
|
|
|
|
print_arg(kv.first);
|
|
|
|
|
depth++;
|
|
|
|
|
for (const auto &s : kv.second) {
|
|
|
|
|
ss << '\n' << indent(depth + 2) << quote(s);
|
|
|
|
|
print_arg(s);
|
|
|
|
|
}
|
|
|
|
|
had_newline = true;
|
|
|
|
|
first_arg = false;
|
|
|
|
|
depth--;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class K, class V>
|
|
|
|
|
bool print_arg(const std::pair<K, V> &kv) {
|
|
|
|
|
std::stringstream tmp;
|
|
|
|
|
tmp << kv.second;
|
|
|
|
|
auto str = tmp.str();
|
|
|
|
|
|
|
|
|
|
if (kv.second.empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << '\n' << indent(depth + 1) << kv.first;
|
|
|
|
|
ss << '\n' << indent(depth + 2) << quote(kv.second);
|
|
|
|
|
had_newline = true;
|
|
|
|
|
first_arg = false;
|
|
|
|
|
print_arg(kv.first);
|
|
|
|
|
depth++;
|
|
|
|
|
print_arg(str);
|
|
|
|
|
depth--;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
bool print_arg(const T &value) {
|
|
|
|
|
if (first_arg) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
} else {
|
|
|
|
|
ss << (had_newline ? '\n' : ' ');
|
|
|
|
|
}
|
|
|
|
|
bool print_arg(const T &value, bool indentation = true) {
|
|
|
|
|
std::stringstream tmp;
|
|
|
|
|
tmp << value;
|
|
|
|
|
ss << quote(tmp.str());
|
|
|
|
|
auto str = tmp.str();
|
|
|
|
|
if (str.empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (indentation) {
|
|
|
|
|
if (had_newline) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
ss << '\n' << indent(depth + 1);
|
|
|
|
|
} else if (first_arg) {
|
|
|
|
|
first_arg = false;
|
|
|
|
|
} else {
|
|
|
|
|
ss << ' ';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss << quote(str);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -275,416 +276,350 @@ struct Command {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int generate_cmake(const char *path, bool root) {
|
|
|
|
|
if (fs::exists(fs::path(path) / "cmake.toml")) {
|
|
|
|
|
cmake::CMake cmake(path, false);
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
|
|
|
|
|
int indent = 0;
|
|
|
|
|
auto cmd = [&ss, &indent](const std::string &command) {
|
|
|
|
|
if (command.empty())
|
|
|
|
|
throw std::invalid_argument("command cannot be empty");
|
|
|
|
|
if (command == "if") {
|
|
|
|
|
indent++;
|
|
|
|
|
return Command(ss, indent - 1, command);
|
|
|
|
|
} else if (command == "else" || command == "elseif") {
|
|
|
|
|
return Command(ss, indent - 1, command);
|
|
|
|
|
} else if (command == "endif") {
|
|
|
|
|
indent--;
|
|
|
|
|
}
|
|
|
|
|
return Command(ss, indent, command);
|
|
|
|
|
};
|
|
|
|
|
auto comment = [&ss](const std::string &comment) {
|
|
|
|
|
ss << "# " << 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;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
comment("This file was generated automatically by cmkr.").endl();
|
|
|
|
|
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
|
|
|
|
throw std::runtime_error("No cmake.toml found!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.cmake_before.empty()) {
|
|
|
|
|
ss << tolf(cmake.cmake_before) << "\n\n";
|
|
|
|
|
// Helper lambdas for more convenient CMake generation
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
int indent = 0;
|
|
|
|
|
auto cmd = [&ss, &indent](const std::string &command) {
|
|
|
|
|
if (command.empty())
|
|
|
|
|
throw std::invalid_argument("command cannot be empty");
|
|
|
|
|
if (command == "if") {
|
|
|
|
|
indent++;
|
|
|
|
|
return Command(ss, indent - 1, command);
|
|
|
|
|
} else if (command == "else" || command == "elseif") {
|
|
|
|
|
return Command(ss, indent - 1, command);
|
|
|
|
|
} else if (command == "endif") {
|
|
|
|
|
indent--;
|
|
|
|
|
}
|
|
|
|
|
return Command(ss, indent, command);
|
|
|
|
|
};
|
|
|
|
|
auto comment = [&ss](const std::string &comment) {
|
|
|
|
|
ss << "# " << 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.include_before.empty()) {
|
|
|
|
|
for (const auto &file : cmake.include_before) {
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
auto inject_includes = [&cmd, &endl](const std::vector<std::string> &includes) {
|
|
|
|
|
if (!includes.empty()) {
|
|
|
|
|
for (const auto &file : includes) {
|
|
|
|
|
// TODO: warn/error if file doesn't exist?
|
|
|
|
|
cmd("include")(file);
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
auto inject_cmake = [&ss, &tolf](const std::string &cmake) {
|
|
|
|
|
if (!cmake.empty()) {
|
|
|
|
|
ss << tolf(cmake) << "\n\n";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (root) {
|
|
|
|
|
comment("Regenerate CMakeLists.txt file when necessary");
|
|
|
|
|
cmd("include")(cmake.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT").endl();
|
|
|
|
|
comment("This file was generated automatically by cmkr.").endl();
|
|
|
|
|
// TODO: add link with proper documentation
|
|
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
|
cmd("if")("CMKR_INCLUDE_RESULT");
|
|
|
|
|
cmd("cmkr")();
|
|
|
|
|
cmd("endif")().endl();
|
|
|
|
|
// clang-format on
|
|
|
|
|
cmake::CMake cmake(path, false);
|
|
|
|
|
|
|
|
|
|
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
|
|
|
|
|
if (root) {
|
|
|
|
|
comment("Regenerate CMakeLists.txt file when necessary");
|
|
|
|
|
cmd("include")(cmake.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT").endl();
|
|
|
|
|
|
|
|
|
|
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON").endl();
|
|
|
|
|
}
|
|
|
|
|
// clang-format off
|
|
|
|
|
cmd("if")("CMKR_INCLUDE_RESULT");
|
|
|
|
|
cmd("cmkr")();
|
|
|
|
|
cmd("endif")().endl();
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
|
|
// TODO: remove support and replace with global compile-features
|
|
|
|
|
if (!cmake.cppflags.empty()) {
|
|
|
|
|
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
|
|
|
|
|
for (const auto &flag : cmake.cppflags) {
|
|
|
|
|
ss << flag << " ";
|
|
|
|
|
}
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
}
|
|
|
|
|
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
|
|
|
|
|
|
|
|
|
|
// TODO: remove support and replace with global compile-features
|
|
|
|
|
if (!cmake.cflags.empty()) {
|
|
|
|
|
ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} \"";
|
|
|
|
|
for (const auto &flag : cmake.cflags) {
|
|
|
|
|
ss << flag << " ";
|
|
|
|
|
}
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
}
|
|
|
|
|
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON").endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: remove support and replace with global linker-flags
|
|
|
|
|
if (!cmake.linkflags.empty()) {
|
|
|
|
|
ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} \"";
|
|
|
|
|
for (const auto &flag : cmake.linkflags) {
|
|
|
|
|
ss << flag << " ";
|
|
|
|
|
}
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
// TODO: remove support and replace with global compile-features
|
|
|
|
|
if (!cmake.cppflags.empty()) {
|
|
|
|
|
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
|
|
|
|
|
for (const auto &flag : cmake.cppflags) {
|
|
|
|
|
ss << flag << " ";
|
|
|
|
|
}
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.project_name.empty()) {
|
|
|
|
|
auto languages = std::make_pair("LANGUAGES", cmake.project_languages);
|
|
|
|
|
auto version = std::make_pair("VERSION", cmake.project_version);
|
|
|
|
|
auto description = std::make_pair("DESCRIPTION", cmake.project_description);
|
|
|
|
|
cmd("project")(cmake.project_name, languages, version, description).endl();
|
|
|
|
|
// TODO: remove support and replace with global compile-features
|
|
|
|
|
if (!cmake.cflags.empty()) {
|
|
|
|
|
ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} \"";
|
|
|
|
|
for (const auto &flag : cmake.cflags) {
|
|
|
|
|
ss << flag << " ";
|
|
|
|
|
}
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.cmake_after.empty()) {
|
|
|
|
|
ss << tolf(cmake.cmake_after) << "\n\n";
|
|
|
|
|
// TODO: remove support and replace with global linker-flags
|
|
|
|
|
if (!cmake.linkflags.empty()) {
|
|
|
|
|
ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} \"";
|
|
|
|
|
for (const auto &flag : cmake.linkflags) {
|
|
|
|
|
ss << flag << " ";
|
|
|
|
|
}
|
|
|
|
|
ss << "\")\n\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.include_after.empty()) {
|
|
|
|
|
for (const auto &file : cmake.include_after) {
|
|
|
|
|
// TODO: warn/error if file doesn't exist?
|
|
|
|
|
cmd("include")(file);
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
inject_includes(cmake.include_before);
|
|
|
|
|
inject_cmake(cmake.cmake_before);
|
|
|
|
|
|
|
|
|
|
if (!cmake.project_name.empty()) {
|
|
|
|
|
auto languages = std::make_pair("LANGUAGES", cmake.project_languages);
|
|
|
|
|
auto version = std::make_pair("VERSION", cmake.project_version);
|
|
|
|
|
auto description = std::make_pair("DESCRIPTION", cmake.project_description);
|
|
|
|
|
cmd("project")(cmake.project_name, languages, version, description).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inject_includes(cmake.include_after);
|
|
|
|
|
inject_cmake(cmake.cmake_after);
|
|
|
|
|
|
|
|
|
|
if (!cmake.packages.empty()) {
|
|
|
|
|
for (const auto &dep : cmake.packages) {
|
|
|
|
|
auto version = dep.version;
|
|
|
|
|
if (version == "*")
|
|
|
|
|
version.clear();
|
|
|
|
|
auto required = dep.required ? "REQUIRED" : "";
|
|
|
|
|
auto components = std::make_pair("COMPONENTS", dep.components);
|
|
|
|
|
cmd("find_package")(dep.name, version, required, components).endl();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.packages.empty()) {
|
|
|
|
|
for (const auto &dep : cmake.packages) {
|
|
|
|
|
ss << "find_package(" << dep.name << ' ';
|
|
|
|
|
if (dep.version != "*") {
|
|
|
|
|
ss << dep.version << " ";
|
|
|
|
|
}
|
|
|
|
|
if (dep.required) {
|
|
|
|
|
ss << "REQUIRED ";
|
|
|
|
|
}
|
|
|
|
|
if (!dep.components.empty()) {
|
|
|
|
|
ss << "COMPONENTS ";
|
|
|
|
|
for (const auto &comp : dep.components) {
|
|
|
|
|
ss << comp << " ";
|
|
|
|
|
}
|
|
|
|
|
if (!cmake.contents.empty()) {
|
|
|
|
|
cmd("include")("FetchContent").endl();
|
|
|
|
|
for (const auto &dep : cmake.contents) {
|
|
|
|
|
cmd("message")("STATUS", "Fetching " + dep.first + "...");
|
|
|
|
|
ss << "FetchContent_Declare(\n\t" << dep.first << "\n";
|
|
|
|
|
for (const auto &arg : dep.second) {
|
|
|
|
|
std::string first_arg = arg.first;
|
|
|
|
|
if (first_arg == "git") {
|
|
|
|
|
first_arg = "GIT_REPOSITORY";
|
|
|
|
|
} else if (first_arg == "tag") {
|
|
|
|
|
first_arg = "GIT_TAG";
|
|
|
|
|
} else if (first_arg == "svn") {
|
|
|
|
|
first_arg = "SVN_REPOSITORY";
|
|
|
|
|
} else if (first_arg == "rev") {
|
|
|
|
|
first_arg = "SVN_REVISION";
|
|
|
|
|
} else if (first_arg == "url") {
|
|
|
|
|
first_arg = "URL";
|
|
|
|
|
} else if (first_arg == "hash") {
|
|
|
|
|
first_arg = "URL_HASH";
|
|
|
|
|
} else {
|
|
|
|
|
// don't change arg
|
|
|
|
|
}
|
|
|
|
|
ss << ")\n\n";
|
|
|
|
|
ss << "\t" << first_arg << "\n\t\t" << arg.second << "\n";
|
|
|
|
|
}
|
|
|
|
|
ss << ")\n";
|
|
|
|
|
cmd("FetchContent_MakeAvailable")(dep.first).endl();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.contents.empty()) {
|
|
|
|
|
ss << "include(FetchContent)\n\n";
|
|
|
|
|
for (const auto &dep : cmake.contents) {
|
|
|
|
|
ss << "message(STATUS \"Fetching " << dep.first << "...\")\n";
|
|
|
|
|
ss << "FetchContent_Declare(\n\t" << dep.first << "\n";
|
|
|
|
|
for (const auto &arg : dep.second) {
|
|
|
|
|
std::string first_arg = arg.first;
|
|
|
|
|
if (first_arg == "git") {
|
|
|
|
|
first_arg = "GIT_REPOSITORY";
|
|
|
|
|
} else if (first_arg == "tag") {
|
|
|
|
|
first_arg = "GIT_TAG";
|
|
|
|
|
} else if (first_arg == "svn") {
|
|
|
|
|
first_arg = "SVN_REPOSITORY";
|
|
|
|
|
} else if (first_arg == "rev") {
|
|
|
|
|
first_arg = "SVN_REVISION";
|
|
|
|
|
} else if (first_arg == "url") {
|
|
|
|
|
first_arg = "URL";
|
|
|
|
|
} else if (first_arg == "hash") {
|
|
|
|
|
first_arg = "URL_HASH";
|
|
|
|
|
} else {
|
|
|
|
|
// don't change arg
|
|
|
|
|
}
|
|
|
|
|
ss << "\t" << first_arg << " " << arg.second << "\n";
|
|
|
|
|
}
|
|
|
|
|
ss << ")\n"
|
|
|
|
|
<< "FetchContent_MakeAvailable(" << dep.first << ")\n\n";
|
|
|
|
|
}
|
|
|
|
|
if (!cmake.options.empty()) {
|
|
|
|
|
for (const auto &opt : cmake.options) {
|
|
|
|
|
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.options.empty()) {
|
|
|
|
|
for (const auto &opt : cmake.options) {
|
|
|
|
|
ss << "option(" << opt.name << " \"" << opt.comment << "\" " << (opt.val ? "ON" : "OFF") << ")\n\n";
|
|
|
|
|
if (!cmake.settings.empty()) {
|
|
|
|
|
for (const auto &set : cmake.settings) {
|
|
|
|
|
std::string set_val;
|
|
|
|
|
if (set.val.index() == 1) {
|
|
|
|
|
set_val = mpark::get<1>(set.val);
|
|
|
|
|
} else {
|
|
|
|
|
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.settings.empty()) {
|
|
|
|
|
for (const auto &set : cmake.settings) {
|
|
|
|
|
std::string set_val;
|
|
|
|
|
if (set.val.index() == 1) {
|
|
|
|
|
set_val = mpark::get<1>(set.val);
|
|
|
|
|
} else {
|
|
|
|
|
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
|
|
|
|
|
}
|
|
|
|
|
ss << "set(" << set.name << " " << set_val;
|
|
|
|
|
;
|
|
|
|
|
if (set.cache) {
|
|
|
|
|
std::string typ;
|
|
|
|
|
if (set.val.index() == 1)
|
|
|
|
|
typ = "STRING";
|
|
|
|
|
else
|
|
|
|
|
typ = "BOOL";
|
|
|
|
|
ss << " CACHE " << typ << " \"" << set.comment << "\"";
|
|
|
|
|
if (set.force)
|
|
|
|
|
ss << " FORCE";
|
|
|
|
|
}
|
|
|
|
|
ss << ")\n\n";
|
|
|
|
|
if (set.cache) {
|
|
|
|
|
auto typ = set.val.index() == 1 ? "STRING" : "BOOL";
|
|
|
|
|
auto force = set.force ? "FORCE" : "";
|
|
|
|
|
cmd("set")(set.name, set_val, typ, set.comment, force);
|
|
|
|
|
} else {
|
|
|
|
|
cmd("set")(set.name, set_val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate_cmake is called on these recursively later
|
|
|
|
|
if (!cmake.subdirs.empty()) {
|
|
|
|
|
for (const auto &dir : cmake.subdirs) {
|
|
|
|
|
// clang-format off
|
|
|
|
|
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
|
|
|
|
|
cmd("if")("CMAKE_FOLDER");
|
|
|
|
|
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
|
|
|
|
|
cmd("else")();
|
|
|
|
|
cmd("set")("CMAKE_FOLDER", dir);
|
|
|
|
|
cmd("endif")();
|
|
|
|
|
// clang-format on
|
|
|
|
|
cmd("add_subdirectory")(dir);
|
|
|
|
|
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
// generate_cmake is called on the subdirectories recursively later
|
|
|
|
|
if (!cmake.subdirs.empty()) {
|
|
|
|
|
for (const auto &dir : cmake.subdirs) {
|
|
|
|
|
// clang-format off
|
|
|
|
|
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
|
|
|
|
|
cmd("if")("CMAKE_FOLDER");
|
|
|
|
|
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
|
|
|
|
|
cmd("else")();
|
|
|
|
|
cmd("set")("CMAKE_FOLDER", dir);
|
|
|
|
|
cmd("endif")();
|
|
|
|
|
// clang-format on
|
|
|
|
|
cmd("add_subdirectory")(dir);
|
|
|
|
|
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.targets.empty()) {
|
|
|
|
|
for (const auto &target : cmake.targets) {
|
|
|
|
|
comment("Target " + target.name);
|
|
|
|
|
if (!target.cmake_before.empty()) {
|
|
|
|
|
ss << tolf(target.cmake_before) << "\n\n";
|
|
|
|
|
}
|
|
|
|
|
if (!cmake.targets.empty()) {
|
|
|
|
|
for (const auto &target : cmake.targets) {
|
|
|
|
|
comment("Target " + target.name);
|
|
|
|
|
inject_includes(target.include_before);
|
|
|
|
|
inject_cmake(target.cmake_before);
|
|
|
|
|
|
|
|
|
|
if (!target.include_before.empty()) {
|
|
|
|
|
for (const auto &file : target.include_before) {
|
|
|
|
|
// TODO: warn/error if file doesn't exist?
|
|
|
|
|
cmd("include")(file);
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
if (!target.sources.empty()) {
|
|
|
|
|
auto sources = expand_cmake_paths(target.sources);
|
|
|
|
|
if (sources.empty()) {
|
|
|
|
|
throw std::runtime_error(target.name + " sources wildcard found 0 files");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!target.sources.empty()) {
|
|
|
|
|
// TODO: add duplicate checking
|
|
|
|
|
std::vector<std::string> sources;
|
|
|
|
|
for (const auto &src : target.sources) {
|
|
|
|
|
auto path = fs::path(src);
|
|
|
|
|
auto expanded = detail::expand_cmake_path(path);
|
|
|
|
|
for (const auto &f : expanded) {
|
|
|
|
|
sources.push_back(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (sources.empty()) {
|
|
|
|
|
throw std::runtime_error(target.name + " sources 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");
|
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cmd("set")(target.name + "_SOURCES", sources).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string add_command;
|
|
|
|
|
std::string target_type;
|
|
|
|
|
std::string target_scope;
|
|
|
|
|
switch (target.type) {
|
|
|
|
|
case cmake::target_executable:
|
|
|
|
|
add_command = "add_executable";
|
|
|
|
|
target_type = "";
|
|
|
|
|
target_scope = "PRIVATE";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_library:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_shared:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "SHARED";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_static:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "STATIC";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_interface:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "INTERFACE";
|
|
|
|
|
target_scope = "INTERFACE";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_custom:
|
|
|
|
|
// TODO: add proper support, this is hacky
|
|
|
|
|
add_command = "add_custom_target";
|
|
|
|
|
target_type = "SOURCES";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert("Unimplemented enum value" && false);
|
|
|
|
|
}
|
|
|
|
|
cmd("set")(target.name + "_SOURCES", sources).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd(add_command)(target.name, target_type, "${" + target.name + "_SOURCES}").endl();
|
|
|
|
|
std::string add_command;
|
|
|
|
|
std::string target_type;
|
|
|
|
|
std::string target_scope;
|
|
|
|
|
switch (target.type) {
|
|
|
|
|
case cmake::target_executable:
|
|
|
|
|
add_command = "add_executable";
|
|
|
|
|
target_type = "";
|
|
|
|
|
target_scope = "PRIVATE";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_library:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_shared:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "SHARED";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_static:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "STATIC";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_interface:
|
|
|
|
|
add_command = "add_library";
|
|
|
|
|
target_type = "INTERFACE";
|
|
|
|
|
target_scope = "INTERFACE";
|
|
|
|
|
break;
|
|
|
|
|
case cmake::target_custom:
|
|
|
|
|
// TODO: add proper support, this is hacky
|
|
|
|
|
add_command = "add_custom_target";
|
|
|
|
|
target_type = "SOURCES";
|
|
|
|
|
target_scope = "PUBLIC";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert("Unimplemented enum value" && false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!target.sources.empty()) {
|
|
|
|
|
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + target.name + "_SOURCES}").endl();
|
|
|
|
|
}
|
|
|
|
|
cmd(add_command)(target.name, target_type, "${" + target.name + "_SOURCES}").endl();
|
|
|
|
|
|
|
|
|
|
if (!target.alias.empty()) {
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
target_cmd("target_compile_definitions", target.compile_definitions);
|
|
|
|
|
target_cmd("target_compile_features", target.compile_features);
|
|
|
|
|
target_cmd("target_compile_options", target.compile_options);
|
|
|
|
|
target_cmd("target_include_directories", target.include_directories);
|
|
|
|
|
target_cmd("target_link_directories", target.link_directories);
|
|
|
|
|
target_cmd("target_link_libraries", target.link_libraries);
|
|
|
|
|
target_cmd("target_precompile_headers", target.precompile_headers);
|
|
|
|
|
|
|
|
|
|
if (!target.properties.empty()) {
|
|
|
|
|
cmd("set_target_properties")(target.name, "PROPERTIES", target.properties).endl();
|
|
|
|
|
}
|
|
|
|
|
if (!target.sources.empty()) {
|
|
|
|
|
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + target.name + "_SOURCES}").endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!target.cmake_after.empty()) {
|
|
|
|
|
ss << tolf(target.cmake_after) << "\n\n";
|
|
|
|
|
}
|
|
|
|
|
if (!target.alias.empty()) {
|
|
|
|
|
cmd("add_library")(target.alias, "ALIAS", target.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!target.include_after.empty()) {
|
|
|
|
|
for (const auto &file : target.include_after) {
|
|
|
|
|
// TODO: warn/error if file doesn't exist?
|
|
|
|
|
cmd("include")(file);
|
|
|
|
|
}
|
|
|
|
|
endl();
|
|
|
|
|
auto target_cmd = [&](const char *command, const std::vector<std::string> &args) {
|
|
|
|
|
if (!args.empty()) {
|
|
|
|
|
cmd(command)(target.name, target_scope, args).endl();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
target_cmd("target_compile_definitions", target.compile_definitions);
|
|
|
|
|
target_cmd("target_compile_features", target.compile_features);
|
|
|
|
|
target_cmd("target_compile_options", target.compile_options);
|
|
|
|
|
target_cmd("target_include_directories", target.include_directories);
|
|
|
|
|
target_cmd("target_link_directories", target.link_directories);
|
|
|
|
|
target_cmd("target_link_libraries", target.link_libraries);
|
|
|
|
|
target_cmd("target_precompile_headers", target.precompile_headers);
|
|
|
|
|
|
|
|
|
|
if (!target.properties.empty()) {
|
|
|
|
|
cmd("set_target_properties")(target.name, "PROPERTIES", target.properties).endl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inject_includes(target.include_after);
|
|
|
|
|
inject_cmake(target.cmake_after);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.tests.empty()) {
|
|
|
|
|
cmd("include")("CTest");
|
|
|
|
|
cmd("enable_testing")().endl();
|
|
|
|
|
for (const auto &test : cmake.tests) {
|
|
|
|
|
ss << "add_test(NAME " << test.name << " COMMAND " << test.cmd;
|
|
|
|
|
if (!test.args.empty()) {
|
|
|
|
|
for (const auto &arg : test.args) {
|
|
|
|
|
ss << " " << arg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ss << ")\n\n";
|
|
|
|
|
}
|
|
|
|
|
if (!cmake.tests.empty()) {
|
|
|
|
|
cmd("include")("CTest");
|
|
|
|
|
cmd("enable_testing")().endl();
|
|
|
|
|
for (const auto &test : cmake.tests) {
|
|
|
|
|
auto name = std::make_pair("NAME", test.name);
|
|
|
|
|
auto command = std::make_pair("COMMAND", test.cmd);
|
|
|
|
|
cmd("add_test")(name, command, test.args).endl();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cmake.installs.empty()) {
|
|
|
|
|
for (const auto &inst : cmake.installs) {
|
|
|
|
|
ss << "install(\n";
|
|
|
|
|
if (!inst.targets.empty()) {
|
|
|
|
|
ss << "\tTARGETS ";
|
|
|
|
|
for (const auto &target : inst.targets) {
|
|
|
|
|
ss << target << " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!inst.dirs.empty()) {
|
|
|
|
|
ss << "\tDIRS ";
|
|
|
|
|
for (const auto &dir : inst.dirs) {
|
|
|
|
|
ss << dir << " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!inst.files.empty()) {
|
|
|
|
|
ss << "\tFILES ";
|
|
|
|
|
int files_added = 0;
|
|
|
|
|
for (const auto &file : inst.files) {
|
|
|
|
|
auto path = detail::expand_cmake_path(fs::path(file));
|
|
|
|
|
for (const auto &f : path) {
|
|
|
|
|
ss << f << " ";
|
|
|
|
|
files_added++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (files_added == 0) {
|
|
|
|
|
throw std::runtime_error("[[install]] files wildcard did not resolve to any files");
|
|
|
|
|
}
|
|
|
|
|
if (!cmake.installs.empty()) {
|
|
|
|
|
for (const auto &inst : cmake.installs) {
|
|
|
|
|
auto targets = std::make_pair("TARGETS", inst.targets);
|
|
|
|
|
auto dirs = std::make_pair("DIRS", inst.dirs);
|
|
|
|
|
std::vector<std::string> files_data;
|
|
|
|
|
if (!inst.files.empty()) {
|
|
|
|
|
files_data = expand_cmake_paths(inst.files);
|
|
|
|
|
if (files_data.empty()) {
|
|
|
|
|
throw std::runtime_error("[[install]] files wildcard did not resolve to any files");
|
|
|
|
|
}
|
|
|
|
|
if (!inst.configs.empty()) {
|
|
|
|
|
ss << "\tCONFIGURATIONS";
|
|
|
|
|
for (const auto &conf : inst.configs) {
|
|
|
|
|
ss << conf << " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ss << "\n\tDESTINATION " << inst.destination << "\n\t";
|
|
|
|
|
if (!inst.targets.empty())
|
|
|
|
|
ss << "COMPONENT " << inst.targets[0] << "\n)\n\n";
|
|
|
|
|
else
|
|
|
|
|
ss << "\n)\n\n";
|
|
|
|
|
}
|
|
|
|
|
auto files = std::make_pair("FILES", inst.files);
|
|
|
|
|
auto configs = std::make_pair("CONFIGURATIONS", inst.configs);
|
|
|
|
|
auto destination = std::make_pair("DESTINATION", inst.destination);
|
|
|
|
|
auto component = std::make_pair("COMPONENT", inst.targets.empty() ? "" : inst.targets.front());
|
|
|
|
|
cmd("install")(targets, dirs, files, configs, destination, component);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto list_path = fs::path(path) / "CMakeLists.txt";
|
|
|
|
|
// Generate CMakeLists.txt
|
|
|
|
|
auto list_path = fs::path(path) / "CMakeLists.txt";
|
|
|
|
|
|
|
|
|
|
auto should_regenerate = [&list_path, &ss]() {
|
|
|
|
|
if (!fs::exists(list_path))
|
|
|
|
|
return true;
|
|
|
|
|
auto should_regenerate = [&list_path, &ss]() {
|
|
|
|
|
if (!fs::exists(list_path))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
std::ifstream ifs(list_path, std::ios_base::binary);
|
|
|
|
|
if (!ifs.is_open()) {
|
|
|
|
|
throw std::runtime_error("Failed to read " + list_path.string());
|
|
|
|
|
}
|
|
|
|
|
std::ifstream ifs(list_path, std::ios_base::binary);
|
|
|
|
|
if (!ifs.is_open()) {
|
|
|
|
|
throw std::runtime_error("Failed to read " + list_path.string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string data((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
|
|
|
|
return data != ss.str();
|
|
|
|
|
}();
|
|
|
|
|
std::string data((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
|
|
|
|
return data != ss.str();
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
if (should_regenerate) {
|
|
|
|
|
std::ofstream ofs(list_path, std::ios_base::binary);
|
|
|
|
|
if (ofs.is_open()) {
|
|
|
|
|
ofs << ss.str();
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Failed to write " + list_path.string());
|
|
|
|
|
}
|
|
|
|
|
if (should_regenerate) {
|
|
|
|
|
std::ofstream ofs(list_path, std::ios_base::binary);
|
|
|
|
|
if (ofs.is_open()) {
|
|
|
|
|
ofs << ss.str();
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Failed to write " + list_path.string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto &sub : cmake.subdirs) {
|
|
|
|
|
if (fs::exists(fs::path(sub) / "cmake.toml"))
|
|
|
|
|
generate_cmake(sub.c_str(), false);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("No cmake.toml found!");
|
|
|
|
|
for (const auto &sub : cmake.subdirs) {
|
|
|
|
|
if (fs::exists(fs::path(sub) / "cmake.toml"))
|
|
|
|
|
generate_cmake(sub.c_str(), false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} // namespace gen
|
|
|
|
|