Slightly improve cmkr init #24

main
Duncan Ogilvie 2 years ago
parent cc2b984aa5
commit 6a825e15a0

@ -5,13 +5,9 @@
namespace cmkr { namespace cmkr {
namespace gen { namespace gen {
int generate_project(const char *typ); void generate_project(const std::string &type);
int generate_cmake(const char *path, const parser::Project *parent_project = nullptr); void generate_cmake(const char *path, const parser::Project *parent_project = nullptr);
} // namespace gen } // namespace gen
} // namespace cmkr } // namespace cmkr
int cmkr_gen_generate_project(const char *typ);
int cmkr_gen_generate_cmake(const char *path);

@ -1,45 +1,100 @@
#pragma once #pragma once
static const char *hello_world = &R"lit( static const char *cpp_executable = &R"lit(
#include <iostream> #include <iostream>
#include <cstdlib>
int %s() { int main() {
std::cout << "Hello World!\n"; std::cout << "Hello from cmkr!\n";
return 0; return EXIT_SUCCESS;
} }
)lit"[1]; // skip initial newline )lit"[1]; // skip initial newline
static const char *cmake_toml = &R"lit( static const char *cpp_library = &R"lit(
[cmake] #include <@name/@name.hpp>
version = "3.15"
# subdirs = []
# build-dir = ""
# cpp-flags = []
# c-flags = []
# link-flags = []
# generator = ""
# arguments = []
#include <iostream>
namespace @name {
void hello() {
std::cout << "Hello from cmkr!\n";
}
} // namespace @name
)lit"[1]; // skip initial newline
static const char *hpp_library = &R"lit(
#pragma once
namespace @name {
void hello();
} // namespace @name
)lit"[1]; // skip initial newline
static const char *hpp_interface = &R"lit(
#pragma once
#include <iostream>
namespace @name {
inline void hello() {
std::cout << "Hello from cmkr!\n";
}
} // namespace @name
)lit"[1]; // skip initial newline
static const char *toml_executable = &R"lit(
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
[project] [project]
name = "%s" name = "@name"
version = "0.1.0"
[target.@name]
type = "executable"
sources = ["src/@name/main.cpp"]
compile-features = ["cxx_std_11"]
)lit"[1]; // skip initial newline
# [find-package] static const char *toml_library = &R"lit(
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
[project]
name = "@name"
# [fetch-content] [target.@name]
type = "@type"
sources = [
"src/@name/@name.cpp",
"include/@name/@name.hpp"
]
include-directories = ["include"]
compile-features = ["cxx_std_11"]
)lit"[1]; // skip initial newline
# [options] static const char *toml_interface = &R"lit(
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
[project]
name = "@name"
[target.%s] [target.@name]
type = "%s" type = "interface"
sources = ["src/*.cpp"]
include-directories = ["include"] include-directories = ["include"]
# alias = "" compile-features = ["cxx_std_11"]
# compile-features = [] )lit"[1]; // skip initial newline
# compile-definitions = []
# link-libraries = [] static const char *toml_migration = &R"lit(
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
[[install]] [project]
%s = ["%s"] name = "@name"
destination = "${CMAKE_INSTALL_PREFIX}/%s"
# TODO: define a target for each of your executables/libraries like this:
#[target.@name]
#type = "executable"
#sources = ["src/@name/*.cpp", "include/@name/*.hpp"]
#include-directories = ["include"]
#compile-features = ["cxx_std_11"]
#link-libraries = ["other-targets"]
)lit"[1]; // skip initial newline )lit"[1]; // skip initial newline

@ -21,9 +21,7 @@ const char *handle_args(int argc, char **argv) {
throw std::runtime_error(cmkr::help::message()); throw std::runtime_error(cmkr::help::message());
std::string main_arg = args[1]; std::string main_arg = args[1];
if (main_arg == "gen") { if (main_arg == "gen") {
auto ret = cmkr::gen::generate_cmake(fs::current_path().string().c_str()); cmkr::gen::generate_cmake(fs::current_path().string().c_str());
if (ret)
return "CMake generation error!";
return "CMake generation successful!"; return "CMake generation successful!";
} else if (main_arg == "help") { } else if (main_arg == "help") {
return cmkr::help::message(); return cmkr::help::message();
@ -33,18 +31,14 @@ const char *handle_args(int argc, char **argv) {
std::string type = "executable"; std::string type = "executable";
if (args.size() > 2) if (args.size() > 2)
type = args[2]; type = args[2];
auto ret = cmkr::gen::generate_project(type.c_str()); cmkr::gen::generate_project(type.c_str());
if (ret) cmkr::gen::generate_cmake(fs::current_path().string().c_str());
return "Initialization failure!";
ret = cmkr::gen::generate_cmake(fs::current_path().string().c_str());
if (ret)
return "CMake generation error!";
return "Directory initialized!"; return "Directory initialized!";
} else if (main_arg == "build") { } else if (main_arg == "build") {
auto ret = build::run(argc, argv); auto ret = build::run(argc, argv);
if (ret) if (ret)
return "CMake build error!"; return "CMake build error!";
return "CMake run completed!"; return "CMake build completed!";
} else if (main_arg == "install") { } else if (main_arg == "install") {
auto ret = build::install(); auto ret = build::install();
if (ret) if (ret)

@ -22,10 +22,9 @@ int run(int argc, char **argv) {
} }
std::stringstream ss; std::stringstream ss;
if (gen::generate_cmake(fs::current_path().string().c_str())) gen::generate_cmake(fs::current_path().string().c_str());
throw std::runtime_error("CMake generation failure!");
ss << "cmake -S. -DCMKR_BUILD_SKIP_GENERATION=ON -B" << project.build_dir << " "; ss << "cmake -DCMKR_BUILD_SKIP_GENERATION=ON -B" << project.build_dir << " ";
if (!project.generator.empty()) { if (!project.generator.empty()) {
ss << "-G \"" << project.generator << "\" "; ss << "-G \"" << project.generator << "\" ";

@ -19,7 +19,7 @@
namespace cmkr { namespace cmkr {
namespace gen { namespace gen {
inline std::string to_upper(const std::string &str) { static std::string to_upper(const std::string &str) {
std::string temp; std::string temp;
temp.reserve(str.size()); temp.reserve(str.size());
for (auto c : str) { for (auto c : str) {
@ -28,16 +28,16 @@ inline std::string to_upper(const std::string &str) {
return temp; return temp;
} }
template <typename... Args> static std::string format(const char *format, tsl::ordered_map<std::string, std::string> variables) {
std::string format(const char *fmt, Args... args) { std::string s = format;
auto sz = snprintf(nullptr, 0, fmt, args...) + 1; for (const auto &itr : variables) {
char *buf = new char[sz]; size_t start_pos = 0;
int ret = snprintf(buf, sz, fmt, args...); while ((start_pos = s.find(itr.first, start_pos)) != std::string::npos) {
if (ret != sz - 1) s.replace(start_pos, itr.first.length(), itr.second);
throw std::runtime_error("Error formatting string!"); start_pos += itr.second.length();
std::string temp(buf, buf + sz - 1); }
delete[] buf; }
return temp; return s;
} }
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir) { static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir) {
@ -89,52 +89,47 @@ static std::vector<std::string> expand_cmake_paths(const std::vector<std::string
return result; return result;
} }
int generate_project(const char *str) { static void create_file(const fs::path &path, const std::string &contents) {
fs::create_directory("src"); if (!path.parent_path().empty()) {
fs::create_directory("include"); fs::create_directories(path.parent_path());
const auto dir_name = fs::current_path().stem().string();
std::string mainbuf;
std::string installed;
std::string target;
std::string dest;
if (!strcmp(str, "executable")) {
mainbuf = format(hello_world, "main");
installed = "targets";
target = dir_name;
dest = "bin";
} else if (!strcmp(str, "static") || !strcmp(str, "shared") || !strcmp(str, "library")) {
mainbuf = format(hello_world, "test");
installed = "targets";
target = dir_name;
dest = "lib";
} else if (!strcmp(str, "interface")) {
installed = "files";
target = "include/*.h";
dest = "include/" + dir_name;
} else {
throw std::runtime_error("Unknown project type " + std::string(str) +
"! Supported types are: executable, library, shared, static, interface");
} }
std::ofstream ofs(path, std::ios::binary);
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 (!ofs) {
throw std::runtime_error("Failed to create " + path.string());
if (strcmp(str, "interface")) {
std::ofstream ofs("src/main.cpp", std::ios::binary);
if (ofs.is_open()) {
ofs << mainbuf;
}
ofs.flush();
ofs.close();
} }
ofs << contents;
}
std::ofstream ofs2("cmake.toml", std::ios::binary); void generate_project(const std::string &type) {
if (ofs2.is_open()) { const auto name = fs::current_path().stem().string();
ofs2 << tomlbuf; if (fs::exists(fs::current_path() / "cmake.toml")) {
throw std::runtime_error("Cannot initialize a project when cmake.toml already exists!");
} }
ofs2.flush();
ofs2.close();
return 0; tsl::ordered_map<std::string, std::string> variables = {
{"@name", name},
{"@type", type},
};
if (!fs::is_empty(fs::current_path())) {
create_file("cmake.toml", format(toml_migration, variables));
puts("Generated migration cmake.toml in existing project directory...");
return;
}
if (type == "executable") {
create_file("cmake.toml", format(toml_executable, variables));
create_file("src/" + name + "/main.cpp", format(cpp_executable, variables));
} else if (type == "static" || type == "shared" || type == "library") {
create_file("cmake.toml", format(toml_library, variables));
create_file("src/" + name + "/" + name + ".cpp", format(cpp_library, variables));
create_file("include/" + name + "/" + name + ".hpp", format(hpp_library, variables));
} else if (type == "interface") {
create_file("cmake.toml", format(toml_interface, variables));
create_file("include/" + name + "/" + name + ".hpp", format(hpp_interface, variables));
} else {
throw std::runtime_error("Unknown project type " + type + "! Supported types are: executable, library, shared, static, interface");
}
} }
struct CommandEndl { struct CommandEndl {
@ -440,7 +435,7 @@ static std::string vcpkg_escape_identifier(const std::string &name) {
return escaped; return escaped;
} }
int generate_cmake(const char *path, const parser::Project *parent_project) { void generate_cmake(const char *path, const parser::Project *parent_project) {
if (!fs::exists(fs::path(path) / "cmake.toml")) { if (!fs::exists(fs::path(path) / "cmake.toml")) {
throw std::runtime_error("No cmake.toml found!"); throw std::runtime_error("No cmake.toml found!");
} }
@ -494,14 +489,7 @@ int generate_cmake(const char *path, const parser::Project *parent_project) {
fs::path cmkr_include(project.cmkr_include); fs::path cmkr_include(project.cmkr_include);
if (!project.cmkr_include.empty() && !fs::exists(cmkr_include) && cmkr_include.is_relative()) { if (!project.cmkr_include.empty() && !fs::exists(cmkr_include) && cmkr_include.is_relative()) {
if (!cmkr_include.parent_path().empty()) { create_file(cmkr_include, resources::cmkr);
fs::create_directories(cmkr_include.parent_path());
}
std::ofstream ofs(cmkr_include.string(), std::ios::binary);
if (!ofs) {
throw std::runtime_error("Failed to create " + project.cmkr_include);
}
ofs.write(resources::cmkr, strlen(resources::cmkr));
} }
} }
@ -910,12 +898,7 @@ int generate_cmake(const char *path, const parser::Project *parent_project) {
}(); }();
if (should_regenerate) { if (should_regenerate) {
std::ofstream ofs(list_path, std::ios::binary); create_file(list_path, ss.str());
if (ofs.is_open()) {
ofs << ss.str();
} else {
throw std::runtime_error("Failed to write " + list_path.string());
}
} }
auto generate_subdir = [path, &project](const fs::path &sub) { auto generate_subdir = [path, &project](const fs::path &sub) {
@ -941,28 +924,6 @@ int generate_cmake(const char *path, const parser::Project *parent_project) {
for (const auto &subdir : project.subdirs) { for (const auto &subdir : project.subdirs) {
generate_subdir(subdir.name); generate_subdir(subdir.name);
} }
return 0;
} }
} // namespace gen } // namespace gen
} // namespace cmkr } // namespace cmkr
int cmkr_gen_generate_project(const char *typ) {
try {
return cmkr::gen::generate_project(typ);
} catch (const std::system_error &e) {
return e.code().value();
} catch (...) {
return cmkr::error::Status(cmkr::error::Status::Code::InitError);
}
}
int cmkr_gen_generate_cmake(const char *path) {
try {
return cmkr::gen::generate_cmake(path);
} catch (const std::system_error &e) {
return e.code().value();
} catch (...) {
return cmkr::error::Status(cmkr::error::Status::Code::GenerationError);
}
}

Loading…
Cancel
Save