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) project(cmkr VERSION 0.1.0)
set(CMKRLIB_SOURCES set(CMKRLIB_SOURCES
"src/args.cpp" src/cmake.cpp
"src/gen.cpp" src/gen.cpp
"src/help.cpp" src/help.cpp
"src/build.cpp" src/build.cpp
"src/error.cpp" src/error.cpp
) )
add_library(cmkrlib STATIC ${CMKRLIB_SOURCES}) add_library(cmkrlib STATIC ${CMKRLIB_SOURCES})
target_include_directories(cmkrlib PUBLIC target_include_directories(cmkrlib PUBLIC
"vendor" vendor
) )
target_compile_features(cmkrlib PUBLIC target_compile_features(cmkrlib PUBLIC
"cxx_std_17" cxx_std_17
) )
set(CMKR_SOURCES set(CMKR_SOURCES
"src/main.cpp" src/main.cpp
src/args.cpp
) )
add_executable(cmkr ${CMKR_SOURCES}) add_executable(cmkr ${CMKR_SOURCES})
target_link_libraries(cmkr PUBLIC target_link_libraries(cmkr PUBLIC
"cmkrlib" cmkrlib
) )

@ -40,14 +40,14 @@ version = "0.1.0"
[[bin]] [[bin]]
name = "cmkrlib" name = "cmkrlib"
type = "static" 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"] include-dirs = ["vendor"]
features = ["cxx_std_17"] features = ["cxx_std_17"]
[[bin]] [[bin]]
name = "cmkr" name = "cmkr"
type = "exe" type = "exe"
sources = ["src/main.cpp"] sources = ["src/main.cpp", "src/args.cpp"]
link-libs = ["cmkrlib"] link-libs = ["cmkrlib"]
``` ```

@ -8,13 +8,13 @@ version = "0.1.0"
[[bin]] [[bin]]
name = "cmkrlib" name = "cmkrlib"
type = "static" 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"] include-dirs = ["vendor"]
features = ["cxx_std_17"] features = ["cxx_std_17"]
[[bin]] [[bin]]
name = "cmkr" name = "cmkr"
type = "exe" type = "exe"
sources = ["src/main.cpp"] sources = ["src/main.cpp", "src/args.cpp"]
link-libs = ["cmkrlib"] link-libs = ["cmkrlib"]

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

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

Loading…
Cancel
Save