Compare commits

..

27 Commits

Author SHA1 Message Date
Duncan Ogilvie 61851d2bee Bump to 0.2.5 2022-01-07 19:45:08 +01:00
Duncan Ogilvie c0d665095e Add script to automatically bump the version
Closes #44
2022-01-07 19:44:33 +01:00
Duncan Ogilvie 6c1e388391 Add root, x64 and x32 conditions 2021-12-30 14:27:07 +01:00
Duncan Ogilvie 04905371b8 Bump to 0.2.4 2021-12-30 02:45:02 +01:00
Duncan Ogilvie 5a6ac44fcf Merge pull request #46 from build-cpp/expanded-conditions
Support conditions everywhere
2021-12-30 02:44:02 +01:00
Duncan Ogilvie 9db82dfd05 Update toml reference 2021-12-30 02:43:17 +01:00
Duncan Ogilvie 459ebb14b5 Support conditions everywhere
Closes #36
2021-12-30 02:35:59 +01:00
Duncan Ogilvie 490869b9a2 Bump to 0.2.3 2021-12-28 12:08:16 +01:00
Duncan Ogilvie 8ae2754994 Merge pull request #45 from cursey/bootstrap-vcpkg-early
Bootstrap Vcpkg prior to FetchContent
2021-12-28 07:01:05 +01:00
cursey 18419297f4 Bootstrap Vcpkg prior to FetchContent 2021-12-27 16:49:11 -08:00
Duncan Ogilvie bdaf5c94e6 Bump to 0.2.2 2021-12-27 11:49:22 +01:00
Duncan Ogilvie fa3b7a346c Emit options and settings before the project() 2021-12-27 11:45:23 +01:00
Duncan Ogilvie df1dbf7953 Bump to 0.2.1 2021-12-27 11:17:20 +01:00
Duncan Ogilvie 5951f6930e Update documentation for vcpkg features 2021-12-27 11:12:28 +01:00
Duncan Ogilvie a6d5f1c99b Merge pull request #41 from cursey/vcpkg-package-features
Add support for vcpkg package features
2021-12-27 11:01:04 +01:00
Duncan Ogilvie dc852b9a2f Run clang-format 2021-12-27 10:54:46 +01:00
cursey 66621a9818 Fix spacing 2021-12-27 01:50:25 -08:00
cursey 2cd8ad0f18 Add missing const 2021-12-27 01:13:16 -08:00
cursey 084cbd6159 Cleanup vcpkg.json generation a little 2021-12-27 01:08:56 -08:00
cursey b4b0f533a9 Validate vcpkg features 2021-12-27 01:02:05 -08:00
cursey 0acc9b4e5a Report the badly formed package name to the user 2021-12-27 00:57:48 -08:00
cursey 61dfb0eb60 Fix tabbing in vcpkg.json for packages that have features 2021-12-26 16:32:38 -08:00
cursey 3201fd052e Add support for vcpkg package features 2021-12-26 15:40:45 -08:00
Duncan Ogilvie 83087ff06e Release v0.2.0 (finally) 2021-12-23 20:13:12 +01:00
Duncan Ogilvie 88e9241e9f Improve onboarding experience
Thanks Matthaus :give:
2021-12-23 18:04:54 +01:00
Duncan Ogilvie eff967277d Update README with a migration guide 2021-12-23 13:32:46 +01:00
Duncan Ogilvie ea01497246 Bump CMKR_TAG 2021-12-23 04:01:18 +01:00
19 changed files with 395 additions and 168 deletions
+1
View File
@@ -0,0 +1 @@
github: [mrexodia]
+1 -1
View File
@@ -50,7 +50,7 @@ jobs:
string: ${{ runner.os }}
- name: Compress artifacts
uses: papeloto/action-zip@v1
uses: vimtor/action-zip@v1
with:
files: install/bin/
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
+1 -1
View File
@@ -25,7 +25,7 @@ project(cmkr
LANGUAGES
CXX
VERSION
0.1.4
0.2.5
DESCRIPTION
"CMakeLists generator from TOML"
)
+24 -5
View File
@@ -7,29 +7,48 @@
```toml
[project]
name = "cmkr_for_beginners"
description = "A minimal cmkr project."
[target.hello_world]
type = "executable"
sources = ["src/main.cpp"]
```
`cmkr` can bootstrap itself from CMake and consumers of your project do not need to install anything to work with it.
`cmkr` can bootstrap itself from CMake and you only need CMake to use it.
## Getting started
The easiest way to get started is to use the [cmkr_for_beginners](https://github.com/build-cpp/cmkr_for_beginners) template repository. Either open it in [Gitpod](https://gitpod.io/#https://github.com/build-cpp/cmkr_for_beginners), or clone the repository and run:
To get started run the following commands from your project directory:
```sh
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
cmake -P cmkr.cmake
```
After the bootstrapping process finishes, modify [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) and open the project in your favorite IDE or build with CMake:
```sh
cmake -B build
cmake --build build
```
Alternatively you can check out the [cmkr topic](https://github.com/topics/cmkr), the [build-cpp organization](https://github.com/build-cpp) or the [tests](https://github.com/build-cpp/cmkr/tree/main/tests) for more examples and templates. You can also check out the [documentation](https://build-cpp.github.io/cmkr).
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt`.
In CI settings the `cmkr` bootstrapping process is skipped so there is no extra configure-time overhead in your pipelines.
## Template repositories
Another way to get started is to use the [cmkr_for_beginners](https://github.com/build-cpp/cmkr_for_beginners) template repository. Either open it in [Gitpod](https://gitpod.io/#https://github.com/build-cpp/cmkr_for_beginners), or clone the repository and run:
```sh
cmake -B build
cmake --build build
```
Check out the [cmkr topic](https://github.com/topics/cmkr), the [build-cpp organization](https://github.com/build-cpp) or the [tests](https://github.com/build-cpp/cmkr/tree/main/tests) for more examples and templates.
## Command line
Optionally you can install `cmkr` in your `PATH` and use it as a utility from the command line:
Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/releases) in your `PATH` and use it as a utility from the command line:
```
Usage: cmkr [arguments]
+1 -1
View File
@@ -4,7 +4,7 @@ cmkr-include = false
[project]
name = "cmkr"
version = "0.1.4"
version = "0.2.5"
description = "CMakeLists generator from TOML"
languages = ["CXX"]
subdirs = ["third_party", "tests"]
+70
View File
@@ -0,0 +1,70 @@
cmake_minimum_required(VERSION 3.20)
if(NOT CMAKE_SCRIPT_MODE_FILE)
message(FATAL_ERROR "Usage: cmake -P bump_version.cmake [1.2.3]")
endif()
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
message(FATAL_ERROR "Cannot find cmake.toml")
endif()
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake")
message(FATAL_ERROR "Cannot find cmkr.cmake")
endif()
file(READ "${CMAKE_SOURCE_DIR}/cmake.toml" CMAKE_TOML)
string(FIND "${CMAKE_TOML}" "[project]" PROJECT_INDEX)
string(SUBSTRING "${CMAKE_TOML}" ${PROJECT_INDEX} -1 CMAKE_TOML_PROJECT)
set(SEMVER_REGEX "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
set(VERSION_REGEX "version = \"${SEMVER_REGEX}\"")
if(CMAKE_TOML_PROJECT MATCHES "${VERSION_REGEX}")
set(MAJOR "${CMAKE_MATCH_1}")
set(MINOR "${CMAKE_MATCH_2}")
set(PATCH "${CMAKE_MATCH_3}")
set(OLDVERSION "${MAJOR}.${MINOR}.${PATCH}")
else()
message(FATAL_ERROR "Failed to match semantic version in cmake.toml")
endif()
if(CMAKE_ARGV3)
if(NOT CMAKE_ARGV3 MATCHES "${SEMVER_REGEX}")
message(FATAL_ERROR "Invalid semantic version number '${CMAKE_ARGV3}'")
endif()
set(NEWVERSION "${CMAKE_ARGV3}")
else()
math(EXPR NEWPATCH "${PATCH} + 1")
set(NEWVERSION "${MAJOR}.${MINOR}.${NEWPATCH}")
endif()
message(STATUS "Version ${OLDVERSION} -> ${NEWVERSION}")
find_program(CMKR_EXECUTABLE "cmkr" PATHS "${CMAKE_SOURCE_DIR}/build" NO_CACHE REQUIRED)
message(STATUS "Found cmkr: ${CMKR_EXECUTABLE}")
# Replace version in cmake.toml
string(REPLACE "version = \"${OLDVERSION}\"" "version = \"${NEWVERSION}\"" CMAKE_TOML "${CMAKE_TOML}")
file(CONFIGURE
OUTPUT "${CMAKE_SOURCE_DIR}/cmake.toml"
CONTENT "${CMAKE_TOML}"
@ONLY
NEWLINE_STYLE LF
)
# Run cmkr gen
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen RESULT_VARIABLE CMKR_EXEC_RESULT)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "cmkr gen failed (exit code ${CMKR_EXEC_RESULT})")
endif()
# Replace version in cmkr.cmake
file(READ "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake" CMKR_CMAKE)
string(REGEX REPLACE "CMKR_TAG \"[^\"]+\"" "CMKR_TAG \"v${NEWVERSION}\"" CMKR_CMAKE "${CMKR_CMAKE}")
file(CONFIGURE
OUTPUT "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake"
CONTENT "${CMKR_CMAKE}"
@ONLY
NEWLINE_STYLE LF
)
# Print git commands
message(STATUS "Git commands to create new version:\ngit commit -a -m \"Bump to ${NEWVERSION}\"\ngit tag v${NEWVERSION}\ngit push origin main v${NEWVERSION}")
+32 -6
View File
@@ -1,12 +1,15 @@
include_guard()
if(CMAKE_SCRIPT_MODE_FILE)
message(FATAL_ERROR "Running cmkr.cmake as a script is unsupported. To build your project try: cmake -B build")
endif()
# Change these defaults to point to your infrastructure if desired
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
set(CMKR_TAG "archive_52671dcf" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_TAG "v0.2.5" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
if(CMAKE_SCRIPT_MODE_FILE)
set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build")
set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
# Set these from the command line to customize for development/debugging purposes
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
@@ -23,7 +26,7 @@ if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION)
endif()
# Disable cmkr if no cmake.toml file is found
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
macro(cmkr)
endmacro()
@@ -143,6 +146,29 @@ if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding")
endif()
# Use cmkr.cmake as a script
if(CMAKE_SCRIPT_MODE_FILE)
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
execute_process(COMMAND "${CMKR_EXECUTABLE}" init
RESULT_VARIABLE CMKR_EXEC_RESULT
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "[cmkr] Failed to bootstrap cmkr project. Please report an issue: https://github.com/build-cpp/cmkr/issues/new")
else()
message(STATUS "[cmkr] Modify cmake.toml and then configure using: cmake -B build")
endif()
else()
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen
RESULT_VARIABLE CMKR_EXEC_RESULT
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "[cmkr] Failed to generate project.")
else()
message(STATUS "[cmkr] Configure using: cmake -B build")
endif()
endif()
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
+2 -2
View File
@@ -46,14 +46,14 @@ function(generate_documentation)
string(REGEX MATCH "^(\n*(#[^\n]+\n)+\n*)" EXAMPLE_HEADER "${test_contents}")
string(LENGTH "${EXAMPLE_HEADER}" header_length)
string(STRIP "${EXAMPLE_HEADER}" EXAMPLE_HEADER)
string(REGEX REPLACE "\n# ?" "\n" EXAMPLE_HEADER "\n${EXAMPLE_HEADER}")
string(REGEX REPLACE "\n# ?" "\n\n" EXAMPLE_HEADER "\n${EXAMPLE_HEADER}")
string(STRIP "${EXAMPLE_HEADER}" EXAMPLE_HEADER)
# Extract footer text
string(REGEX MATCH "(((#[^\n]+)(\n+|$))+)$" EXAMPLE_FOOTER "${test_contents}")
string(LENGTH "${EXAMPLE_FOOTER}" footer_length)
string(STRIP "${EXAMPLE_FOOTER}" EXAMPLE_FOOTER)
string(REGEX REPLACE "\n# ?" "\n" EXAMPLE_FOOTER "\n${EXAMPLE_FOOTER}")
string(REGEX REPLACE "\n# ?" "\n\n" EXAMPLE_FOOTER "\n${EXAMPLE_FOOTER}")
string(STRIP "${EXAMPLE_FOOTER}" EXAMPLE_FOOTER)
# Extract toml body
+1
View File
@@ -1,2 +1,3 @@
_site/
.jekyll-metadata
assets/
+30 -19
View File
@@ -1,15 +1,15 @@
---
layout: page
title: cmake.toml
title: Reference
permalink: /cmake-toml/
nav_order: 3
nav_order: 1
---
# cmake.toml
# Reference
This page is supposed to be a reference for the options in the `cmake.toml` file. If you think anything is missing or unclear, please [edit this page](https://github.com/build-cpp/cmkr/edit/main/docs/cmake-toml.md) or open an [issue](https://github.com/build-cpp/cmkr/issues).
This page is a reference for the options in the `cmake.toml` file. If you think anything is missing or unclear, please [edit this page](https://github.com/build-cpp/cmkr/edit/main/docs/cmake-toml.md) or open an [issue](https://github.com/build-cpp/cmkr/issues).
See the [examples](/examples) section for more real-world examples.
See the [examples](/examples) section for concrete examples.
## CMake configuration
@@ -47,6 +47,8 @@ arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8"
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4"
```
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
You can also prefix most keys with `condition.` to represent a conditional:
```toml
@@ -56,7 +58,7 @@ sources = ["src/main.cpp"]
windows.sources = ["src/windows_specific.cpp"]
```
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions. The following conditions are predefined (you can override them if you desire):
The following conditions are predefined (you can override them if you desire):
```toml
[conditions]
@@ -68,13 +70,16 @@ linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\""
gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\""
clang = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
msvc = "MSVC"
root = "CMKR_ROOT_PROJECT"
x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
```
## Subdirectories
```toml
[subdir.mysubdir]
condition = "linux"
condition = "mycondition"
cmake-before = """
message(STATUS "CMake injected before the add_subdirectory() call"
"""
@@ -96,14 +101,13 @@ packages = ["fmt", "zlib"]
The vcpkg `version` will automatically generate the `url` from the [official repository](https://github.com/microsoft/vcpkg/releases). For a custom registry you can specify your own `url` (and omit the `version`). You can browse available packages on [vcpkg.io](https://vcpkg.io/en/packages.html).
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
## Packages
```toml
[find-package]
mypackage = { version = "1.0", required = true, config = true, components = ["mycomponent"] }
# Alternative syntax
[find-package.mypackage]
condition = "mycondition"
version = "1.0"
required = true
config = true
@@ -115,24 +119,29 @@ components = ["mycomponent"]
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
```toml
[fetch-content]
gitcontent = { git = "https://github.com/myuser/gitcontent", tag = "v0.1" }
svncontent = { svn = "https://svn-host.com/url", rev = "svn_rev" }
urlcontent = { url = "https://content-host.com/urlcontent.zip", hash = "123123123123" }
# Alternative syntax
[fetch-content.gitcontent]
condition = "mycondition"
git = "https://github.com/myuser/gitcontent"
tag = "v0.1"
[fetch-content.svncontent]
condition = "mycondition"
svn = "https://svn-host.com/url"
rev = "svn_rev"
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
hash = "123123123123"
```
## Targets
```toml
[target.mytarget]
condition = "linux"
condition = "mycondition"
alias = "mytarget::mytarget"
type = "static" # executable, library, shared, static, interface, custom
type = "static" # executable, shared (DLL), static, interface, object, library, custom
headers = ["src/mytarget.h"]
sources = ["src/mytarget.cpp"]
@@ -178,6 +187,7 @@ FOLDER = "MyFolder"
```toml
# You can declare as many as you want like this, but the name has to be unique
[[test]]
condition = "mycondition"
name = "mytest"
command = "$<TARGET_FILE:mytest>"
arguments = ["arg1", "arg2"]
@@ -187,6 +197,7 @@ working-directory = "mytest-dir"
```toml
[[install]]
condition = "mycondition"
targets = ["mytarget", "mytest"]
destination = ["bin"]
files = ["content/my.png"]
-22
View File
@@ -1,22 +0,0 @@
---
layout: page
title: Command line
permalink: /command-line/
nav_order: 2
---
# Command line
Optionally you can install `cmkr` in your `PATH` and use it as a utility from the command line:
```
Usage: cmkr [arguments]
arguments:
init [executable|library|shared|static|interface] Starts a new project in the same directory.
gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build.
install Run cmake --install. Needs admin privileges.
clean Clean the build directory.
help Show help.
version Current cmkr version.
```
+2
View File
@@ -33,4 +33,6 @@ link-libraries = ["fmt::fmt"]
The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`.
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
<sup><sub>This page was automatically generated from [tests/vcpkg/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/vcpkg/cmake.toml).</sub></sup>
-17
View File
@@ -1,17 +0,0 @@
---
layout: page
title: Getting started
permalink: /getting-started/
nav_order: 1
---
# Getting started
The easiest way to get started is to use the [cmkr_for_beginners](https://github.com/build-cpp/cmkr_for_beginners) template repository. Either open it in [Gitpod](https://gitpod.io/#https://github.com/build-cpp/cmkr_for_beginners), or clone the repository and run:
```sh
cmake -B build
cmake --build build
```
Alternatively you can check out the [cmkr topic](https://github.com/topics/cmkr) or the [build-cpp organization](https://github.com/build-cpp) for more examples and templates.
+57 -3
View File
@@ -6,18 +6,72 @@ nav_order: 0
# Index
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io). It was originally created by [Mohammed Alyousef](https://github.com/MoAlyousef).
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io).
`cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
```toml
[project]
name = "cmkr_for_beginners"
description = "A minimal cmkr project."
[target.hello_world]
type = "executable"
sources = ["src/main.cpp"]
```
`cmkr` can bootstrap itself from CMake and consumers of your project do not need to install anything to work with it.
`cmkr` can bootstrap itself from CMake and you only need CMake to use it.
## Getting started
To get started run the following commands from your project directory:
```sh
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
cmake -P cmkr.cmake
```
After the bootstrapping process finishes, modify [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) and open the project in your favorite IDE or build with CMake:
```sh
cmake -B build
cmake --build build
```
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt`.
In CI settings the `cmkr` bootstrapping process is skipped so there is no extra configure-time overhead in your pipelines.
## Template repositories
Another way to get started is to use the [cmkr_for_beginners](https://github.com/build-cpp/cmkr_for_beginners) template repository. Either open it in [Gitpod](https://gitpod.io/#https://github.com/build-cpp/cmkr_for_beginners), or clone the repository and run:
```sh
cmake -B build
cmake --build build
```
Check out the [cmkr topic](https://github.com/topics/cmkr), the [build-cpp organization](https://github.com/build-cpp) or the [tests](https://github.com/build-cpp/cmkr/tree/main/tests) for more examples and templates.
## Command line
Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/releases) in your `PATH` and use it as a utility from the command line:
```
Usage: cmkr [arguments]
arguments:
init [executable|library|shared|static|interface] Starts a new project in the same directory.
gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build.
install Run cmake --install. Needs admin privileges.
clean Clean the build directory.
help Show help.
version Current cmkr version.
```
## Credits
- https://github.com/gulrak/filesystem
- https://github.com/Tessil/ordered-map
- https://github.com/ToruNiina/toml11
- https://github.com/mpark/variant
- https://www.svgrepo.com/svg/192268/hammer
+3 -3
View File
@@ -91,10 +91,10 @@ static const char *toml_migration = &R"lit(
name = "@name"
# TODO: define a target for each of your executables/libraries like this:
#[target.@name]
#type = "executable"
#[target.myexecutable]
#type = "executable" # static, shared
#sources = ["src/@name/*.cpp", "include/@name/*.hpp"]
#include-directories = ["include"]
#compile-features = ["cxx_std_11"]
#link-libraries = ["other-targets"]
#link-libraries = ["target-or-library"]
)lit"[1]; // skip initial newline
+16 -6
View File
@@ -8,6 +8,11 @@
namespace cmkr {
namespace parser {
template <typename T>
using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>;
struct Setting {
std::string name;
std::string comment;
@@ -24,6 +29,7 @@ struct Option {
struct Package {
std::string name;
std::string condition;
std::string version;
bool required = true;
bool config = false;
@@ -33,7 +39,13 @@ struct Package {
struct Vcpkg {
std::string version;
std::string url;
std::vector<std::string> packages;
struct Package {
std::string name;
std::vector<std::string> features;
};
std::vector<Package> packages;
};
enum TargetType {
@@ -46,11 +58,6 @@ enum TargetType {
target_object,
};
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 = {};
@@ -95,6 +102,7 @@ struct Target {
struct Test {
std::string name;
std::string condition;
std::vector<std::string> configurations;
std::string working_directory;
std::string command;
@@ -102,6 +110,7 @@ struct Test {
};
struct Install {
std::string condition;
std::vector<std::string> targets;
std::vector<std::string> files;
std::vector<std::string> dirs;
@@ -120,6 +129,7 @@ struct Subdir {
struct Content {
std::string name;
std::string condition;
tsl::ordered_map<std::string, std::string> arguments;
};
+116 -80
View File
@@ -105,8 +105,11 @@ void generate_project(const std::string &type) {
};
if (!fs::is_empty(fs::current_path())) {
// Make a backup of an existing CMakeLists.txt if it exists
std::error_code ec;
fs::rename("CMakeLists.txt", "CMakeLists.txt.bak", ec);
// Create an empty cmake.toml for migration purporses
create_file("cmake.toml", format(toml_migration, variables));
puts("Generated migration cmake.toml in existing project directory...");
return;
}
@@ -313,10 +316,10 @@ static std::string tolf(const std::string &str) {
};
struct Generator {
Generator(parser::Project &project) : project(project) {}
Generator(const parser::Project &project) : project(project) {}
Generator(const Generator &) = delete;
parser::Project &project;
const parser::Project &project;
std::stringstream ss;
int indent = 0;
@@ -380,11 +383,7 @@ struct Generator {
for (const auto &itr : value) {
const auto &condition = itr.first;
if (!condition.empty()) {
if (project.conditions.count(condition) == 0) {
// TODO: somehow print line number information here?
throw std::runtime_error("Unknown condition '" + condition + "'");
}
cmd("if", condition)(RawArg(project.conditions[condition]));
cmd("if", condition)(RawArg(project.conditions.at(condition)));
}
if (!itr.second.empty()) {
@@ -400,6 +399,27 @@ struct Generator {
}
};
struct ConditionScope {
Generator &gen;
bool endif = false;
ConditionScope(Generator &gen, const std::string &condition) : gen(gen) {
if (!condition.empty()) {
gen.cmd("if", condition)(RawArg(gen.project.conditions.at(condition)));
endif = true;
}
}
ConditionScope(const ConditionScope &) = delete;
ConditionScope(ConditionScope &&) = delete;
~ConditionScope() {
if (endif) {
gen.cmd("endif")();
}
}
};
static bool vcpkg_valid_identifier(const std::string &name) {
// prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default
auto is_reserved = [](const std::string &s) {
@@ -538,6 +558,35 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
ss << "\")\n\n";
}
if (!project.options.empty()) {
comment("Options");
for (const auto &opt : project.options) {
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
}
endl();
}
if (!project.settings.empty()) {
comment("Settings");
for (const auto &set : project.settings) {
std::string set_val;
if (set.val.index() == 1) {
set_val = mpark::get<1>(set.val);
} else {
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
}
if (set.cache) {
auto typ = set.val.index() == 1 ? "STRING" : "BOOL";
auto force = set.force ? "FORCE" : "";
cmd("set")(set.name, set_val, typ, set.comment, force);
} else {
cmd("set")(set.name, set_val);
}
}
endl();
}
gen.handle_condition(project.include_before, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(project.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
@@ -551,19 +600,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.handle_condition(project.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(project.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
if (!project.contents.empty()) {
cmd("include")("FetchContent").endl();
for (const auto &content : project.contents) {
cmd("message")("STATUS", "Fetching " + content.name + "...");
ss << "FetchContent_Declare(\n\t" << content.name << "\n";
for (const auto &arg : content.arguments) {
ss << "\t" << arg.first << "\n\t\t" << arg.second << "\n";
}
ss << ")\n";
cmd("FetchContent_MakeAvailable")(content.name).endl();
}
}
if (!project.vcpkg.packages.empty()) {
// Allow the user to specify a url or derive it from the version
auto url = project.vcpkg.url;
@@ -576,8 +612,8 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
// Show a nicer error than vcpkg when specifying an invalid package name
for (const auto &package : project.vcpkg.packages) {
if (!vcpkg_valid_identifier(package)) {
throw std::runtime_error("Invalid [vcpkg].packages name '" + package + "' (needs to be lowercase alphanumeric)");
if (!vcpkg_valid_identifier(package.name)) {
throw std::runtime_error("Invalid [vcpkg].packages name '" + package.name + "' (needs to be lowercase alphanumeric)");
}
}
@@ -608,10 +644,31 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
const auto &packages = project.vcpkg.packages;
for (size_t i = 0; i < packages.size(); i++) {
const auto &package = packages[i];
if (!vcpkg_valid_identifier(package)) {
throw std::runtime_error("Invalid vcpkg package name '" + package + "'");
const auto &features = package.features;
if (!vcpkg_valid_identifier(package.name)) {
throw std::runtime_error("Invalid vcpkg package name '" + package.name + "'");
}
for (const auto &feature : features) {
if (!vcpkg_valid_identifier(feature)) {
throw std::runtime_error("Invalid vcpkg package feature '" + feature + "'");
}
}
if (features.empty()) {
ofs << " \"" << package.name << '\"';
} else {
ofs << " {\n";
ofs << " \"name\": \"" << package.name << "\",\n";
ofs << " \"features\": [";
for (size_t j = 0; j < features.size(); j++) {
const auto &feature = features[j];
ofs << '\"' << feature << '\"';
if (j + 1 < features.size()) {
ofs << ',';
}
}
ofs << "]\n";
ofs << " }";
}
ofs << " \"" << package << '\"';
if (i + 1 < packages.size()) {
ofs << ',';
}
@@ -636,6 +693,26 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
ofs << "}\n";
}
if (!project.contents.empty()) {
cmd("include")("FetchContent").endl();
for (const auto &content : project.contents) {
ConditionScope cs(gen, content.condition);
std::string version_info = "";
if (content.arguments.contains("GIT_TAG")) {
version_info = " (" + content.arguments.at("GIT_TAG") + ")";
} else if (content.arguments.contains("SVN_REVISION")) {
version_info = " (" + content.arguments.at("SVN_REVISION") + ")";
}
cmd("message")("STATUS", "Fetching " + content.name + version_info + "...");
ss << "FetchContent_Declare(\n\t" << content.name << "\n";
for (const auto &arg : content.arguments) {
ss << "\t" << arg.first << "\n\t\t" << arg.second << "\n";
}
ss << ")\n";
cmd("FetchContent_MakeAvailable")(content.name).endl();
}
}
if (!project.packages.empty()) {
comment("Packages");
for (const auto &dep : project.packages) {
@@ -645,39 +722,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto required = dep.required ? "REQUIRED" : "";
auto config = dep.config ? "CONFIG" : "";
auto components = std::make_pair("COMPONENTS", dep.components);
ConditionScope cs(gen, dep.condition);
cmd("find_package")(dep.name, version, required, config, components).endl();
}
}
if (!project.options.empty()) {
comment("Options");
for (const auto &opt : project.options) {
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
}
endl();
}
if (!project.settings.empty()) {
comment("Settings");
for (const auto &set : project.settings) {
std::string set_val;
if (set.val.index() == 1) {
set_val = mpark::get<1>(set.val);
} else {
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
}
if (set.cache) {
auto typ = set.val.index() == 1 ? "STRING" : "BOOL";
auto force = set.force ? "FORCE" : "";
cmd("set")(set.name, set_val, typ, set.comment, force);
} else {
cmd("set")(set.name, set_val);
}
}
endl();
}
auto add_subdir = [&](const std::string &dir) {
// clang-format off
comment(dir);
@@ -703,13 +752,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
endl();
}
for (const auto &subdir : project.subdirs) {
if (!subdir.condition.empty()) {
const auto &condition = subdir.condition;
if (project.conditions.count(condition) == 0) {
throw std::runtime_error("Unknown condition '" + condition + "' for [subdir." + subdir.name + "]");
}
gen.cmd("if", condition)(RawArg(project.conditions[condition]));
}
ConditionScope cs(gen, subdir.condition);
gen.handle_condition(subdir.include_before,
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
@@ -719,23 +762,18 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.handle_condition(subdir.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
gen.handle_condition(subdir.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
if (!subdir.condition.empty()) {
cmd("endif")();
}
}
if (!project.targets.empty()) {
for (const auto &target : project.targets) {
for (size_t i = 0; i < project.targets.size(); i++) {
if (i > 0) {
endl();
}
const auto &target = project.targets[i];
comment("Target " + target.name);
if (!target.condition.empty()) {
const auto &condition = target.condition;
if (project.conditions.count(condition) == 0) {
throw std::runtime_error("Unknown condition '" + condition + "' for [target." + target.name + "]");
}
gen.cmd("if", condition)(RawArg(project.conditions[condition]));
}
ConditionScope cs(gen, target.condition);
cmd("set")("CMKR_TARGET", target.name);
@@ -877,13 +915,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
cmd("unset")("CMKR_TARGET");
cmd("unset")("CMKR_SOURCES");
if (!target.condition.empty()) {
cmd("endif")();
}
endl();
}
endl();
}
if (!project.tests.empty()) {
@@ -898,6 +932,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto working_directory = std::make_pair("WORKING_DIRECTORY", dir);
auto command = std::make_pair("COMMAND", test.command);
auto arguments = std::make_pair("", test.arguments);
ConditionScope cs(gen, test.condition);
cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
}
}
@@ -917,6 +952,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto configs = std::make_pair("CONFIGURATIONS", inst.configs);
auto destination = std::make_pair("DESTINATION", inst.destination);
auto component = std::make_pair("COMPONENT", inst.targets.empty() ? "" : inst.targets.front());
ConditionScope cs(gen, inst.condition);
cmd("install")(targets, dirs, files, configs, destination, component);
}
}
+36 -1
View File
@@ -145,6 +145,11 @@ class TomlChecker {
}
}
throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second));
} else if (ky == "condition") {
std::string condition = itr.second.as_string();
if (!conditions.contains(condition)) {
throw std::runtime_error(format_key_error("Unknown condition '" + condition + "'", condition, itr.second));
}
}
}
}
@@ -231,6 +236,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake";
conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake";
conditions["msvc"] = R"cmake(MSVC)cmake";
conditions["root"] = R"cmake(CMKR_ROOT_PROJECT)cmake";
conditions["x64"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 8)cmake";
conditions["x32"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 4)cmake";
} else {
conditions = parent->conditions;
}
@@ -330,6 +338,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
p.version = itr.second.as_string();
} else {
auto &pkg = checker.create(value);
pkg.optional("condition", p.condition);
pkg.optional("version", p.version);
pkg.optional("required", p.required);
pkg.optional("config", p.config);
@@ -347,6 +356,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
content.name = itr.first;
for (const auto &argItr : itr.second.as_table()) {
auto key = argItr.first;
if (key == "condition") {
content.condition = argItr.second.as_string();
continue;
}
if (key == "git") {
key = "GIT_REPOSITORY";
} else if (key == "tag") {
@@ -469,6 +483,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
auto &t = checker.create(value);
Test test;
t.required("name", test.name);
t.optional("condition", test.condition);
t.optional("configurations", test.configurations);
t.optional("working-directory", test.working_directory);
t.required("command", test.command);
@@ -482,6 +497,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
for (const auto &value : is) {
auto &i = checker.create(value);
Install inst;
i.optional("condition", inst.condition);
i.optional("targets", inst.targets);
i.optional("files", inst.files);
i.optional("dirs", inst.dirs);
@@ -495,7 +511,26 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
auto &v = checker.create(toml, "vcpkg");
v.optional("url", vcpkg.url);
v.optional("version", vcpkg.version);
v.required("packages", vcpkg.packages);
for (const auto &p : v.find("packages").as_array()) {
Vcpkg::Package package;
const auto &package_str = p.as_string().str;
const auto open_bracket = package_str.find('[');
const auto close_bracket = package_str.find(']', open_bracket);
if (open_bracket == std::string::npos && close_bracket == std::string::npos) {
package.name = package_str;
} else if (close_bracket != std::string::npos) {
package.name = package_str.substr(0, open_bracket);
const auto features = package_str.substr(open_bracket + 1, close_bracket - open_bracket - 1);
std::istringstream feature_stream{features};
std::string feature;
while (std::getline(feature_stream, feature, ',')) {
package.features.emplace_back(feature);
}
} else {
throw std::runtime_error("Invalid vcpkg package '" + package_str + "'");
}
vcpkg.packages.emplace_back(std::move(package));
}
}
checker.check(conditions);
+2 -1
View File
@@ -18,4 +18,5 @@ type = "executable"
sources = ["src/main.cpp"]
link-libraries = ["fmt::fmt"]
# The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`.
# The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`.
# To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.