Compare commits

..

21 Commits

Author SHA1 Message Date
Duncan Ogilvie 9622334bf1 Switch to using target_sources instead of passing sources directly to add_xxx 2021-05-03 19:39:22 +02:00
Duncan Ogilvie 6ad29ef624 Allow conditional subdirs 2021-05-03 19:37:39 +02:00
Duncan Ogilvie 718c2c7527 Get rid of redundant name field in cmkr init 2021-05-03 17:45:51 +02:00
Duncan Ogilvie 8ea0c7396c Merge pull request #19 from build-cpp/project-refactor
Project refactor
2021-05-03 17:42:08 +02:00
Duncan Ogilvie 34c12379c2 Add a check to see whether cmkr was run before pushing 2021-05-03 17:35:31 +02:00
Duncan Ogilvie 6395267e4b Merge cmkrlib into the cmkr target 2021-05-03 17:23:33 +02:00
Duncan Ogilvie 6d9b40bd15 Get rid of hack to hide a warning during bootstrapping on Windows 2021-05-03 17:08:23 +02:00
Duncan Ogilvie c4673d59e2 Simplify default conditions
It was JustMagic's idea to add 'lunix'
2021-05-03 17:05:19 +02:00
Duncan Ogilvie 220dec6bbb Merge pull request #18 from darknessxk/task/change-init-code
Task/change init code
2021-05-03 16:27:16 +02:00
darknessxk 4efa74f91f Changed project string from [[target]] to [target.%s] 2021-05-03 11:21:59 -03:00
darknessxk fbd07c47bc Added argument to target name 2021-05-03 11:21:26 -03:00
Duncan Ogilvie 17d4dc0555 Move all links to the new organization 2021-05-03 15:42:37 +02:00
Duncan Ogilvie a6dfb2272d Add docs placeholder for pages 2021-05-03 14:09:42 +02:00
Duncan Ogilvie 7fdafa4189 Delete CHANGELOG.md and move to GitHub releases 2021-05-03 11:45:28 +02:00
Duncan Ogilvie 7cdf36f317 Initial support for conditional arguments 2021-05-02 23:53:12 +02:00
Duncan Ogilvie bf14f069a7 Minor fixes (removed TODOs and rename checks) 2021-05-02 21:51:04 +02:00
Duncan Ogilvie 3b4bc919a1 Merge branch 'bootstrapper-upgrade' 2021-05-02 02:22:46 +02:00
Duncan Ogilvie c0bdbeb9d8 Merge branch 'array-properties' 2021-05-02 02:22:39 +02:00
Duncan Ogilvie 42725d0339 Update the README to be in sync with master 2021-05-02 02:12:24 +02:00
Duncan Ogilvie 8f393d967d Make cmkr bootstrapper more robust 2021-05-01 00:44:29 +02:00
Duncan Ogilvie 1cf9240579 Support an array of properties
[target.example.properties]
SOME_LIST = [
    "item1",
    "item2",
]
2021-04-26 01:01:28 +02:00
36 changed files with 486 additions and 449 deletions
+1
View File
@@ -0,0 +1 @@
CMakeLists.txt linguist-generated
+11 -1
View File
@@ -4,6 +4,8 @@ on: [push, pull_request]
jobs:
build:
# Skip building pull requests from the same repository
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != '${{ github.repository }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@@ -14,10 +16,18 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build
run: |
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} "-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install"
cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
cmake --install build --config ${{ env.BUILD_TYPE }}
- name: Check if cmkr was run
run: |
./install/bin/cmkr gen
git diff --exit-code
- name: Test
run: |
cd build/tests
-27
View File
@@ -1,27 +0,0 @@
# 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.
- Add dependency on ghc_filesystem and mpark_variant which are fetched automatically using FetchContent.
## 0.1.2 - 2020-11-20
- Add support for target properties.
- Add installs.
- Require cmake >= 3.15.
- Support settings and caching settings.
- Support config when running cmkr build.
- Add prompt prior to CMakeLists.txt generation if already existent in the current dir.
## 0.1.1 - 2020-11-19
- Add support for globbing.
- Add support for find_package components.
- Add options.
- Support aliases.
- Support interface libs (header-only libs).
- Support testing.
+46 -49
View File
@@ -1,4 +1,5 @@
# This file was generated automatically by cmkr.
# This file is automatically generated from cmake.toml - DO NOT EDIT
# See https://github.com/build-cpp/cmkr for more information
cmake_minimum_required(VERSION 2.8...3.8)
@@ -22,16 +23,11 @@ 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)
endif()
project(cmkr
LANGUAGES
CXX
VERSION
0.1.3
0.1.4
DESCRIPTION
"CMakeLists generator from TOML"
)
@@ -57,52 +53,40 @@ add_subdirectory(tests)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Target cmkrlib
set(cmkrlib_SOURCES
"src/cmkrlib/args.cpp"
"src/cmkrlib/build.cpp"
"src/cmkrlib/cmake.cpp"
"src/cmkrlib/error.cpp"
"src/cmkrlib/gen.cpp"
"src/cmkrlib/help.cpp"
"src/cmkrlib/cmake.hpp"
"src/cmkrlib/enum_helper.hpp"
"src/cmkrlib/fs.hpp"
"include/args.h"
"include/build.h"
"include/error.h"
"include/gen.h"
"include/help.h"
"include/literals.h"
cmake.toml
)
add_library(cmkrlib STATIC ${cmkrlib_SOURCES})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkrlib_SOURCES})
target_compile_features(cmkrlib PUBLIC
cxx_std_11
)
target_include_directories(cmkrlib PUBLIC
include
)
target_link_libraries(cmkrlib PUBLIC
toml11
ghc_filesystem
mpark_variant
ordered_map
)
# Target cmkr
set(cmkr_SOURCES
set(CMKR_TARGET cmkr)
set(cmkr_SOURCES "")
list(APPEND cmkr_SOURCES
"src/args.cpp"
"src/build.cpp"
"src/cmake.cpp"
"src/error.cpp"
"src/gen.cpp"
"src/help.cpp"
"src/main.cpp"
"include/args.hpp"
"include/build.hpp"
"include/cmake.hpp"
"include/enum_helper.hpp"
"include/error.hpp"
"include/fs.hpp"
"include/gen.hpp"
"include/help.hpp"
"include/literals.hpp"
)
list(APPEND cmkr_SOURCES
cmake.toml
)
add_executable(cmkr ${cmkr_SOURCES})
set(CMKR_SOURCES ${cmkr_SOURCES})
add_executable(cmkr)
if(cmkr_SOURCES)
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
endif()
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
if(NOT CMKR_VS_STARTUP_PROJECT)
@@ -111,10 +95,23 @@ endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES})
target_link_libraries(cmkr PRIVATE
cmkrlib
target_compile_features(cmkr PRIVATE
cxx_std_11
)
target_include_directories(cmkr PRIVATE
include
)
target_link_libraries(cmkr PRIVATE
toml11
ghc_filesystem
mpark_variant
ordered_map
)
unset(CMKR_TARGET)
unset(CMKR_SOURCES)
install(
TARGETS
cmkr
+19 -124
View File
@@ -1,19 +1,13 @@
# cmkr
cmkr, pronounced "cmaker", is A CMakeLists.txt generator from TOML.
cmkr, pronounced "cmaker", is a modern build system based on CMake and TOML. It was originally created by [Mohammed Alyousef](https://github.com/MoAlyousef).
**NOTE**: The documentation is currently a work-in-progress due to breaking changes since `0.1.4`. For examples you can check the [cmkr GitHub topic](https://github.com/topics/cmkr) and the [tests](https://github.com/build-cpp/cmkr/tree/main/tests).
## Building
cmkr requires a C++11 compiler, cmake >= 3.15.
```
git clone https://github.com/moalyousef/cmkr
cd cmkr
cmake -Bbin
cmake --build bin --parallel
```
## Getting started
`cmkr` parses `cmake.toml` files and generates a modern, idomatic `CMakeLists.txt` for you. A basic hello world format with the minimum required fields:
## Usage
cmkr parses cmake.toml files (using toml11 by Toru Niina) at the project directory. A basic hello world format with the minimum required fields:
```toml
[cmake]
minimum = "3.15"
@@ -22,106 +16,26 @@ minimum = "3.15"
name = "app"
version = "0.1.0"
[[target]]
name = "app"
[target.app]
type = "executable"
sources = ["src/main.cpp"]
```
This project's cmake.toml:
```toml
[cmake]
minimum = "3.15"
## Building
[project]
name = "cmkr"
version = "0.1.4"
description = "CMakeLists generator from TOML"
`cmkr` requires a C++11 compiler and CMake >= ~3.x (exact minimum version is not yet specified). C++11 was picked to allow the broadest possible set of compilers to bootstrap `cmkr`.
[fetch-content]
toml11 = { git = "https://github.com/ToruNiina/toml11" }
filesystem = { git = "https://github.com/gulrak/filesystem" }
mpark_variant = { url = "https://github.com/mpark/variant/archive/v1.4.0.tar.gz" }
[[target]]
name = "cmkrlib"
type = "static"
sources = ["src/cmake.cpp", "src/gen.cpp", "src/help.cpp", "src/build.cpp", "src/error.cpp"]
include-directories = ["include"]
compile-features = ["cxx_std_11"]
link-libraries = ["toml11::toml11", "ghc_filesystem"]
[[target]]
name = "cmkr"
type = "executable"
sources = ["src/main.cpp", "src/args.cpp"]
link-libraries = ["cmkrlib"]
[[install]]
targets = ["cmkr"]
destination = "${CMAKE_INSTALL_PREFIX}/bin"
```sh
git clone https://github.com/moalyousef/cmkr
cd cmkr
cmake -Bbin
cmake --build bin --parallel
```
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
c-flags = [] # optional
link-flags = [] # optional
generator = "Ninja" # optional, only valid when run using: cmkr build
config = "Release" # optional, only valid when run using: cmkr build
arguments = ["CMAKE_TOOLCHAIN_FILE=/path/to/toolchain"] # optional, valid when run using: cmkr build
## Command line
[settings] # optional
CMAKE_BUILD_TYPE = "Release"
TOML_BUILD_TESTS = false # optional
TOML_BUILD_DOCS = { value = false, comment = "builds dependency docs", cache = true, force = true } # optional
OLD_VERSION = "0.1.1" # optional
The `cmkr` executable can be run from the command-line:
[project] # required per project
name = "app" # required
version = "0.1.0" # required
[find-package] # optional, runs find_package, use "*" to ignore version
Boost = { version = "1.74.0", required = false, components = ["system"] } # optional
spdlog = "*"
[fetch-content] # optional, runs fetchContent
toml11 = { git = "https://github.com/ToruNiina/toml11", tag = "v3.5.0" } # optional
[options] # optional
APP_BUILD_STUFF = false # optional
APP_OTHER_STUFF = { comment = "does other stuff", value = false } # optional
[[target]] # required, can define several binaries
name = "app" # required
type = "executable" # required (executable || library || shared || static || interface)
sources = ["src/*.cpp"] # required, supports globbing
include-directories = ["include"] # optional
alias = "" # optional
compile-features = [] # optional
compile-definitions = [] # optional
link-libraries = [] # optional
properties = { PROPERTY1 = "property1", ... } # optional
[[test]] # optional, can define several
name = "test1" # required
command = "app" # required
arguments = ["arg1", "arg2"] # optional
[[install]] # optional, can define several
targets = ["app"] # optional
files = ["include/*.h"] # optional
dirs = [] # optional
configs = [] # optional (Release|Debug...etc)
destination = "${CMAKE_INSTALL_PREFIX}/bin" # required
```
The cmkr executable can be run from the command-line:
```
Usage: cmkr [arguments]
arguments:
@@ -133,28 +47,9 @@ arguments:
help Show help.
version Current cmkr version.
```
The build command invokes cmake and the default build-system on your platform (unless a generator is specified), it also accepts extra cmake build arguments:
```
The build command invokes `cmake` and the default build-system on your platform (unless a generator is specified), it also accepts extra build arguments:
```sh
cmkr build --config Release
```
## Binary types
### executable
Executable binary. Equivalent to [add_executable(name)](https://cmake.org/cmake/help/latest/command/add_executable.html).
### library
Library, can be static or shared depending on the BUILD_SHARED_LIBS variable. Equivalent to [add_library()](https://cmake.org/cmake/help/latest/command/add_library.html).
### static
Static library/archive. Equivalent to [add_library(name STATIC)](https://cmake.org/cmake/help/latest/command/add_library.html).
### shared
Shared/dynamic library. Equivalent to [add_library(name SHARED)](https://cmake.org/cmake/help/latest/command/add_library.html).
### interface
Header-only library. Equivalent to [add_library(name INTERFACE)](https://cmake.org/cmake/help/latest/command/add_library.html).
## Roadmap
- Support more cmake fields.
- Support conditional cmake args somehow!
+3 -13
View File
@@ -2,29 +2,19 @@
version = "2.8...3.8"
[project]
cmake-before = """
# Hack to hide a warning during cmkr bootstrapping on Windows
if(CMAKE_BUILD_TYPE)
endif()
"""
name = "cmkr"
version = "0.1.4"
description = "CMakeLists generator from TOML"
languages = ["CXX"]
subdirs = ["third_party", "tests"]
[target.cmkrlib]
type = "static"
sources = ["src/cmkrlib/*.cpp", "src/cmkrlib/*.hpp", "include/*.h"]
[target.cmkr]
type = "executable"
sources = ["src/*.cpp", "include/*.hpp"]
include-directories = ["include"]
compile-features = ["cxx_std_11"]
link-libraries = ["toml11", "ghc_filesystem", "mpark_variant", "ordered_map"]
[target.cmkr]
type = "executable"
sources = ["src/main.cpp"]
link-libraries = ["cmkrlib"]
[[install]]
targets = ["cmkr"]
destination = "${CMAKE_INSTALL_PREFIX}/bin"
+32 -15
View File
@@ -1,27 +1,44 @@
# This file was generated automatically by cmkr.
# 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 3.15)
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)
set(example_PROJECT_VERSION 0.1.0)
project(example VERSION ${example_PROJECT_VERSION})
# Bootstrap cmkr
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
if(CMKR_INCLUDE_RESULT)
cmkr()
endif()
set(EXAMPLE_SOURCES
src/example.cpp
# 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(example
VERSION
0.1.0
)
# Target example
set(example_SOURCES
"src/example.cpp"
cmake.toml
)
add_executable(example ${EXAMPLE_SOURCES})
source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${EXAMPLE_SOURCES})
add_executable(example ${example_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 example)
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_SOURCES})
+2 -3
View File
@@ -1,13 +1,12 @@
# This is a minimal example project used for testing the cmkr bootstrapping process
[cmake]
minimum = "3.15"
version = "3.15"
[project]
name = "example"
version = "0.1.0"
[[target]]
name = "example"
[target.example]
type = "executable"
sources = ["src/example.cpp"]
+13 -4
View File
@@ -1,8 +1,10 @@
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_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
set(CMKR_TAG "archive_7cdf36f3" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
# Set these from the command line to customize for development/debugging purposes
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
@@ -45,7 +47,14 @@ else()
endif()
# Use cached cmkr if found
set(CMKR_CACHED_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/_cmkr/bin/${CMKR_EXECUTABLE_NAME}")
set(CMKR_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_${CMKR_TAG}")
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
if(NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE AND CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE)
endif()
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)
@@ -55,7 +64,6 @@ else()
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()
@@ -70,6 +78,7 @@ else()
)
message(STATUS "[cmkr] Building cmkr...")
cmkr_exec("${CMAKE_COMMAND}"
--no-warn-unused-cli
"${CMKR_DIRECTORY}"
"-B${CMKR_DIRECTORY}/build"
"-DCMAKE_BUILD_TYPE=Release"
View File
-8
View File
@@ -1,6 +1,5 @@
#pragma once
#ifdef __cplusplus
namespace cmkr {
namespace args {
@@ -9,11 +8,4 @@ const char *handle_args(int argc, char **argv);
} // namespace args
} // namespace cmkr
extern "C" {
#endif
const char *cmkr_args_handle_args(int, char **);
#ifdef __cplusplus
}
#endif
+2 -10
View File
@@ -1,7 +1,5 @@
#pragma once
#ifdef __cplusplus
namespace cmkr {
namespace build {
@@ -13,15 +11,9 @@ int install();
} // namespace build
} // namespace cmkr
extern "C" {
#endif
int cmkr_build_run(int argc, char **argv);
int cmkr_build_clean(void);
int cmkr_build_clean();
int cmkr_build_install(void);
#ifdef __cplusplus
}
#endif
int cmkr_build_install();
+25 -18
View File
@@ -46,28 +46,33 @@ enum TargetType {
target_custom,
};
template <typename T>
using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>;
struct Target {
std::string name;
TargetType type = {};
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
std::vector<std::string> compile_definitions;
std::vector<std::string> compile_features;
std::vector<std::string> compile_options;
std::vector<std::string> include_directories;
std::vector<std::string> link_directories;
std::vector<std::string> link_libraries;
std::vector<std::string> link_options;
std::vector<std::string> precompile_headers;
std::vector<std::string> sources;
ConditionVector compile_definitions;
ConditionVector compile_features;
ConditionVector compile_options;
ConditionVector include_directories;
ConditionVector link_directories;
ConditionVector link_libraries;
ConditionVector link_options;
ConditionVector precompile_headers;
ConditionVector sources;
std::string alias;
tsl::ordered_map<std::string, std::string> properties;
std::string cmake_before;
std::string cmake_after;
std::vector<std::string> include_before;
std::vector<std::string> include_after;
Condition<std::string> cmake_before;
Condition<std::string> cmake_after;
ConditionVector include_before;
ConditionVector include_after;
};
struct Test {
@@ -93,7 +98,7 @@ struct CMake {
std::string build_dir = "build";
std::string generator;
std::string config;
std::vector<std::string> subdirs;
Condition<std::vector<std::string>> subdirs;
std::vector<std::string> cppflags;
std::vector<std::string> cflags;
std::vector<std::string> linkflags;
@@ -103,10 +108,10 @@ struct CMake {
std::string project_version;
std::string project_description;
std::vector<std::string> project_languages;
std::string cmake_before;
std::string cmake_after;
std::vector<std::string> include_before;
std::vector<std::string> include_after;
Condition<std::string> cmake_before;
Condition<std::string> cmake_after;
ConditionVector include_before;
ConditionVector include_after;
std::vector<Setting> settings;
std::vector<Option> options;
std::vector<Package> packages;
@@ -115,6 +120,8 @@ struct CMake {
std::vector<Target> targets;
std::vector<Test> tests;
std::vector<Install> installs;
tsl::ordered_map<std::string, std::string> conditions;
CMake(const std::string &path, bool build);
};
-8
View File
@@ -1,6 +1,5 @@
#pragma once
#ifdef __cplusplus
namespace cmkr {
namespace error {
@@ -25,11 +24,4 @@ struct Status {
} // namespace error
} // namespace cmkr
extern "C" {
#endif
const char *cmkr_error_status_string(int);
#ifdef __cplusplus
}
#endif
-8
View File
@@ -1,6 +1,5 @@
#pragma once
#ifdef __cplusplus
namespace cmkr {
namespace gen {
@@ -11,13 +10,6 @@ int generate_cmake(const char *path, bool root = true);
} // namespace gen
} // namespace cmkr
extern "C" {
#endif
int cmkr_gen_generate_project(const char *typ);
int cmkr_gen_generate_cmake(const char *path);
#ifdef __cplusplus
}
#endif
-8
View File
@@ -1,6 +1,5 @@
#pragma once
#ifdef __cplusplus
namespace cmkr {
namespace help {
@@ -11,13 +10,6 @@ const char *message() noexcept;
} // namespace help
} // namespace cmkr
extern "C" {
#endif
const char *cmkr_help_version(void);
const char *cmkr_help_message(void);
#ifdef __cplusplus
}
#endif
+1 -2
View File
@@ -30,8 +30,7 @@ version = "0.1.0"
# [options]
[[target]]
name = "%s"
[target.%s]
type = "%s"
sources = ["src/*.cpp"]
include-directories = ["include"]
+4 -4
View File
@@ -1,7 +1,7 @@
#include "args.h"
#include "build.h"
#include "gen.h"
#include "help.h"
#include "args.hpp"
#include "build.hpp"
#include "gen.hpp"
#include "help.hpp"
#include "fs.hpp"
#include <exception>
+4 -4
View File
@@ -1,7 +1,7 @@
#include "build.h"
#include "build.hpp"
#include "cmake.hpp"
#include "error.h"
#include "gen.h"
#include "error.hpp"
#include "gen.hpp"
#include "fs.hpp"
#include <sstream>
@@ -16,7 +16,7 @@ namespace build {
int run(int argc, char **argv) {
cmake::CMake cmake(".", true);
if (argc > 2) {
for (size_t i = 2; i < argc; ++i) {
for (int i = 2; i < argc; ++i) {
cmake.build_args.push_back(argv[i]);
}
}
+49 -15
View File
@@ -33,6 +33,26 @@ static EnumType to_enum(const std::string &str, const std::string &help_name) {
return value;
}
template <typename T>
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination);
template <typename T>
static void get_optional(const TomlBasicValue &v, const toml::key &ky, Condition<T> &destination) {
// TODO: this algorithm in O(n) over the amount of keys, kinda bad
const auto &table = v.as_table();
for (const auto &itr : table) {
const auto &key = itr.first;
const auto &value = itr.second;
if (value.is_table()) {
if (value.contains(ky)) {
destination[key] = toml::find<T>(value, ky);
}
} else if (key == ky) {
destination[""] = toml::find<T>(v, ky);
}
}
}
template <typename T>
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination) {
if (v.contains(ky)) {
@@ -159,19 +179,6 @@ CMake::CMake(const std::string &path, bool build) {
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
get_optional(t, "sources", target.sources);
#define renamed(from, to) \
if (t.contains(from)) { \
throw std::runtime_error(from "has been renamed to " to); \
}
renamed("include-dirs", "include-directories");
renamed("link-libs", "link-libraries");
renamed("defines", "compile-definitions");
renamed("features", "compile-features");
#undef renamed
get_optional(t, "compile-definitions", target.compile_definitions);
get_optional(t, "compile-features", target.compile_features);
get_optional(t, "compile-options", target.compile_options);
@@ -186,8 +193,21 @@ CMake::CMake(const std::string &path, bool build) {
}
if (t.contains("properties")) {
using prop_map = tsl::ordered_map<std::string, std::string>;
target.properties = toml::find<prop_map>(t, "properties");
const auto &props = toml::find(t, "properties").as_table();
for (const auto &propKv : props) {
if (propKv.second.is_array()) {
std::string property_list;
for (const auto &list_val : propKv.second.as_array()) {
if (!property_list.empty()) {
property_list += ';';
}
property_list += list_val.as_string();
}
target.properties[propKv.first] = property_list;
} else {
target.properties[propKv.first] = propKv.second.as_string();
}
}
}
get_optional(t, "cmake-before", target.cmake_before);
@@ -237,6 +257,20 @@ CMake::CMake(const std::string &path, bool build) {
contents["pmm"]["SOURCE_SUBDIR"] = "pmm";
}
}
// Reasonable default conditions (you can override these if you desire)
conditions["windows"] = R"cmake(WIN32)cmake";
conditions["macos"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")cmake";
conditions["unix"] = R"cmake(UNIX)cmake";
conditions["bsd"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")cmake";
conditions["linux"] = conditions["lunix"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")cmake";
if (toml.contains("conditions")) {
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
for (const auto &cond : conds) {
conditions[cond.first] = cond.second;
}
}
}
}
} // namespace cmake
+1 -1
View File
@@ -1,4 +1,4 @@
#include "error.h"
#include "error.hpp"
#include <assert.h>
+191 -83
View File
@@ -1,7 +1,7 @@
#include "gen.h"
#include "gen.hpp"
#include "cmake.hpp"
#include "error.h"
#include "literals.h"
#include "error.hpp"
#include "literals.hpp"
#include "fs.hpp"
#include <cassert>
@@ -41,7 +41,6 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
std::vector<std::string> temp;
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
// TODO: delet this
auto fullpath = full.string();
auto base_len = base.string().length();
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
@@ -141,6 +140,13 @@ struct CommandEndl {
void endl() { ss << '\n'; }
};
struct RawArg {
RawArg() = default;
RawArg(std::string arg) : arg(std::move(arg)) {}
std::string arg;
};
// Credit: JustMagic
struct Command {
std::stringstream &ss;
@@ -160,7 +166,8 @@ struct Command {
std::string quote(const std::string &str) {
// Don't quote arguments that don't need quoting
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos) {
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos &&
str.find(';') == std::string::npos) {
return str;
}
std::string result;
@@ -246,8 +253,26 @@ struct Command {
return true;
}
bool print_arg(const RawArg &arg) {
if (arg.arg.empty()) {
return true;
}
if (had_newline) {
first_arg = false;
ss << '\n' << indent(depth + 1);
} else if (first_arg) {
first_arg = false;
} else {
ss << ' ';
}
ss << arg.arg;
return true;
}
template <class T>
bool print_arg(const T &value, bool indentation = true) {
bool print_arg(const T &value) {
std::stringstream tmp;
tmp << value;
auto str = tmp.str();
@@ -255,15 +280,13 @@ struct Command {
return true;
}
if (indentation) {
if (had_newline) {
first_arg = false;
ss << '\n' << indent(depth + 1);
} else if (first_arg) {
first_arg = false;
} else {
ss << ' ';
}
if (had_newline) {
first_arg = false;
ss << '\n' << indent(depth + 1);
} else if (first_arg) {
first_arg = false;
} else {
ss << ' ';
}
ss << quote(str);
@@ -276,21 +299,31 @@ struct Command {
ss << indent(depth) << command << '(';
(void)std::initializer_list<bool>{print_arg(values)...};
if (had_newline)
ss << '\n';
ss << '\n' << indent(depth);
ss << ")\n";
return CommandEndl(ss);
}
};
int generate_cmake(const char *path, bool root) {
if (!fs::exists(fs::path(path) / "cmake.toml")) {
throw std::runtime_error("No cmake.toml found!");
static std::string tolf(const std::string &str) {
std::string result;
for (char ch : str) {
if (ch != '\r') {
result += ch;
}
}
return result;
};
// Helper lambdas for more convenient CMake generation
struct Generator {
Generator(cmake::CMake &cmake) : cmake(cmake) {}
Generator(const Generator &) = delete;
cmake::CMake &cmake;
std::stringstream ss;
int indent = 0;
auto cmd = [&ss, &indent](const std::string &command) {
Command cmd(const std::string &command) {
if (command.empty())
throw std::invalid_argument("command cannot be empty");
if (command == "if") {
@@ -302,43 +335,93 @@ int generate_cmake(const char *path, bool root) {
indent--;
}
return Command(ss, indent, command);
};
auto comment = [&ss, &indent](const std::string &comment) {
}
CommandEndl comment(const std::string &comment) {
ss << Command::indent(indent) << "# " << comment << '\n';
return CommandEndl(ss);
};
auto endl = [&ss]() { ss << '\n'; };
auto tolf = [](const std::string &str) {
std::string result;
for (char ch : str) {
if (ch != '\r') {
result += ch;
}
}
return result;
};
auto inject_includes = [&cmd, &endl](const std::vector<std::string> &includes) {
}
void endl() { ss << '\n'; }
void inject_includes(const std::vector<std::string> &includes) {
if (!includes.empty()) {
for (const auto &file : includes) {
// TODO: warn/error if file doesn't exist?
if (!fs::is_regular_file(file)) {
throw std::runtime_error("Include '" + file + "' does not exist");
}
cmd("include")(file);
}
endl();
}
};
auto inject_cmake = [&ss, &tolf](const std::string &cmake) {
}
void inject_cmake(const std::string &cmake) {
if (!cmake.empty()) {
if (cmake.back() == '\"') {
throw std::runtime_error("Detected additional \" at the end of cmake block");
}
ss << tolf(cmake) << "\n\n";
auto cmake_lf = tolf(cmake);
while (cmake_lf.back() == '\n')
cmake_lf.pop_back();
bool did_indent = false;
for (char ch : cmake_lf) {
if (!did_indent) {
ss << Command::indent(indent);
did_indent = true;
} else if (ch == '\n') {
did_indent = false;
}
ss << ch;
}
ss << '\n';
}
};
}
// TODO: add link with proper documentation
comment("This file was generated automatically by cmkr.").endl();
template <typename T, typename Lambda>
void handle_condition(const cmake::Condition<T> &value, const Lambda &fn) {
if (!value.empty()) {
for (const auto &itr : value) {
const auto &condition = itr.first;
if (!condition.empty()) {
if (cmake.conditions.count(condition) == 0) {
// TODO: somehow print line number information here?
throw std::runtime_error("Unknown condition '" + condition + "'");
}
cmd("if")(RawArg(cmake.conditions[condition]));
}
if (!itr.second.empty()) {
fn(condition, itr.second);
}
if (!condition.empty()) {
cmd("endif")();
}
endl();
}
}
}
};
int generate_cmake(const char *path, bool root) {
if (!fs::exists(fs::path(path) / "cmake.toml")) {
throw std::runtime_error("No cmake.toml found!");
}
cmake::CMake cmake(path, false);
Generator gen(cmake);
// Helper lambdas for more convenient CMake generation
auto &ss = gen.ss;
auto cmd = [&gen](const std::string &comment) { return gen.cmd(comment); };
auto comment = [&gen](const std::string &comment) { return gen.comment(comment); };
auto endl = [&gen]() { gen.endl(); };
auto inject_includes = [&gen](const std::vector<std::string> &includes) { gen.inject_includes(includes); };
auto inject_cmake = [&gen](const std::string &cmake) { gen.inject_cmake(cmake); };
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
comment("See https://github.com/build-cpp/cmkr for more information");
endl();
if (root) {
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
@@ -395,8 +478,8 @@ int generate_cmake(const char *path, bool root) {
ss << "\")\n\n";
}
inject_includes(cmake.include_before);
inject_cmake(cmake.cmake_before);
gen.handle_condition(cmake.include_before, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(cmake.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
if (!cmake.project_name.empty()) {
auto languages = std::make_pair("LANGUAGES", cmake.project_languages);
@@ -405,8 +488,8 @@ int generate_cmake(const char *path, bool root) {
cmd("project")(cmake.project_name, languages, version, description).endl();
}
inject_includes(cmake.include_after);
inject_cmake(cmake.cmake_after);
gen.handle_condition(cmake.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(cmake.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
if (!cmake.contents.empty()) {
cmd("include")("FetchContent").endl();
@@ -442,7 +525,7 @@ int generate_cmake(const char *path, bool root) {
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["REVISION"] = {cmake.vcpkg.version};
vcpkg_args["REQUIRES"] = cmake.vcpkg.packages;
auto vcpkg = std::make_pair("VCPKG", vcpkg_args);
cmd("pmm")(vcpkg).endl();
@@ -492,41 +575,55 @@ int generate_cmake(const char *path, bool root) {
// generate_cmake is called on the subdirectories recursively later
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);
cmd("else")();
cmd("set")("CMAKE_FOLDER", dir);
cmd("endif")();
// clang-format on
cmd("add_subdirectory")(dir);
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
}
gen.handle_condition(cmake.subdirs, [&](const std::string &, const std::vector<std::string> &subdirs) {
for (const auto &dir : subdirs) {
// clang-format off
comment(dir);
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
cmd("if")("CMAKE_FOLDER");
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
cmd("else")();
cmd("set")("CMAKE_FOLDER", dir);
cmd("endif")();
// clang-format on
cmd("add_subdirectory")(dir);
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
}
});
endl();
}
if (!cmake.targets.empty()) {
for (const auto &target : cmake.targets) {
comment("Target " + target.name);
inject_includes(target.include_before);
inject_cmake(target.cmake_before);
cmd("set")("CMKR_TARGET", target.name);
if (!target.sources.empty()) {
auto sources = expand_cmake_paths(target.sources, path);
gen.handle_condition(target.include_before,
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(target.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
auto sources_var = target.name + "_SOURCES";
bool added_toml = false;
cmd("set")(sources_var, RawArg("\"\"")).endl();
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
auto sources = expand_cmake_paths(condition_sources, path);
if (sources.empty()) {
throw std::runtime_error(target.name + " sources wildcard found 0 files");
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
}
if (target.type != cmake::target_interface) {
// Do not add cmake.toml twice
if (std::find(sources.begin(), sources.end(), "cmake.toml") == sources.end()) {
sources.push_back("cmake.toml");
}
// Do not add cmake.toml twice
if (!added_toml && std::find(sources.begin(), sources.end(), "cmake.toml") != sources.end()) {
added_toml = true;
}
cmd("set")(target.name + "_SOURCES", sources).endl();
cmd("list")("APPEND", sources_var, sources);
});
if (!added_toml && target.type != cmake::target_interface) {
cmd("list")("APPEND", sources_var, std::vector<std::string>{"cmake.toml"}).endl();
}
cmd("set")("CMKR_SOURCES", "${" + sources_var + "}");
std::string add_command;
std::string target_type;
@@ -567,7 +664,13 @@ int generate_cmake(const char *path, bool root) {
assert("Unimplemented enum value" && false);
}
cmd(add_command)(target.name, target_type, "${" + target.name + "_SOURCES}").endl();
cmd(add_command)(target.name, target_type).endl();
// clang-format off
cmd("if")(sources_var);
cmd("target_sources")(target.name, target.type == cmake::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
cmd("endif")().endl();
// clang-format on
// The first executable target will become the Visual Studio startup project
if (target.type == cmake::target_executable) {
@@ -587,10 +690,9 @@ int generate_cmake(const char *path, bool root) {
cmd("add_library")(target.alias, "ALIAS", target.name);
}
auto target_cmd = [&](const char *command, const std::vector<std::string> &args) {
if (!args.empty()) {
cmd(command)(target.name, target_scope, args).endl();
}
auto target_cmd = [&](const char *command, const cmake::ConditionVector &cargs) {
gen.handle_condition(
cargs, [&](const std::string &, const std::vector<std::string> &args) { cmd(command)(target.name, target_scope, args); });
};
target_cmd("target_compile_definitions", target.compile_definitions);
@@ -605,8 +707,12 @@ int generate_cmake(const char *path, bool root) {
cmd("set_target_properties")(target.name, "PROPERTIES", target.properties).endl();
}
inject_includes(target.include_after);
inject_cmake(target.cmake_after);
gen.handle_condition(target.include_after,
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(target.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
cmd("unset")("CMKR_TARGET");
cmd("unset")("CMKR_SOURCES");
}
}
@@ -666,10 +772,12 @@ int generate_cmake(const char *path, bool root) {
}
}
for (const auto &sub : cmake.subdirs) {
auto subpath = fs::path(path) / fs::path(sub);
if (fs::exists(subpath / "cmake.toml"))
generate_cmake(subpath.string().c_str(), false);
for (const auto &itr : cmake.subdirs) {
for (const auto &sub : itr.second) {
auto subpath = fs::path(path) / fs::path(sub);
if (fs::exists(subpath / "cmake.toml"))
generate_cmake(subpath.string().c_str(), false);
}
}
return 0;
+1 -1
View File
@@ -1,4 +1,4 @@
#include "help.h"
#include "help.hpp"
namespace cmkr {
namespace help {
+1 -1
View File
@@ -1,4 +1,4 @@
#include "args.h"
#include "args.hpp"
#include <cstdio>
#include <cstdlib>
+2
View File
@@ -0,0 +1,2 @@
# These will be generated by cmkr, so no point in tracking them
**/CMakeLists.txt
+22 -1
View File
@@ -1,4 +1,5 @@
# This file was generated automatically by cmkr.
# This file is automatically generated from cmake.toml - DO NOT EDIT
# See https://github.com/build-cpp/cmkr for more information
# Create a configure-time dependency on cmake.toml to improve IDE support
if(CMKR_ROOT_PROJECT)
@@ -17,3 +18,23 @@ add_test(
build
)
add_test(
NAME
conditions
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/conditions"
COMMAND
$<TARGET_FILE:cmkr>
build
)
add_test(
NAME
interface
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/interface"
COMMAND
$<TARGET_FILE:cmkr>
build
)
-41
View File
@@ -1,41 +0,0 @@
# 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})
+12
View File
@@ -2,4 +2,16 @@
name = "basic"
command = "$<TARGET_FILE:cmkr>"
working-directory = "${CMAKE_CURRENT_LIST_DIR}/basic"
arguments = ["build"]
[[test]]
name = "conditions"
command = "$<TARGET_FILE:cmkr>"
working-directory = "${CMAKE_CURRENT_LIST_DIR}/conditions"
arguments = ["build"]
[[test]]
name = "interface"
command = "$<TARGET_FILE:cmkr>"
working-directory = "${CMAKE_CURRENT_LIST_DIR}/interface"
arguments = ["build"]
+17
View File
@@ -0,0 +1,17 @@
[project]
name = "conditions"
cmake-after = "set(CUSTOM ON)"
[conditions]
custom = "CUSTOM"
[target.example]
type = "executable"
sources = ["src/main.cpp"]
windows.sources = ["src/windows_specific.cpp"]
cmake-after = "message(STATUS cmake-after)"
windows.cmake-after = "message(STATUS win32-after)"
macos.cmake-after = "message(STATUS macos-after)"
linux.cmake-after = "message(STATUS linux-after)"
unix.cmake-after = "message(STATUS unix-after)"
custom.cmake-after = "message(STATUS custom-after)"
+1
View File
@@ -0,0 +1 @@
int main() { }
+3
View File
@@ -0,0 +1,3 @@
#include <Windows.h>
void foo() { }
+11
View File
@@ -0,0 +1,11 @@
[project]
name = "interface"
[target.mylib]
type = "interface"
include-directories = ["include"]
[target.example]
type = "executable"
sources = ["src/main.cpp"]
link-libraries = ["mylib"]
+4
View File
@@ -0,0 +1,4 @@
namespace mylib
{
static const char* version() { return "v1.0"; }
} // namespace mylib
+8
View File
@@ -0,0 +1,8 @@
#include <cstdio>
#include "mylib/mylib.hpp"
int main()
{
printf("mylib version: %s\n", mylib::version())
}