add cmake class

self-hosting
MoAlyousef 4 years ago
parent c2885a09f2
commit b64d5a370d

@ -5,31 +5,32 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(cmkr VERSION 0.1.0)
set(CMKRLIB_SOURCES
"src/args.cpp"
"src/gen.cpp"
"src/help.cpp"
"src/build.cpp"
"src/error.cpp"
src/cmake.cpp
src/gen.cpp
src/help.cpp
src/build.cpp
src/error.cpp
)
add_library(cmkrlib STATIC ${CMKRLIB_SOURCES})
target_include_directories(cmkrlib PUBLIC
"vendor"
vendor
)
target_compile_features(cmkrlib PUBLIC
"cxx_std_17"
cxx_std_17
)
set(CMKR_SOURCES
"src/main.cpp"
src/main.cpp
src/args.cpp
)
add_executable(cmkr ${CMKR_SOURCES})
target_link_libraries(cmkr PUBLIC
"cmkrlib"
cmkrlib
)

@ -40,14 +40,14 @@ version = "0.1.0"
[[bin]]
name = "cmkrlib"
type = "static"
sources = ["src/args.cpp", "src/gen.cpp", "src/help.cpp", "src/build.cpp", "error.cpp"]
sources = ["src/cmake.cpp", "src/gen.cpp", "src/help.cpp", "src/build.cpp", "src/error.cpp"]
include-dirs = ["vendor"]
features = ["cxx_std_17"]
[[bin]]
name = "cmkr"
type = "exe"
sources = ["src/main.cpp"]
sources = ["src/main.cpp", "src/args.cpp"]
link-libs = ["cmkrlib"]
```

@ -8,13 +8,13 @@ version = "0.1.0"
[[bin]]
name = "cmkrlib"
type = "static"
sources = ["src/args.cpp", "src/gen.cpp", "src/help.cpp", "src/build.cpp", "src/error.cpp"]
sources = ["src/cmake.cpp", "src/gen.cpp", "src/help.cpp", "src/build.cpp", "src/error.cpp"]
include-dirs = ["vendor"]
features = ["cxx_std_17"]
[[bin]]
name = "cmkr"
type = "exe"
sources = ["src/main.cpp"]
sources = ["src/main.cpp", "src/args.cpp"]
link-libs = ["cmkrlib"]

@ -1,4 +1,5 @@
#include "build.h"
#include "cmake.hpp"
#include "error.h"
#include "gen.h"
@ -7,45 +8,40 @@
#include <stddef.h>
#include <stdexcept>
#include <stdlib.h>
#include <string>
#include <system_error>
#include <toml.hpp>
namespace cmkr::build {
int run(int argc, char **argv) {
cmake::CMake cmake(".", true);
if (argc > 2) {
for (size_t i = 2; i < argc; ++i) {
cmake.build_args.push_back(argv[i]);
}
}
std::stringstream ss;
std::string bin_dir = "bin";
if (!std::filesystem::exists("CMakeLists.txt"))
if (gen::generate_cmake("."))
throw std::runtime_error("CMake generation failure!");
const auto toml = toml::parse("cmake.toml");
if (toml.contains("cmake")) {
const auto &cmake = toml::find(toml, "cmake");
ss << "cmake -S. -B";
if (cmake.contains("bin-dir")) {
bin_dir = toml::find(cmake, "bin-dir").as_string();
}
ss << bin_dir << " ";
if (cmake.contains("generator")) {
const auto gen = toml::find(cmake, "generator").as_string();
ss << "-G " << gen << " ";
}
if (cmake.contains("arguments")) {
const auto args = toml::find(cmake, "arguments").as_array();
for (const auto &arg : args) {
ss << "-D" << arg << " ";
}
ss << "cmake -S. -B" << cmake.bin_dir << " ";
if (!cmake.generator.empty()) {
ss << "-G " << cmake.generator << " ";
}
if (!cmake.gen_args.empty()) {
for (const auto &arg : cmake.gen_args) {
ss << "-D" << arg << " ";
}
ss << "&& cmake --build " << bin_dir;
if (argc > 2) {
for (size_t i = 2; i < argc; ++i) {
ss << " " << argv[i];
}
}
ss << "&& cmake --build " << cmake.bin_dir;
if (argc > 2) {
for (const auto &arg : cmake.build_args) {
ss << " " << arg;
}
}
return ::system(ss.str().c_str());
}

@ -0,0 +1,112 @@
#include "cmake.hpp"
#include <filesystem>
#include <toml.hpp>
namespace fs = std::filesystem;
namespace cmkr::cmake {
namespace detail {
std::vector<std::string> to_string_vec(
const std::vector<toml::basic_value<toml::discard_comments, std::unordered_map, std::vector>>
vals) {
std::vector<std::string> temp;
for (const auto &val : vals)
temp.push_back(val.as_string());
return temp;
}
} // namespace detail
CMake::CMake(const std::string &path, bool build) {
if (fs::exists(fs::path(path) / "cmake.toml")) {
if (build) {
const auto toml = toml::parse("cmake.toml");
if (toml.contains("cmake")) {
const auto &cmake = toml::find(toml, "cmake");
if (cmake.contains("bin-dir")) {
bin_dir = toml::find(cmake, "bin-dir").as_string();
}
if (cmake.contains("generator")) {
generator = toml::find(cmake, "generator").as_string();
}
if (cmake.contains("arguments")) {
gen_args = detail::to_string_vec(toml::find(cmake, "arguments").as_array());
}
}
} else {
const auto toml = toml::parse((fs::path(path) / "cmake.toml").string());
if (toml.contains("cmake")) {
const auto &cmake = toml::find(toml, "cmake");
cmake_version = toml::find(cmake, "minimum").as_string();
if (cmake.contains("cpp-flags")) {
cppflags = detail::to_string_vec(toml::find(cmake, "cpp-flags").as_array());
}
if (cmake.contains("c-flags")) {
cflags = detail::to_string_vec(toml::find(cmake, "c-flags").as_array());
}
if (cmake.contains("link-flags")) {
linkflags = detail::to_string_vec(toml::find(cmake, "link-flags").as_array());
}
if (cmake.contains("subdirs")) {
subdirs = detail::to_string_vec(toml::find(cmake, "subdirs").as_array());
}
}
if (toml.contains("project")) {
const auto &project = toml::find(toml, "project");
proj_name = toml::find(project, "name").as_string();
proj_version = toml::find(project, "version").as_string();
}
if (toml.contains("find-package")) {
using pkg_map = std::map<std::string, std::string>;
packages = toml::find<pkg_map>(toml, "find-package");
}
if (toml.contains("fetch-content")) {
using content_map = std::map<std::string, std::map<std::string, std::string>>;
contents = toml::find<content_map>(toml, "fetch-content");
}
if (toml.contains("bin")) {
const auto &bins = toml::find(toml, "bin").as_array();
for (const auto &bin : bins) {
Bin b;
b.name = toml::find(bin, "name").as_string();
b.type = toml::find(bin, "type").as_string();
b.sources = detail::to_string_vec(toml::find(bin, "sources").as_array());
if (bin.contains("include-dirs")) {
b.include_dirs =
detail::to_string_vec(toml::find(bin, "include-dirs").as_array());
}
if (bin.contains("link-libs")) {
b.link_libs =
detail::to_string_vec(toml::find(bin, "link-libs").as_array());
}
if (bin.contains("features")) {
b.features = detail::to_string_vec(toml::find(bin, "features").as_array());
}
if (bin.contains("defines")) {
b.defines = detail::to_string_vec(toml::find(bin, "defines").as_array());
}
binaries.push_back(b);
}
}
}
}
}
} // namespace cmkr::cmake

@ -0,0 +1,37 @@
#pragma once
#include <map>
#include <string>
#include <vector>
namespace cmkr::cmake {
struct Bin {
std::string name;
std::string type;
std::vector<std::string> sources;
std::vector<std::string> include_dirs;
std::vector<std::string> features;
std::vector<std::string> defines;
std::vector<std::string> link_libs;
};
struct CMake {
std::string cmake_version = "3.14";
std::string bin_dir = "bin";
std::string generator;
std::vector<std::string> subdirs;
std::vector<std::string> cppflags;
std::vector<std::string> cflags;
std::vector<std::string> linkflags;
std::vector<std::string> gen_args;
std::vector<std::string> build_args;
std::string proj_name;
std::string proj_version;
std::map<std::string, std::string> packages;
std::map<std::string, std::map<std::string, std::string>> contents;
std::vector<Bin> binaries;
CMake(const std::string &path, bool build);
};
} // namespace cmkr::cmake

@ -24,7 +24,7 @@ struct Status {
extern "C" {
#endif
const char *cmkr_error_status(int);
const char *cmkr_error_status_string(int);
#ifdef __cplusplus
}

@ -1,18 +1,16 @@
#include "gen.h"
#include "cmake.hpp"
#include "error.h"
#include "literals.h"
#include <filesystem>
#include <fstream>
#include <map>
#include <new>
#include <sstream>
#include <stdexcept>
#include <stdio.h>
#include <string.h>
#include <string>
#include <string_view>
#include <toml.hpp>
namespace fs = std::filesystem;
@ -75,166 +73,145 @@ int generate_project(const char *str) {
}
int generate_cmake(const char *path) {
std::stringstream ss;
std::vector<std::string> subdirs;
const auto toml = toml::parse((fs::path(path) / "cmake.toml").string());
if (toml.contains("cmake")) {
const auto &cmake = toml::find(toml, "cmake");
const std::string cmake_min = toml::find(cmake, "minimum").as_string();
ss << "cmake_minimum_required(VERSION " << cmake_min << ")\n\n";
ss << "set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\n";
if (cmake.contains("cpp-flags")) {
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}\"";
const auto flags = toml::find(cmake, "cpp-flags").as_array();
for (const auto &flag : flags) {
ss << " " << std::string_view(flag.as_string());
if (fs::exists(fs::path(path) / "cmake.toml")) {
cmake::CMake cmake(path, false);
std::stringstream ss;
if (!cmake.cmake_version.empty()) {
ss << "cmake_minimum_required(VERSION " << cmake.cmake_version << ")\n\n";
ss << "set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\n";
}
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.contains("c-flags")) {
ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS}\"";
const auto flags = toml::find(cmake, "c-flags").as_array();
for (const auto &flag : flags) {
ss << " " << std::string_view(flag.as_string());
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.contains("link-flags")) {
ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}\"";
const auto flags = toml::find(cmake, "link-flags").as_array();
for (const auto &flag : flags) {
ss << " " << std::string_view(flag.as_string());
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.contains("subdirs")) {
const auto dirs = toml::find(cmake, "subdirs").as_array();
for (const auto &dir : dirs) {
if (!cmake.subdirs.empty()) {
for (const auto &dir : cmake.subdirs) {
ss << "add_subdirectory(" << dir << ")\n";
subdirs.push_back(dir.as_string());
}
ss << "\n\n";
}
}
if (toml.contains("project")) {
const auto &project = toml::find(toml, "project");
const std::string proj_name = toml::find(project, "name").as_string();
const std::string proj_version = toml::find(project, "version").as_string();
ss << "project(" << proj_name << " VERSION " << proj_version << ")\n\n";
}
if (!cmake.proj_name.empty() && !cmake.proj_version.empty()) {
ss << "project(" << cmake.proj_name << " VERSION " << cmake.proj_version << ")\n\n";
}
if (toml.contains("find-package")) {
using pkg_map = std::map<std::string, std::string>;
pkg_map deps =
toml::find<pkg_map>(toml, "find-package");
for (const auto &dep : deps) {
ss << "find_package(" << dep.first;
if (dep.second != "*") {
ss << " " << dep.second << " CONFIG REQUIRED)\n";
} else {
ss << " CONFIG REQUIRED)\n";
if (!cmake.packages.empty()) {
for (const auto &dep : cmake.packages) {
ss << "find_package(" << dep.first;
if (dep.second != "*") {
ss << " " << dep.second << " CONFIG REQUIRED)\n";
} else {
ss << " CONFIG REQUIRED)\n";
}
}
}
}
if (toml.contains("fetch-content")) {
using content_map = std::map<std::string, std::map<std::string, std::string>>;
content_map deps = toml::find<content_map>(toml, "fetch-content");
ss << "include(FetchContent)\n\n";
for (const auto &dep : deps) {
ss << "FetchContent_Declare(\n\t" << dep.first << "\n";
for (const auto &arg : dep.second) {
ss << "\t" << arg.first << " " << arg.second << "\n";
if (!cmake.contents.empty()) {
ss << "include(FetchContent)\n\n";
for (const auto &dep : cmake.contents) {
ss << "FetchContent_Declare(\n\t" << dep.first << "\n";
for (const auto &arg : dep.second) {
ss << "\t" << arg.first << " " << arg.second << "\n";
}
ss << "\t)\n\n"
<< "FetchContent_MakeAvailable(" << dep.first << ")\n\n";
}
ss << "\t)\n\n"
<< "FetchContent_MakeAvailable(" << dep.first << ")\n\n";
}
}
if (toml.contains("bin")) {
const auto &bins = toml::find(toml, "bin").as_array();
for (const auto &bin : bins) {
const std::string bin_name = toml::find(bin, "name").as_string();
const std::string type = toml::find(bin, "type").as_string();
std::string bin_type;
std::string add_command;
if (type == "exe") {
bin_type = "";
add_command = "add_executable";
} else if (type == "shared" || type == "static") {
bin_type = detail::to_upper(type);
add_command = "add_library";
} else {
throw std::runtime_error(
"Unknown binary type! Supported types are exe, shared and static");
}
if (!cmake.binaries.empty()) {
for (const auto &bin : cmake.binaries) {
std::string bin_type;
std::string add_command;
if (bin.type == "exe") {
bin_type = "";
add_command = "add_executable";
} else if (bin.type == "shared" || bin.type == "static") {
bin_type = detail::to_upper(bin.type);
add_command = "add_library";
} else {
throw std::runtime_error(
"Unknown binary type! Supported types are exe, shared and static");
}
const auto srcs = toml::find(bin, "sources").as_array();
ss << "set(" << detail::to_upper(bin_name) << "_SOURCES\n";
for (const auto &src : srcs) {
ss << "\t" << src << "\n";
}
ss << "\t)\n\n"
<< add_command << "(" << bin_name << " " << bin_type << " ${"
<< detail::to_upper(bin_name) << "_SOURCES})\n\n";
if (bin.contains("include-dirs")) {
const auto includes = toml::find(bin, "include-dirs").as_array();
ss << "target_include_directories(" << bin_name << " PUBLIC\n\t";
for (const auto &inc : includes) {
ss << inc << "\n\t";
ss << "set(" << detail::to_upper(bin.name) << "_SOURCES\n";
for (const auto &src : bin.sources) {
ss << "\t" << src << "\n";
}
ss << "\t)\n\n"
<< add_command << "(" << bin.name << " " << bin_type << " ${"
<< detail::to_upper(bin.name) << "_SOURCES})\n\n";
if (!bin.include_dirs.empty()) {
ss << "target_include_directories(" << bin.name << " PUBLIC\n\t";
for (const auto &inc : bin.include_dirs) {
ss << inc << "\n\t";
}
ss << ")\n\n";
}
ss << ")\n\n";
}
if (bin.contains("link-libs")) {
const auto libraries = toml::find(bin, "link-libs").as_array();
ss << "target_link_libraries(" << bin_name << " PUBLIC\n\t";
for (const auto &l : libraries) {
ss << l << "\n\t";
if (!bin.link_libs.empty()) {
ss << "target_link_libraries(" << bin.name << " PUBLIC\n\t";
for (const auto &l : bin.link_libs) {
ss << l << "\n\t";
}
ss << ")\n\n";
}
ss << ")\n\n";
}
if (bin.contains("features")) {
const auto feats = toml::find(bin, "features").as_array();
ss << "target_compile_features(" << bin_name << " PUBLIC\n\t";
for (const auto &feat : feats) {
ss << feat << "\n\t";
if (!bin.features.empty()) {
ss << "target_compile_features(" << bin.name << " PUBLIC\n\t";
for (const auto &feat : bin.features) {
ss << feat << "\n\t";
}
ss << ")\n\n";
}
ss << ")\n\n";
}
if (bin.contains("defines")) {
const auto defs = toml::find(bin, "defines").as_array();
ss << "target_add_definitions(" << bin_name << " PUBLIC\n\t";
for (const auto &def : defs) {
ss << def << "\n\t";
if (!bin.defines.empty()) {
ss << "target_add_definitions(" << bin.name << " PUBLIC\n\t";
for (const auto &def : bin.defines) {
ss << def << "\n\t";
}
ss << ")\n\n";
}
ss << ")\n\n";
}
}
}
ss << "\n\n";
// printf("%s\n", ss.str().c_str());
std::ofstream ofs(fs::path(path) / "CMakeLists.txt");
if (ofs.is_open()) {
ofs << ss.rdbuf();
}
ofs.flush();
ofs.close();
for (const auto &sub : subdirs) {
generate_cmake(sub.c_str());
ss << "\n\n";
// printf("%s\n", ss.str().c_str());
std::ofstream ofs(fs::path(path) / "CMakeLists.txt");
if (ofs.is_open()) {
ofs << ss.rdbuf();
}
ofs.flush();
ofs.close();
for (const auto &sub : cmake.subdirs) {
if (fs::exists(fs::path(sub) / "cmake.toml"))
generate_cmake(sub.c_str());
}
}
return 0;
}

Loading…
Cancel
Save