self-hosting
MoAlyousef 4 years ago
parent 4db372900d
commit e382482021

1
.gitignore vendored

@ -1,4 +1,5 @@
bin bin
bin2
compile_commands.json compile_commands.json
.clangd .clangd
temp.* temp.*

@ -1,24 +1,35 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(cmkr VERSION 0.1.0)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(cmkr VERSION 0.1.0)
set(CMKR_SOURCES set(CMKRLIB_SOURCES
"src/main.cpp"
"src/args.cpp" "src/args.cpp"
"src/gen.cpp" "src/gen.cpp"
"src/help.cpp"
) )
add_executable(cmkr ${CMKR_SOURCES}) add_library(cmkrlib STATIC ${CMKRLIB_SOURCES})
target_include_directories(cmkr PUBLIC target_include_directories(cmkrlib PUBLIC
"vendor" "vendor"
) )
target_compile_features(cmkr PUBLIC target_compile_features(cmkrlib PUBLIC
"cxx_std_17" "cxx_std_17"
) )
set(CMKR_SOURCES
"src/main.cpp"
)
add_executable(cmkr ${CMKR_SOURCES})
target_link_libraries(cmkr PUBLIC
"cmkrlib"
)

@ -18,40 +18,63 @@ cmkr parses cmake.toml files (using toml11) at the project directory. A basic he
[cmake] [cmake]
minimum_required = "3.0" minimum_required = "3.0"
[project]
name = "app"
version = "0.1.0"
[[bin]]
name = "app"
type = "exe"
sources = ["src/main.cpp"]
```
This project's cmake.toml:
```toml
[cmake]
minimum_required = "3.0"
[project] [project]
name = "cmkr" name = "cmkr"
version = "0.1.0" version = "0.1.0"
[[bin]]
name = "cmkrlib"
type = "static"
sources = ["src/args.cpp", "src/gen.cpp", "src/help.cpp"]
include_dirs = ["vendor"]
features = ["cxx_std_17"]
[[bin]] [[bin]]
name = "cmkr" name = "cmkr"
type = "exe" type = "exe"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
link_libs = ["cmkrlib"]
``` ```
Currently supported fields: Currently supported fields:
```toml ```toml
[cmake] # required [cmake] # required for top-level project
minimum_required = "3.0" # required minimum_required = "3.0" # required
# cpp_flags = [] # optional cpp_flags = [] # optional
# c_flags = [] # optional c_flags = [] # optional
# linker_flags = [] # optional link_flags = [] # optional
# subdirs = [] # optional subdirs = [] # optional
[project] # required [project] # required per project
name = "cmkr" # required name = "app" # required
version = "0.1.0" # required version = "0.1.0" # required
[dependencies] # optional, runs find_package, use "*" to ignore version [dependencies] # optional, runs find_package, use "*" to ignore version
boost = "1.74.0" # optional boost = "1.74.0" # optional
[[bin]] # required, can define several binaries [[bin]] # required, can define several binaries
name = "cmkr" # required name = "app" # required
type = "exe" # required (exe || shared || static) type = "exe" # required (exe || shared || static)
sources = ["src/main.cpp", "src/args.cpp", "src/gen.cpp"] # required sources = ["src/main.cpp"] # required
include_directories = ["vendor"] # optional include_dirs = ["vendor"] # optional
compile_features = ["cxx_std_17"] # optional features = ["cxx_std_17"] # optional
# definitions = [] # optional defines = [] # optional
# link_libraries = [] # optional link_libs = [] # optional
``` ```
The cmkr executable can be run from the command-line: The cmkr executable can be run from the command-line:
@ -60,7 +83,7 @@ Usage: cmkr [arguments]
arguments: arguments:
init [exe|shared|static] Starts a new project in the same directory. init [exe|shared|static] Starts a new project in the same directory.
gen Generates CMakeLists.txt file. gen Generates CMakeLists.txt file.
build [extra cmake args] Run cmake and build. build <extra cmake args> Run cmake and build.
help Show help. help Show help.
version Current cmkr version. version Current cmkr version.
``` ```

@ -1,23 +1,20 @@
[cmake] # required [cmake]
minimum_required = "3.0" # required minimum_required = "3.0"
# cpp_flags = [] # optional
# c_flags = [] # optional
# linker_flags = [] # optional
# subdirs = [] # optional
[project] # required [project]
name = "cmkr" # required name = "cmkr"
version = "0.1.0" # required version = "0.1.0"
# [dependencies] # optional, runs find_package, use "*" to ignore version [[bin]]
# boost = "1.74.0" name = "cmkrlib"
type = "static"
sources = ["src/args.cpp", "src/gen.cpp", "src/help.cpp"]
include_dirs = ["vendor"]
features = ["cxx_std_17"]
[[bin]] # required [[bin]]
name = "cmkr" # required name = "cmkr"
type = "exe" # required (exe || shared || static) type = "exe"
sources = ["src/main.cpp", "src/args.cpp", "src/gen.cpp"] # required sources = ["src/main.cpp"]
include_directories = ["vendor"] # optional link_libs = ["cmkrlib"]
compile_features = ["cxx_std_17"] # optional
# definitions = [] # optional
# link_libraries = [] # optional

@ -1,26 +1,34 @@
#include "args.hpp" #include "args.h"
#include "gen.hpp" #include "gen.h"
#include "help.hpp" #include "help.h"
#include <cstddef>
#include <cstdlib> #include <filesystem>
#include <stddef.h>
#include <stdexcept> #include <stdexcept>
#include <stdlib.h>
#include <string>
#include <vector>
namespace cmkr::args { namespace cmkr::args {
std::string handle_args(std::vector<std::string> &args) { const char *handle_args(int argc, char **argv) {
std::vector<std::string> args;
for (int i = 0; i < argc; ++i)
args.push_back(argv[i]);
if (args.size() < 2) if (args.size() < 2)
throw std::runtime_error("Please provide command line arguments!"); throw std::runtime_error("Please provide command line arguments!");
std::string main_arg = args[1]; std::string main_arg = args[1];
if (main_arg == "gen") { if (main_arg == "gen") {
cmkr::gen::generate_cmake(); cmkr::gen::generate_cmake(std::filesystem::current_path().string().c_str());
return "Generation successful!"; return "Generation successful!";
} else if (main_arg == "help") { } else if (main_arg == "help") {
return cmkr::help::help_msg; return cmkr::help::message();
} else if (main_arg == "version") { } else if (main_arg == "version") {
return cmkr::help::version; return cmkr::help::version();
} else if (main_arg == "init") { } else if (main_arg == "init") {
if (args.size() < 3) if (args.size() < 3)
throw std::runtime_error("Please provide a project type!"); throw std::runtime_error("Please provide a project type!");
cmkr::gen::generate_project(args[2]); cmkr::gen::generate_project(args[2].c_str());
return "Directory initialized!"; return "Directory initialized!";
} else if (main_arg == "build") { } else if (main_arg == "build") {
std::string command = "cmake -S. -Bbin "; std::string command = "cmake -S. -Bbin ";
@ -38,4 +46,8 @@ std::string handle_args(std::vector<std::string> &args) {
return "Unknown argument!"; return "Unknown argument!";
} }
} }
} // namespace cmkr::args } // namespace cmkr::args
const char *cmkr_args_handle_args(int argc, char **argv) {
return cmkr::args::handle_args(argc, argv);
}

@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
namespace cmkr::args {
const char *handle_args(int argc, char **argv);
}
extern "C" {
#endif
const char *cmkr_args_handle_args(int, char **);
#ifdef __cplusplus
}
#endif

@ -1,8 +0,0 @@
#pragma once
#include <string>
#include <vector>
namespace cmkr::args {
std::string handle_args(std::vector<std::string> &args);
}

@ -1,4 +1,5 @@
#include "gen.hpp" #include "gen.h"
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <map> #include <map>
@ -9,7 +10,9 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace cmkr::gen { namespace cmkr::gen {
namespace detail { namespace detail {
inline std::string to_upper(const std::string &str) { inline std::string to_upper(const std::string &str) {
std::string temp; std::string temp;
temp.reserve(str.size()); temp.reserve(str.size());
@ -18,14 +21,13 @@ inline std::string to_upper(const std::string &str) {
} }
return temp; return temp;
} }
void handle_bins(std::stringstream &s, const std::string &bin_type) {
}
} // namespace detail } // namespace detail
void generate_project(const std::string &str) { void generate_project(const char *str) {
fs::create_directory("src"); fs::create_directory("src");
auto dir_name = fs::current_path().stem(); auto dir_name = fs::current_path().stem();
if (str == "exe") { if (!strcmp(str, "exe")) {
std::ofstream ofs("src/main.cpp"); std::ofstream ofs("src/main.cpp");
if (ofs.is_open()) { if (ofs.is_open()) {
ofs << "#include <iostream>\n\nint main() {\n\tstd::cout << \"Hello world!\" << " ofs << "#include <iostream>\n\nint main() {\n\tstd::cout << \"Hello world!\" << "
@ -44,7 +46,7 @@ void generate_project(const std::string &str) {
} }
ofs2.flush(); ofs2.flush();
ofs2.close(); ofs2.close();
} else if (str == "static" || str == "shared") { } else if (!strcmp(str, "static") || !strcmp(str, "shared")) {
std::ofstream ofs("src/lib.cpp"); std::ofstream ofs("src/lib.cpp");
if (ofs.is_open()) { if (ofs.is_open()) {
ofs << "int dll_main() {\n\treturn 0;\n}"; ofs << "int dll_main() {\n\treturn 0;\n}";
@ -58,7 +60,8 @@ void generate_project(const std::string &str) {
<< dir_name.string() << dir_name.string()
<< "\"\nversion = " << "\"\nversion = "
"\"0.1.0\"\n\n[[lib]]\nname = \"" "\"0.1.0\"\n\n[[lib]]\nname = \""
<< dir_name.string() << "\"\nsources = [\"src/lib.cpp\"]\ntype = \"" << str << "\"\n"; << dir_name.string() << "\"\nsources = [\"src/lib.cpp\"]\ntype = \"" << str
<< "\"\n";
} }
ofs2.flush(); ofs2.flush();
ofs2.close(); ofs2.close();
@ -67,53 +70,60 @@ void generate_project(const std::string &str) {
} }
} }
void generate_cmake() { void generate_cmake(const char *path) {
std::stringstream ss; std::stringstream ss;
std::vector<std::string> subdirs;
const auto toml = toml::parse(fs::path(path) / "cmake.toml");
if (toml.contains("cmake")) {
const auto &cmake = toml::find(toml, "cmake");
const std::string cmake_min = toml::find(cmake, "minimum_required").as_string();
ss << "cmake_minimum_required(VERSION " << cmake_min << ")\n\n"
<< "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 << " " << flag;
}
ss << ")\n\n";
}
const auto toml = toml::parse("cmake.toml"); if (cmake.contains("c_flags")) {
const auto &cmake = toml::find(toml, "cmake"); ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS}";
const std::string cmake_min = toml::find(cmake, "minimum_required").as_string(); const auto flags = toml::find(cmake, "c_flags").as_array();
const auto &project = toml::find(toml, "project"); for (const auto &flag : flags) {
const std::string proj_name = toml::find(project, "name").as_string(); ss << " " << flag;
const std::string proj_version = toml::find(project, "version").as_string(); }
ss << ")\n\n";
ss << "cmake_minimum_required(VERSION " << cmake_min << ")\n\n"
<< "project(" << proj_name << " VERSION " << proj_version << ")\n\n"
<< "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 << " " << flag;
} }
ss << ")\n\n";
}
if (cmake.contains("c_flags")) { if (cmake.contains("link_flags")) {
ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS}"; ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}";
const auto flags = toml::find(cmake, "c_flags").as_array(); const auto flags = toml::find(cmake, "link_flags").as_array();
for (const auto &flag : flags) { for (const auto &flag : flags) {
ss << " " << flag; ss << " " << flag;
}
ss << ")\n\n";
} }
ss << ")\n\n";
}
if (cmake.contains("linker_flags")) { if (cmake.contains("subdirs")) {
ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}"; const auto dirs = toml::find(cmake, "subdirs").as_array();
const auto flags = toml::find(cmake, "linker_flags").as_array(); for (const auto &dir : dirs) {
for (const auto &flag : flags) { ss << "add_subdirectory(" << dir << ")\n";
ss << " " << flag; subdirs.push_back(dir.as_string());
}
ss << "\n\n";
} }
ss << ")\n\n";
} }
if (cmake.contains("subdirs")) { if (toml.contains("project")) {
const auto dirs = toml::find(cmake, "subdirs").as_array(); const auto &project = toml::find(toml, "project");
for (const auto &dir : dirs) { const std::string proj_name = toml::find(project, "name").as_string();
ss << "add_subdirectory(" << dir << ")\n"; const std::string proj_version = toml::find(project, "version").as_string();
}
ss << "\n\n"; ss << "project(" << proj_name << " VERSION " << proj_version << ")\n\n";
} }
if (toml.contains("dependencies")) { if (toml.contains("dependencies")) {
@ -159,8 +169,8 @@ void generate_cmake() {
<< 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_directories")) { if (bin.contains("include_dirs")) {
const auto includes = toml::find(bin, "include_directories").as_array(); 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 : includes) { for (const auto &inc : includes) {
ss << inc << "\n\t"; ss << inc << "\n\t";
@ -168,8 +178,8 @@ void generate_cmake() {
ss << ")\n\n"; ss << ")\n\n";
} }
if (bin.contains("link_libraries")) { if (bin.contains("link_libs")) {
const auto libraries = toml::find(bin, "link_libraries").as_array(); 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 : libraries) { for (const auto &l : libraries) {
ss << l << "\n\t"; ss << l << "\n\t";
@ -177,8 +187,8 @@ void generate_cmake() {
ss << ")\n\n"; ss << ")\n\n";
} }
if (bin.contains("compile_features")) { if (bin.contains("features")) {
const auto feats = toml::find(bin, "compile_features").as_array(); 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 : feats) { for (const auto &feat : feats) {
ss << feat << "\n\t"; ss << feat << "\n\t";
@ -186,8 +196,8 @@ void generate_cmake() {
ss << ")\n\n"; ss << ")\n\n";
} }
if (bin.contains("definitions")) { if (bin.contains("defines")) {
const auto defs = toml::find(bin, "definitions").as_array(); 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 : defs) { for (const auto &def : defs) {
ss << def << "\n\t"; ss << def << "\n\t";
@ -198,12 +208,23 @@ void generate_cmake() {
} }
ss << "\n\n"; ss << "\n\n";
// printf("%s\n", ss.str().c_str());
std::ofstream ofs("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) {
generate_cmake(sub.c_str());
}
}
} // namespace cmkr::gen
void cmkr_gen_generate_project(const char *typ) {
cmkr::gen::generate_project(typ);
}
void cmkr_gen_generate_cmake(const char *path) {
cmkr::gen::generate_cmake(path);
} }
} // namespace cmkr::gen

@ -0,0 +1,21 @@
#pragma once
#ifdef __cplusplus
namespace cmkr::gen {
void generate_project(const char *typ);
void generate_cmake(const char *path);
} // namespace cmkr::gen
extern "C" {
#endif
void cmkr_gen_generate_project(const char *typ);
void cmkr_gen_generate_cmake(const char *path);
#ifdef __cplusplus
}
#endif

@ -1,9 +0,0 @@
#pragma once
#include <string>
namespace cmkr::gen {
void generate_project(const std::string &str);
void generate_cmake();
} // namespace cmkr::gen

@ -0,0 +1,28 @@
#include "help.h"
namespace cmkr::help {
const char *version() {
return "cmkr version 0.1.0";
}
const char *message() {
return R"lit(
Usage: cmkr [arguments]
arguments:
init [exe|shared|static] Starts a new project in the same directory.
gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build.
help Show help.
version Current cmkr version.
)lit";
}
} // namespace cmkr::help
const char *cmkr_help_version(void) {
return cmkr::help::version();
}
const char *cmkr_help_message(void) {
return cmkr::help::message();
}

@ -0,0 +1,21 @@
#pragma once
#ifdef __cplusplus
namespace cmkr::help {
const char *version();
const char *message();
} // namespace cmkr::help
extern "C" {
#endif
const char *cmkr_help_version(void);
const char *cmkr_help_message(void);
#ifdef __cplusplus
}
#endif

@ -1,17 +0,0 @@
#pragma once
namespace cmkr::help {
const char *version = "cmkr version 0.1.0";
const char *help_msg = R"lit(
Usage: cmkr [arguments]
arguments:
init [exe|shared|static] Starts a new project in the same directory.
gen Generates CMakeLists.txt file.
build [cmake args...] Run cmake and build.
help Show help.
version Current cmkr version.
)lit";
} // namespace cmkr::help

@ -1,16 +1,12 @@
#include "args.hpp" #include "args.h"
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char **argv) try { #include <stdexcept>
std::vector<std::string> args; #include <stdio.h>
for (int i = 0; i < argc; ++i) #include <stdlib.h>
args.push_back(argv[i]);
auto output = cmkr::args::handle_args(args); int main(int argc, char **argv) try {
std::cout << output << std::endl; auto output = cmkr::args::handle_args(argc, argv);
return 0; return fprintf(stdout, "%s\n", output) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << e.what() << std::endl; fprintf(stderr, "%s\n", e.what());
} }
Loading…
Cancel
Save