mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b7b260379 | |||
| 747bc8be00 | |||
| 00121c261f | |||
| 326647011b | |||
| 598c9b9ba8 | |||
| d62dff29b1 | |||
| 783f343e40 | |||
| 5312b44860 | |||
| 6162e86329 |
@@ -2,18 +2,23 @@ name: CMake
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, macos-10.15, ubuntu-18.04]
|
||||
|
||||
os: [windows-2016, windows-2019, macos-10.15, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: cmake -S. -Bbin -DCMAKE_BUILD_TYPE=$BUILD_TYPE && cmake --build bin --parallel --config $BUILD_TYPE
|
||||
run: |
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
|
||||
cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
|
||||
- name: Test
|
||||
run: |
|
||||
cd build/tests
|
||||
ctest -C ${{ env.BUILD_TYPE }}
|
||||
|
||||
+4
-3
@@ -5,6 +5,7 @@ compile_commands.json
|
||||
temp.*
|
||||
.vs
|
||||
.cache
|
||||
build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
CMakeLists.txt.user
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.1.4 - 2021-04-11
|
||||
- Use `GIT_SHALLOW ON` by default when getting git dependencies.
|
||||
- Add cmake.description field.
|
||||
- Change bin.defines to bin.definitions.
|
||||
|
||||
## 0.1.3 - 2020-11-27
|
||||
- Support building with C++11.
|
||||
- @mrexodia implemented CMake integration and bootstrapping.
|
||||
|
||||
@@ -46,6 +46,16 @@ endif()
|
||||
add_subdirectory(third_party)
|
||||
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||
|
||||
# tests
|
||||
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||
if(CMAKE_FOLDER)
|
||||
set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests")
|
||||
else()
|
||||
set(CMAKE_FOLDER tests)
|
||||
endif()
|
||||
add_subdirectory(tests)
|
||||
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||
|
||||
|
||||
# Target cmkrlib
|
||||
set(cmkrlib_SOURCES
|
||||
|
||||
@@ -35,7 +35,8 @@ minimum = "3.15"
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
description = "CMakeLists generator from TOML"
|
||||
|
||||
[fetch-content]
|
||||
toml11 = { git = "https://github.com/ToruNiina/toml11" }
|
||||
@@ -65,6 +66,7 @@ Currently supported fields:
|
||||
```toml
|
||||
[cmake] # required for top-level project
|
||||
minimum = "3.15" # required
|
||||
description = "" # optional
|
||||
subdirs = [] # optional
|
||||
build-dir = "build" # optional
|
||||
cpp-flags = [] # optional
|
||||
|
||||
+2
-2
@@ -8,10 +8,10 @@ if(CMAKE_BUILD_TYPE)
|
||||
endif()
|
||||
"""
|
||||
name = "cmkr"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
subdirs = ["third_party"]
|
||||
subdirs = ["third_party", "tests"]
|
||||
|
||||
[target.cmkrlib]
|
||||
type = "static"
|
||||
|
||||
+4
-4
@@ -1,15 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
static const char *hello_world = R"lit(
|
||||
static const char *hello_world = &R"lit(
|
||||
#include <iostream>
|
||||
|
||||
int %s() {
|
||||
std::cout << "Hello World!\n";
|
||||
return 0;
|
||||
}
|
||||
)lit" + 1; // skip initial newline
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *cmake_toml = R"lit(
|
||||
static const char *cmake_toml = &R"lit(
|
||||
[cmake]
|
||||
version = "3.15"
|
||||
# subdirs = []
|
||||
@@ -43,4 +43,4 @@ include-directories = ["include"]
|
||||
[[install]]
|
||||
%s = ["%s"]
|
||||
destination = "${CMAKE_INSTALL_PREFIX}/%s"
|
||||
)lit" + 1; // skip initial newline
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
@@ -21,8 +21,7 @@ const char *handle_args(int argc, char **argv) {
|
||||
throw std::runtime_error(cmkr::help::message());
|
||||
std::string main_arg = args[1];
|
||||
if (main_arg == "gen") {
|
||||
auto current_path = fs::current_path();
|
||||
auto ret = cmkr::gen::generate_cmake(current_path.string().c_str());
|
||||
auto ret = cmkr::gen::generate_cmake(fs::current_path().string().c_str());
|
||||
if (ret)
|
||||
return "CMake generation error!";
|
||||
return "CMake generation successful!";
|
||||
|
||||
@@ -22,9 +22,8 @@ int run(int argc, char **argv) {
|
||||
}
|
||||
std::stringstream ss;
|
||||
|
||||
if (!fs::exists("CMakeLists.txt"))
|
||||
if (gen::generate_cmake("."))
|
||||
throw std::runtime_error("CMake generation failure!");
|
||||
if (gen::generate_cmake(fs::current_path().string().c_str()))
|
||||
throw std::runtime_error("CMake generation failure!");
|
||||
|
||||
ss << "cmake -S. -B" << cmake.build_dir << " ";
|
||||
|
||||
|
||||
+55
-50
@@ -33,9 +33,12 @@ static EnumType to_enum(const std::string &str, const std::string &help_name) {
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::vector<std::string> optional_array(const TomlBasicValue &v, const toml::key &ky) {
|
||||
return toml::find_or(v, ky, std::vector<std::string>());
|
||||
};
|
||||
template <typename T>
|
||||
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination) {
|
||||
if (v.contains(ky)) {
|
||||
destination = toml::find<T>(v, ky);
|
||||
}
|
||||
}
|
||||
|
||||
CMake::CMake(const std::string &path, bool build) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
@@ -50,38 +53,38 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
throw std::runtime_error("bin-dir has been renamed to build-dir");
|
||||
}
|
||||
|
||||
build_dir = toml::find_or(cmake, "build-dir", "");
|
||||
generator = toml::find_or(cmake, "generator", "");
|
||||
config = toml::find_or(cmake, "config", "");
|
||||
gen_args = optional_array(cmake, "arguments");
|
||||
get_optional(cmake, "build-dir", build_dir);
|
||||
get_optional(cmake, "generator", generator);
|
||||
get_optional(cmake, "config", config);
|
||||
get_optional(cmake, "arguments", gen_args);
|
||||
}
|
||||
} else {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
cmake_version = toml::find(cmake, "version").as_string();
|
||||
cmkr_include = toml::find_or(cmake, "cmkr-include", cmkr_include);
|
||||
cppflags = optional_array(cmake, "cpp-flags");
|
||||
cflags = optional_array(cmake, "c-flags");
|
||||
linkflags = optional_array(cmake, "link-flags");
|
||||
get_optional(cmake, "cmkr-include", cmkr_include);
|
||||
get_optional(cmake, "cpp-flags", cppflags);
|
||||
get_optional(cmake, "c-flags", cflags);
|
||||
get_optional(cmake, "link-flags", linkflags);
|
||||
}
|
||||
|
||||
if (toml.contains("project")) {
|
||||
const auto &project = toml::find(toml, "project");
|
||||
project_name = toml::find(project, "name").as_string();
|
||||
project_version = toml::find_or(project, "version", "");
|
||||
project_description = toml::find_or(project, "description", "");
|
||||
project_languages = optional_array(project, "languages");
|
||||
cmake_before = toml::find_or(project, "cmake-before", "");
|
||||
cmake_after = toml::find_or(project, "cmake-after", "");
|
||||
include_before = optional_array(project, "include-before");
|
||||
include_after = optional_array(project, "include-after");
|
||||
subdirs = optional_array(project, "subdirs");
|
||||
get_optional(project, "version", project_version);
|
||||
get_optional(project, "description", project_description);
|
||||
get_optional(project, "languages", project_languages);
|
||||
get_optional(project, "cmake-before", cmake_before);
|
||||
get_optional(project, "cmake-after", cmake_after);
|
||||
get_optional(project, "include-before", include_before);
|
||||
get_optional(project, "include-after", include_after);
|
||||
get_optional(project, "subdirs", subdirs);
|
||||
}
|
||||
|
||||
if (toml.contains("settings")) {
|
||||
using set_map = std::map<std::string, TomlBasicValue>;
|
||||
const auto &sets = toml::find<set_map>(toml, "settings");
|
||||
for (const auto set : sets) {
|
||||
for (const auto &set : sets) {
|
||||
Setting s;
|
||||
s.name = set.first;
|
||||
if (set.second.is_boolean()) {
|
||||
@@ -89,7 +92,7 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
} else if (set.second.is_string()) {
|
||||
s.val = set.second.as_string();
|
||||
} else {
|
||||
s.comment = toml::find_or(set.second, "comment", "");
|
||||
get_optional(set.second, "comment", s.comment);
|
||||
if (set.second.contains("value")) {
|
||||
auto v = toml::find(set.second, "value");
|
||||
if (v.is_boolean()) {
|
||||
@@ -98,8 +101,8 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
s.val = v.as_string();
|
||||
}
|
||||
}
|
||||
s.cache = toml::find_or(set.second, "cache", false);
|
||||
s.force = toml::find_or(set.second, "force", false);
|
||||
get_optional(set.second, "cache", s.cache);
|
||||
get_optional(set.second, "force", s.force);
|
||||
}
|
||||
settings.push_back(s);
|
||||
}
|
||||
@@ -108,14 +111,14 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
if (toml.contains("options")) {
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto opt : opts) {
|
||||
for (const auto &opt : opts) {
|
||||
Option o;
|
||||
o.name = opt.first;
|
||||
if (opt.second.is_boolean()) {
|
||||
o.val = opt.second.as_boolean();
|
||||
} else {
|
||||
o.comment = toml::find_or(opt.second, "comment", "");
|
||||
o.val = toml::find_or(opt.second, "value", false);
|
||||
get_optional(opt.second, "comment", o.comment);
|
||||
get_optional(opt.second, "value", o.val);
|
||||
}
|
||||
options.push_back(o);
|
||||
}
|
||||
@@ -130,17 +133,17 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
if (pkg.second.is_string()) {
|
||||
p.version = pkg.second.as_string();
|
||||
} else {
|
||||
p.version = toml::find_or(pkg.second, "version", p.version);
|
||||
p.required = toml::find_or(pkg.second, "required", p.required);
|
||||
p.config = toml::find_or(pkg.second, "config", p.config);
|
||||
p.components = toml::find_or(pkg.second, "components", p.components);
|
||||
get_optional(pkg.second, "version", p.version);
|
||||
get_optional(pkg.second, "required", p.required);
|
||||
get_optional(pkg.second, "config", p.config);
|
||||
get_optional(pkg.second, "components", p.components);
|
||||
}
|
||||
packages.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to std::vector<Content> instead of this hacky thing?
|
||||
contents = toml::find_or<decltype(contents)>(toml, "fetch-content", {});
|
||||
get_optional(toml, "fetch-content", contents);
|
||||
|
||||
if (toml.contains("bin")) {
|
||||
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
|
||||
@@ -155,7 +158,7 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
target.name = itr.first;
|
||||
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
|
||||
|
||||
target.sources = optional_array(t, "sources");
|
||||
get_optional(t, "sources", target.sources);
|
||||
|
||||
#define renamed(from, to) \
|
||||
if (t.contains(from)) { \
|
||||
@@ -169,14 +172,14 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
|
||||
#undef renamed
|
||||
|
||||
target.compile_definitions = optional_array(t, "compile-definitions");
|
||||
target.compile_features = optional_array(t, "compile-features");
|
||||
target.compile_options = optional_array(t, "compile-options");
|
||||
target.include_directories = optional_array(t, "include-directories");
|
||||
target.link_directories = optional_array(t, "link-directories");
|
||||
target.link_libraries = optional_array(t, "link-libraries");
|
||||
target.link_options = optional_array(t, "link-options");
|
||||
target.precompile_headers = optional_array(t, "precompile-headers");
|
||||
get_optional(t, "compile-definitions", target.compile_definitions);
|
||||
get_optional(t, "compile-features", target.compile_features);
|
||||
get_optional(t, "compile-options", target.compile_options);
|
||||
get_optional(t, "include-directories", target.include_directories);
|
||||
get_optional(t, "link-directories", target.link_directories);
|
||||
get_optional(t, "link-libraries", target.link_libraries);
|
||||
get_optional(t, "link-options", target.link_options);
|
||||
get_optional(t, "precompile-headers", target.precompile_headers);
|
||||
|
||||
if (t.contains("alias")) {
|
||||
target.alias = toml::find(t, "alias").as_string();
|
||||
@@ -187,10 +190,10 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
target.properties = toml::find<prop_map>(t, "properties");
|
||||
}
|
||||
|
||||
target.cmake_before = toml::find_or(t, "cmake-before", target.cmake_before);
|
||||
target.cmake_after = toml::find_or(t, "cmake-after", target.cmake_after);
|
||||
target.include_before = optional_array(t, "include-before");
|
||||
target.include_after = optional_array(t, "include-after");
|
||||
get_optional(t, "cmake-before", target.cmake_before);
|
||||
get_optional(t, "cmake-after", target.cmake_after);
|
||||
get_optional(t, "include-before", target.include_before);
|
||||
get_optional(t, "include-after", target.include_after);
|
||||
|
||||
targets.push_back(target);
|
||||
}
|
||||
@@ -201,8 +204,10 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
for (const auto &t : ts) {
|
||||
Test test;
|
||||
test.name = toml::find(t, "name").as_string();
|
||||
test.cmd = toml::find(t, "type").as_string();
|
||||
test.args = optional_array(t, "arguments");
|
||||
get_optional(t, "configurations", test.configurations);
|
||||
get_optional(t, "working-directory", test.working_directory);
|
||||
test.command = toml::find(t, "command").as_string();
|
||||
get_optional(t, "arguments", test.arguments);
|
||||
tests.push_back(test);
|
||||
}
|
||||
}
|
||||
@@ -211,10 +216,10 @@ CMake::CMake(const std::string &path, bool build) {
|
||||
const auto &ts = toml::find(toml, "install").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Install inst;
|
||||
inst.targets = optional_array(t, "targets");
|
||||
inst.files = optional_array(t, "files");
|
||||
inst.dirs = optional_array(t, "dirs");
|
||||
inst.configs = optional_array(t, "configs");
|
||||
get_optional(t, "targets", inst.targets);
|
||||
get_optional(t, "files", inst.files);
|
||||
get_optional(t, "dirs", inst.dirs);
|
||||
get_optional(t, "configs", inst.configs);
|
||||
inst.destination = toml::find(t, "destination").as_string();
|
||||
installs.push_back(inst);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ struct Setting {
|
||||
struct Option {
|
||||
std::string name;
|
||||
std::string comment;
|
||||
bool val;
|
||||
bool val = false;
|
||||
};
|
||||
|
||||
struct Package {
|
||||
@@ -48,7 +48,7 @@ enum TargetType {
|
||||
|
||||
struct Target {
|
||||
std::string name;
|
||||
TargetType type;
|
||||
TargetType type = {};
|
||||
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
|
||||
std::vector<std::string> compile_definitions;
|
||||
@@ -72,8 +72,10 @@ struct Target {
|
||||
|
||||
struct Test {
|
||||
std::string name;
|
||||
std::string cmd;
|
||||
std::vector<std::string> args;
|
||||
std::vector<std::string> configurations;
|
||||
std::string working_directory;
|
||||
std::string command;
|
||||
std::vector<std::string> arguments;
|
||||
};
|
||||
|
||||
struct Install {
|
||||
|
||||
+8
-4
@@ -70,6 +70,8 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
|
||||
for (auto &path : temp) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(temp.begin(), temp.end());
|
||||
return temp;
|
||||
}
|
||||
|
||||
@@ -272,7 +274,7 @@ struct Command {
|
||||
CommandEndl operator()(Ts &&...values) {
|
||||
generated = true;
|
||||
ss << indent(depth) << command << '(';
|
||||
std::initializer_list<bool>{print_arg(values)...};
|
||||
(void)std::initializer_list<bool>{print_arg(values)...};
|
||||
if (had_newline)
|
||||
ss << '\n';
|
||||
ss << ")\n";
|
||||
@@ -609,12 +611,14 @@ int generate_cmake(const char *path, bool root) {
|
||||
}
|
||||
|
||||
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();
|
||||
auto configurations = std::make_pair("CONFIGURATIONS", test.configurations);
|
||||
auto working_directory = std::make_pair("WORKING_DIRECTORY", test.working_directory);
|
||||
auto command = std::make_pair("COMMAND", test.command);
|
||||
auto arguments = std::make_pair("", test.arguments);
|
||||
cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# This file was generated automatically by cmkr.
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
basic
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# This file was generated automatically by cmkr.
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Regenerate CMakeLists.txt automatically in the root project
|
||||
set(CMKR_ROOT_PROJECT OFF)
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CMKR_ROOT_PROJECT ON)
|
||||
|
||||
# Bootstrap cmkr
|
||||
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
|
||||
if(CMKR_INCLUDE_RESULT)
|
||||
cmkr()
|
||||
endif()
|
||||
|
||||
# Enable folder support
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
endif()
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
project(basic)
|
||||
|
||||
# Target basic
|
||||
set(basic_SOURCES
|
||||
"src/basic.cpp"
|
||||
cmake.toml
|
||||
)
|
||||
|
||||
add_executable(basic ${basic_SOURCES})
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT basic)
|
||||
endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${basic_SOURCES})
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[cmake]
|
||||
version = "3.5"
|
||||
|
||||
[project]
|
||||
name = "basic"
|
||||
|
||||
[target.basic]
|
||||
type = "executable"
|
||||
sources = ["src/basic.cpp"]
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr!");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[[test]]
|
||||
name = "basic"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
arguments = ["build"]
|
||||
Reference in New Issue
Block a user