Compare commits

..

13 Commits

Author SHA1 Message Date
Mohammed Alyousef 7b7b260379 Merge pull request #6 from MoAlyousef/develop
Ongoing development
2021-04-20 02:33:29 +03:00
Duncan Ogilvie 747bc8be00 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	CMakeLists.txt
#	README.md
#	cmake.toml
#	include/literals.h
#	src/cmake.cpp
#	src/cmake.hpp
#	src/gen.cpp
2021-04-19 16:22:08 +02:00
MoAlyousef 00121c261f 0.1.4 2021-04-11 04:52:44 +03:00
Duncan Ogilvie 326647011b Initial attempt at adding tests 2021-04-06 15:02:01 +02:00
Duncan Ogilvie 598c9b9ba8 Sort globbed files alphabetically for consistent cross-OS generation 2021-04-06 14:42:07 +02:00
Duncan Ogilvie d62dff29b1 Implement stronger type checking for optional values 2021-04-06 14:08:31 +02:00
Duncan Ogilvie 783f343e40 Run GitHub Actions on a bigger matrix 2021-04-06 12:52:31 +02:00
Duncan Ogilvie 5312b44860 Fix warnings on AppleClang 12 2021-04-06 12:19:47 +02:00
Duncan Ogilvie 6162e86329 Respect default members while building 2021-04-05 18:54:24 +02:00
Duncan Ogilvie 5a20c0fe42 Initial vcpkg support 2021-04-05 16:47:18 +02:00
Duncan Ogilvie b5752b7c2b Breaking change, rename 'cmake.minimum' to 'cmake.version' and make it optional 2021-04-05 14:49:32 +02:00
Duncan Ogilvie 5c942599a5 Recursively bootstrap cmkr.cmake 2021-04-04 17:12:24 +02:00
Duncan Ogilvie 84f6b39f35 Clean up cmkr bootstrapping 2021-04-04 17:11:49 +02:00
18 changed files with 390 additions and 181 deletions
+13 -8
View File
@@ -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
View File
@@ -5,6 +5,7 @@ compile_commands.json
temp.*
.vs
.cache
build*/
.idea/
cmake-build*/
build*/
.idea/
cmake-build*/
CMakeLists.txt.user
+5
View File
@@ -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.
+30 -11
View File
@@ -1,18 +1,26 @@
# This file was generated automatically by cmkr.
# Create a configure-time dependency on cmake.toml to improve IDE support
configure_file(cmake.toml cmake.toml COPYONLY)
# Regenerate CMakeLists.txt file when necessary
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
if(CMKR_INCLUDE_RESULT)
cmkr()
endif()
cmake_minimum_required(VERSION 2.8...3.8)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# 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()
# Hack to hide a warning during cmkr bootstrapping on Windows
if(CMAKE_BUILD_TYPE)
@@ -28,6 +36,7 @@ project(cmkr
"CMakeLists generator from TOML"
)
# third_party
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party")
@@ -37,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
+3 -1
View File
@@ -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
+3 -3
View File
@@ -1,5 +1,5 @@
[cmake]
minimum = "2.8...3.8"
version = "2.8...3.8"
[project]
cmake-before = """
@@ -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"
+98 -53
View File
@@ -1,20 +1,41 @@
include_guard()
# Change these defaults to point to your infrastructure if desired
set(CMKR_REPO "https://github.com/MoAlyousef/cmkr" CACHE STRING "cmkr git repository")
set(CMKR_TAG "archive_84f6b39f" CACHE STRING "cmkr git tag (this needs to be available forever)")
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
# Disable cmkr if generation is disabled
if(CMKR_SKIP_GENERATION)
message(STATUS "[cmkr] Skipping automatic cmkr generation")
macro(cmkr)
endmacro()
return()
endif()
# Disable cmkr if no cmake.toml file is found
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml)
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
macro(cmkr)
endmacro()
return()
endif()
# Convert a Windows native path to CMake path
if(CMKR_EXECUTABLE MATCHES "\\\\")
string(REPLACE "\\" "/" CMKR_EXECUTABLE_CMAKE "${CMKR_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_EXECUTABLE_CMAKE}" CACHE FILEPATH "" FORCE)
unset(CMKR_EXECUTABLE_CMAKE)
endif()
# Helper macro to execute a process (COMMAND_ERROR_IS_FATAL ANY is 3.19 and higher)
macro(cmkr_exec)
function(cmkr_exec)
execute_process(COMMAND ${ARGV} RESULT_VARIABLE CMKR_EXEC_RESULT)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "cmkr_exec(${ARGV}) failed (exit code ${CMKR_EXEC_RESULT})")
endif()
endmacro()
endfunction()
# Windows-specific hack (CMAKE_EXECUTABLE_PREFIX is not set at the moment)
if(WIN32)
@@ -24,79 +45,103 @@ else()
endif()
# Use cached cmkr if found
if(DEFINED CACHE{CMKR_EXECUTABLE} AND EXISTS ${CMKR_EXECUTABLE})
set(CMKR_CACHED_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/_cmkr/bin/${CMKR_EXECUTABLE_NAME}")
if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}")
message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'")
elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE)
message(FATAL_ERROR "[cmkr] '${CMKR_EXECUTABLE}' not found")
else()
if(DEFINED CACHE{CMKR_EXECUTABLE})
message(VERBOSE "[cmkr] '${CMKR_EXECUTABLE}' not found")
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'")
message(STATUS "[cmkr] Fetching cmkr...")
set(CMKR_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
if(EXISTS "${CMKR_DIRECTORY}")
cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}")
endif()
set(CMKR_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/_cmkr)
set(CMKR_EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/_cmkr/bin/${CMKR_EXECUTABLE_NAME} CACHE INTERNAL "Full path to cmkr executable")
find_package(Git QUIET REQUIRED)
cmkr_exec("${GIT_EXECUTABLE}"
clone
--config advice.detachedHead=false
--branch ${CMKR_TAG}
--depth 1
${CMKR_REPO}
"${CMKR_DIRECTORY}"
)
message(STATUS "[cmkr] Building cmkr...")
cmkr_exec("${CMAKE_COMMAND}"
"${CMKR_DIRECTORY}"
"-B${CMKR_DIRECTORY}/build"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY}"
)
cmkr_exec("${CMAKE_COMMAND}"
--build "${CMKR_DIRECTORY}/build"
--config Release
--parallel
)
cmkr_exec("${CMAKE_COMMAND}"
--install "${CMKR_DIRECTORY}/build"
--config Release
--prefix "${CMKR_DIRECTORY}"
--component cmkr
)
if(NOT EXISTS ${CMKR_EXECUTABLE})
message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'")
message(STATUS "[cmkr] Fetching cmkr...")
if(EXISTS ${CMKR_DIRECTORY})
cmkr_exec(${CMAKE_COMMAND} -E rm -rf ${CMKR_DIRECTORY})
endif()
find_package(Git QUIET REQUIRED)
set(CMKR_REPO "https://github.com/moalyousef/cmkr")
cmkr_exec(${GIT_EXECUTABLE} clone ${CMKR_REPO} ${CMKR_DIRECTORY})
cmkr_exec(${CMAKE_COMMAND} ${CMKR_DIRECTORY} -B${CMKR_DIRECTORY}/build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY})
cmkr_exec(${CMAKE_COMMAND} --build ${CMKR_DIRECTORY}/build --config Release --parallel)
cmkr_exec(${CMAKE_COMMAND} --install ${CMKR_DIRECTORY}/build --config Release --prefix ${CMKR_DIRECTORY} --component cmkr)
if(NOT EXISTS ${CMKR_EXECUTABLE})
message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'")
endif()
cmkr_exec(${CMKR_EXECUTABLE} version)
message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}")
else()
message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'")
message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'")
endif()
cmkr_exec("${CMKR_EXECUTABLE}" version)
message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}")
endif()
execute_process(COMMAND ${CMKR_EXECUTABLE} version
execute_process(COMMAND "${CMKR_EXECUTABLE}" version
RESULT_VARIABLE CMKR_EXEC_RESULT
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
unset(CMKR_EXECUTABLE CACHE)
message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding")
endif()
# This is the macro that contains black magic
macro(cmkr)
# When this macro is called from the generated file, fake some internal CMake variables
get_source_file_property(CMKR_CURRENT_LIST_FILE ${CMAKE_CURRENT_LIST_FILE} CMKR_CURRENT_LIST_FILE)
get_source_file_property(CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" CMKR_CURRENT_LIST_FILE)
if(CMKR_CURRENT_LIST_FILE)
set(CMAKE_CURRENT_LIST_FILE ${CMKR_CURRENT_LIST_FILE})
get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
set(CMAKE_CURRENT_LIST_FILE "${CMKR_CURRENT_LIST_FILE}")
get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
endif()
# File-based include guard (include_guard is not documented to work)
get_source_file_property(CMKR_INCLUDE_GUARD ${CMAKE_CURRENT_LIST_FILE} CMKR_INCLUDE_GUARD)
get_source_file_property(CMKR_INCLUDE_GUARD "${CMAKE_CURRENT_LIST_FILE}" CMKR_INCLUDE_GUARD)
if(NOT CMKR_INCLUDE_GUARD)
set_source_files_properties(${CMAKE_CURRENT_LIST_FILE} PROPERTIES CMKR_INCLUDE_GUARD TRUE)
set_source_files_properties("${CMAKE_CURRENT_LIST_FILE}" PROPERTIES CMKR_INCLUDE_GUARD TRUE)
file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_PRE)
# Generate CMakeLists.txt
cmkr_exec(${CMKR_EXECUTABLE} gen
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
cmkr_exec("${CMKR_EXECUTABLE}" gen
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
# Copy the now-generated CMakeLists.txt to CMakerLists.txt
# This is done because you cannot include() a file you are currently in
set(CMKR_TEMP_FILE ${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt)
configure_file(CMakeLists.txt ${CMKR_TEMP_FILE} COPYONLY)
# Add the macro required for the hack at the start of the cmkr macro
set_source_files_properties(${CMKR_TEMP_FILE} PROPERTIES
CMKR_CURRENT_LIST_FILE ${CMAKE_CURRENT_LIST_FILE}
)
# 'Execute' the newly-generated CMakeLists.txt
include(${CMKR_TEMP_FILE})
# Delete the generated file
file(REMOVE ${CMKR_TEMP_FILE})
# Do not execute the rest of the original CMakeLists.txt
return()
file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_POST)
if(NOT CMKR_LIST_FILE_SHA256_PRE STREQUAL CMKR_LIST_FILE_SHA256_POST)
# Copy the now-generated CMakeLists.txt to CMakerLists.txt
# This is done because you cannot include() a file you are currently in
set(CMKR_TEMP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt")
configure_file(CMakeLists.txt "${CMKR_TEMP_FILE}" COPYONLY)
# Add the macro required for the hack at the start of the cmkr macro
set_source_files_properties("${CMKR_TEMP_FILE}" PROPERTIES
CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}"
)
# 'Execute' the newly-generated CMakeLists.txt
include("${CMKR_TEMP_FILE}")
# Delete the generated file
file(REMOVE "${CMKR_TEMP_FILE}")
# Do not execute the rest of the original CMakeLists.txt
return()
endif()
# Resume executing the unmodified CMakeLists.txt
endif()
endmacro()
+5 -5
View File
@@ -1,17 +1,17 @@
#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]
minimum = "3.15"
version = "3.15"
# subdirs = []
# build-dir = ""
# cpp-flags = []
@@ -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
+1 -2
View File
@@ -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!";
+2 -3
View File
@@ -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 << " ";
+69 -50
View File
@@ -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, "minimum").as_string();
cmkr_include = toml::find_or(cmake, "cmkr-include", "cmkr.cmake");
cppflags = optional_array(cmake, "cpp-flags");
cflags = optional_array(cmake, "c-flags");
linkflags = optional_array(cmake, "link-flags");
cmake_version = toml::find(cmake, "version").as_string();
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,16 +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.required = toml::find_or(pkg.second, "required", false);
p.components = toml::find_or(pkg.second, "components", std::vector<std::string>());
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]]");
@@ -154,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)) { \
@@ -168,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();
@@ -186,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_after = toml::find_or(t, "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);
}
@@ -200,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);
}
}
@@ -210,14 +216,27 @@ 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);
}
}
if (toml.contains("vcpkg")) {
const auto &v = toml::find(toml, "vcpkg");
vcpkg.version = toml::find(v, "version").as_string();
vcpkg.packages = toml::find<decltype(vcpkg.packages)>(v, "packages");
// This allows the user to use a custom pmm version if desired
if (contents.count("pmm") == 0) {
contents["pmm"]["url"] = "https://github.com/vector-of-bool/pmm/archive/refs/tags/1.5.1.tar.gz";
// Hack to not execute pmm's example CMakeLists.txt
contents["pmm"]["SOURCE_SUBDIR"] = "pmm";
}
}
}
}
} // namespace cmake
+16 -6
View File
@@ -21,16 +21,22 @@ struct Setting {
struct Option {
std::string name;
std::string comment;
bool val;
bool val = false;
};
struct Package {
std::string name;
std::string version;
bool required = true;
bool config = true;
std::vector<std::string> components;
};
struct Vcpkg {
std::string version;
std::vector<std::string> packages;
};
enum TargetType {
target_executable,
target_library,
@@ -42,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;
@@ -66,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 {
@@ -79,8 +87,9 @@ struct Install {
};
struct CMake {
std::string cmake_version;
std::string cmkr_include;
// This is the CMake version required to use all cmkr versions.
std::string cmake_version = "3.15";
std::string cmkr_include = "cmkr.cmake";
std::string build_dir = "build";
std::string generator;
std::string config;
@@ -101,6 +110,7 @@ struct CMake {
std::vector<Setting> settings;
std::vector<Option> options;
std::vector<Package> packages;
Vcpkg vcpkg;
tsl::ordered_map<std::string, std::map<std::string, std::string>> contents;
std::vector<Target> targets;
std::vector<Test> tests;
+62 -36
View File
@@ -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;
}
@@ -177,7 +179,7 @@ struct Command {
return result;
}
std::string indent(int n) {
static std::string indent(int n) {
std::string result;
for (int i = 0; i < n; i++) {
result += '\t';
@@ -231,10 +233,6 @@ struct Command {
template <class K, class V>
bool print_arg(const std::pair<K, V> &kv) {
std::stringstream tmp;
tmp << kv.second;
auto str = tmp.str();
if (kv.second.empty()) {
return true;
}
@@ -242,7 +240,7 @@ struct Command {
had_newline = true;
print_arg(kv.first);
depth++;
print_arg(str);
print_arg(kv.second);
depth--;
return true;
@@ -276,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";
@@ -305,8 +303,8 @@ int generate_cmake(const char *path, bool root) {
}
return Command(ss, indent, command);
};
auto comment = [&ss](const std::string &comment) {
ss << "# " << comment << '\n';
auto comment = [&ss, &indent](const std::string &comment) {
ss << Command::indent(indent) << "# " << comment << '\n';
return CommandEndl(ss);
};
auto endl = [&ss]() { ss << '\n'; };
@@ -340,26 +338,36 @@ int generate_cmake(const char *path, bool root) {
// TODO: add link with proper documentation
comment("This file was generated automatically by cmkr.").endl();
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY").endl();
cmake::CMake cmake(path, false);
if (root) {
comment("Regenerate CMakeLists.txt file when necessary");
cmd("include")(cmake.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT").endl();
// clang-format off
cmd("if")("CMKR_INCLUDE_RESULT");
cmd("cmkr")();
cmd("endif")().endl();
// clang-format on
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON").endl();
comment("Regenerate CMakeLists.txt automatically in the root project");
cmd("set")("CMKR_ROOT_PROJECT", "OFF");
// clang-format off
cmd("if")("CMAKE_CURRENT_SOURCE_DIR", "STREQUAL", "CMAKE_SOURCE_DIR");
cmd("set")("CMKR_ROOT_PROJECT", "ON").endl();
comment("Bootstrap cmkr");
cmd("include")(cmake.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT");
cmd("if")("CMKR_INCLUDE_RESULT");
cmd("cmkr")();
cmd("endif")().endl();
comment("Enable folder support");
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON");
cmd("endif")().endl();
// clang-format on
}
// clang-format off
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
cmd("if")("CMKR_ROOT_PROJECT");
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
cmd("endif")().endl();
// clang-format on
// TODO: remove support and replace with global compile-features
if (!cmake.cppflags.empty()) {
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
@@ -400,17 +408,6 @@ int generate_cmake(const char *path, bool root) {
inject_includes(cmake.include_after);
inject_cmake(cmake.cmake_after);
if (!cmake.packages.empty()) {
for (const auto &dep : cmake.packages) {
auto version = dep.version;
if (version == "*")
version.clear();
auto required = dep.required ? "REQUIRED" : "";
auto components = std::make_pair("COMPONENTS", dep.components);
cmd("find_package")(dep.name, version, required, components).endl();
}
}
if (!cmake.contents.empty()) {
cmd("include")("FetchContent").endl();
for (const auto &dep : cmake.contents) {
@@ -440,7 +437,32 @@ int generate_cmake(const char *path, bool root) {
}
}
if (!cmake.vcpkg.version.empty()) {
assert("pmm is required in fetch-content for vcpkg to work" && cmake.contents.count("pmm") != 0);
comment("Bootstrap vcpkg");
cmd("include")("${pmm_SOURCE_DIR}/pmm.cmake");
tsl::ordered_map<std::string, std::vector<std::string>> vcpkg_args;
vcpkg_args["REVISION"] = { cmake.vcpkg.version };
vcpkg_args["REQUIRES"] = cmake.vcpkg.packages;
auto vcpkg = std::make_pair("VCPKG", vcpkg_args);
cmd("pmm")(vcpkg).endl();
}
if (!cmake.packages.empty()) {
comment("Packages");
for (const auto &dep : cmake.packages) {
auto version = dep.version;
if (version == "*")
version.clear();
auto required = dep.required ? "REQUIRED" : "";
auto config = dep.config ? "CONFIG" : "";
auto components = std::make_pair("COMPONENTS", dep.components);
cmd("find_package")(dep.name, version, required, config, components).endl();
}
}
if (!cmake.options.empty()) {
comment("Options");
for (const auto &opt : cmake.options) {
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
}
@@ -448,6 +470,7 @@ int generate_cmake(const char *path, bool root) {
}
if (!cmake.settings.empty()) {
comment("Settings");
for (const auto &set : cmake.settings) {
std::string set_val;
if (set.val.index() == 1) {
@@ -471,6 +494,7 @@ int generate_cmake(const char *path, bool root) {
if (!cmake.subdirs.empty()) {
for (const auto &dir : cmake.subdirs) {
// clang-format off
comment(dir);
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
cmd("if")("CMAKE_FOLDER");
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
@@ -587,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();
}
}
+19
View File
@@ -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
)
+41
View File
@@ -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})
+9
View File
@@ -0,0 +1,9 @@
[cmake]
version = "3.5"
[project]
name = "basic"
[target.basic]
type = "executable"
sources = ["src/basic.cpp"]
+5
View File
@@ -0,0 +1,5 @@
#include <cstdio>
int main() {
puts("Hello from cmkr!");
}
+5
View File
@@ -0,0 +1,5 @@
[[test]]
name = "basic"
command = "$<TARGET_FILE:cmkr>"
working-directory = "${CMAKE_CURRENT_LIST_DIR}/basic"
arguments = ["build"]