Compare commits

...

126 Commits

Author SHA1 Message Date
Duncan Ogilvie a499df84b1 Bump to 0.2.19 2023-02-28 17:11:55 +01:00
Duncan Ogilvie 49440f99e0 Merge pull request #92 from build-cpp/improved-options
Improve and document the `[options]` feature
2023-02-20 02:13:50 +01:00
Duncan Ogilvie 0c19c3da0c Improve the code related to [options] 2023-02-20 02:09:36 +01:00
Duncan Ogilvie 233cadadd0 Merge pull request #90 from mike1k/sanity
Error on unknown language & unacceptable source file.
2023-02-20 01:31:47 +01:00
Duncan Ogilvie 58a5a935e5 Add a bunch of missing languages and extensions and refactor 2023-02-20 01:18:48 +01:00
Duncan Ogilvie e69427bf94 Merge pull request #91 from build-cpp/clang-condition
Fix the `clang` condition and CI improvements
2023-02-19 22:30:15 +01:00
Duncan Ogilvie bc6359805d Change the clang condition to not detect clang-cl
References:
- https://github.com/cursey/safetyhook/issues/18
- https://discourse.cmake.org/t/clang-on-windows-detected-with-msvc-interface/3470
2023-02-19 22:23:34 +01:00
Duncan Ogilvie 8dcc11e349 Add helper function Project::cmake_minimum_version for version-dependent features 2023-02-19 22:19:12 +01:00
Duncan Ogilvie 51dc49e6f1 Improve CI performance for non-tagged builds 2023-02-19 20:06:58 +01:00
mike 7d27c28b59 Fixed failing tests due to implicit project languages (internal list empty) 2023-02-09 10:53:48 -06:00
Duncan Ogilvie ae6bea3b58 Improve reference documentation 2023-01-28 11:57:22 +01:00
Duncan Ogilvie 5a0eda7abc Automatically generate release notes 2023-01-15 13:34:36 +01:00
Duncan Ogilvie f17923c18b Bump to 0.2.18 2023-01-14 21:28:17 +01:00
Duncan Ogilvie ad17890e23 Merge pull request #85 from build-cpp/settings-rename
Rename `[settings]` to `[variables]`
2023-01-14 21:14:12 +01:00
Duncan Ogilvie c0b98b3ef4 Add documentation for the [variables] section 2023-01-14 18:12:57 +01:00
Duncan Ogilvie 2c6fc569af Preserve backwards compatibility 2023-01-14 16:17:06 +01:00
Duncan Ogilvie 12ee23a44c Improve error messages 2023-01-14 15:52:19 +01:00
Duncan Ogilvie 82342a6b6f Rename [settings] to [variables] 2023-01-14 15:51:57 +01:00
Duncan Ogilvie 1d95fab0ce Switch to giscus for documentation comments 2023-01-02 14:56:38 +01:00
Duncan Ogilvie 44a77329bf Merge pull request #83 from build-cpp/document-basics
Document basics
2023-01-02 12:43:24 +01:00
Duncan Ogilvie e68c5fccdc Add utteranc.es comments 2023-01-02 12:43:20 +01:00
Duncan Ogilvie e2722d2c09 Initial draft of new documentation 2023-01-02 12:02:24 +01:00
Duncan Ogilvie 2627864383 Merge pull request #78 from build-cpp/fix-tests
Successfully fail when running cmkr subcommands
2022-11-29 10:11:45 +01:00
Duncan Ogilvie 05e21f734a Fix the tests 2022-11-29 10:06:34 +01:00
Duncan Ogilvie 1fd18503cd Successfully fail when running cmkr subcommands 2022-11-24 00:32:31 +01:00
Duncan Ogilvie 8b19441d34 Bump to 0.2.17 2022-11-24 00:31:42 +01:00
Duncan Ogilvie 2437eb90fe Minor improvements
- Drastically improve quality of generated CMake
- Fix a regression with recursive globbing in the root
- Automatically include CSharpUtilities when using CSharp
2022-11-24 00:23:30 +01:00
Duncan Ogilvie 6e4006ec07 Fix typo in the docs 2022-10-25 00:59:58 +02:00
Duncan Ogilvie 697b638723 Fix warning on macos 2022-10-11 14:19:29 +02:00
Duncan Ogilvie 109c4bc99f Bump to 0.2.16 2022-10-08 10:48:16 +02:00
Duncan Ogilvie cad85f0bb1 Merge pull request #70 from build-cpp/fix-msvc-runtime
Actually generate the set(CMAKE_MSVC_RUNTIME_LIBRARY)
2022-10-08 10:47:56 +02:00
Duncan Ogilvie 53c88ceafa Actually generate the set(CMAKE_MSVC_RUNTIME_LIBRARY)
Closes #69 (nice)
2022-10-08 10:37:27 +02:00
Duncan Ogilvie d2aa15b1c7 Bump to 0.2.15 2022-10-07 22:21:07 +02:00
Duncan Ogilvie e6ded077cf Merge pull request #65 from gmh5225/feature-msvc-static
[feature] msvc-static
2022-10-07 22:20:36 +02:00
Duncan Ogilvie 3615ccab94 Document msvc-runtime feature 2022-10-07 22:15:32 +02:00
Duncan Ogilvie 73622aa5ba Remove the add_test hook to generate tests the same on all platforms 2022-10-07 22:09:54 +02:00
Duncan Ogilvie 69d844a152 Refactor msvc-static to msvc-runtime 2022-10-07 17:46:36 +02:00
gmh5225 af9a117f50 Fix merge 2022-10-07 06:50:26 +08:00
gmh5225 5fe8220728 Merge branch 'feature-msvc-static' of https://github.com/gmh5225/cmkr into feature-msvc-static 2022-10-07 06:41:43 +08:00
gmh5225 28f541cd3c [feature] add CMP0091 2022-10-07 06:41:11 +08:00
gmh5225 2f3fd7b95e Merge branch 'main' into feature-msvc-static 2022-10-07 05:35:11 +08:00
Duncan Ogilvie 07d99c582d Merge pull request #68 from build-cpp/headers-crash
Fix the way target.headers is merged into target.sources
2022-10-06 18:30:13 +02:00
Duncan Ogilvie 5af2385cc3 Fix the way target.headers is merged into target.sources
- It would crash if you did not specify target.sources
- Headers with condition would be merged incorrectly
2022-10-06 18:22:51 +02:00
gmh5225 981c48dbc5 [feature] msvc-static 2022-10-02 10:43:32 +08:00
Duncan Ogilvie 153bdbe591 Bump to 0.2.14 2022-07-19 14:17:47 +02:00
Duncan Ogilvie 97f1c5c1cc Fix a bug when there are regex characters in the path
Closes #61
2022-07-19 14:05:47 +02:00
Duncan Ogilvie 5b6d9c3826 Replace illegal characters in the project name with _ when doing cmkr init 2022-07-19 13:42:56 +02:00
Duncan Ogilvie a90988b81a Update the formatting to now allow functions on a single line
This makes it less annoying when the IDE is auto-formatting while you type
2022-07-19 13:41:43 +02:00
Duncan Ogilvie 3a1298f4e8 Merge pull request #60 from pmeerw/fix-warning
error: fix warning, signedness of comparison
2022-06-28 19:47:39 +02:00
Peter Meerwald-Stadler 534f955827 error: fix warning, signedness of comparison
cmkr/src/error.cpp:23:24: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
   23 |     assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string))));
2022-06-28 18:21:15 +02:00
Duncan Ogilvie ef537ce084 Bump to 0.2.13 2022-06-01 19:27:03 +02:00
Duncan Ogilvie 9cdd0f7344 Escape lists when generating commands 2022-06-01 19:26:41 +02:00
Duncan Ogilvie e69cf4d2b9 Add install.optional flag 2022-06-01 19:06:53 +02:00
Duncan Ogilvie e98a906231 Document the install.component option 2022-06-01 16:59:54 +02:00
Duncan Ogilvie 9a82f8c796 Temporarily remove the crt-linkage and library-linkage options
These require a lot more work to integrate properly with vcpkg, reimplement triplet detection and set a custom triplet
2022-06-01 16:57:09 +02:00
Duncan Ogilvie 13255c68cf Use FetchContent_MakeAvailable for vcpkg in case they add CMakeLists.txt 2022-06-01 16:53:38 +02:00
Duncan Ogilvie 5768460827 Update credits 2022-04-08 18:16:38 +02:00
Duncan Ogilvie 50d4a905b6 Merge pull request #55 from ZehMatt/fix/#54
Fix #54: Fix settings not being properly quoted
2022-04-01 12:49:02 +02:00
ζeh Matt f957cec2dc Update CMakeLists.txt for tests 2022-04-01 03:37:04 +03:00
Duncan Ogilvie 232e49e087 Add support for vcpkg CRT and library linkage customization 2022-04-01 02:27:41 +02:00
ζeh Matt 7408d42160 Fix #54: Fix settings not being properly quoted 2022-04-01 03:15:32 +03:00
Duncan Ogilvie 1596a8143d Bump to 0.2.12 2022-03-06 06:07:55 +01:00
Duncan Ogilvie 9b0f18ee94 Fix a bug in fetch-content generation 2022-03-06 06:05:17 +01:00
Duncan Ogilvie 887086dc05 Credits 2022-02-23 21:32:58 +01:00
Duncan Ogilvie edc8e1e02e Bump to 0.2.11 2022-02-12 04:42:19 +01:00
Duncan Ogilvie 1f6e31e0ef Add optional CMKR_COMMIT_HASH for the paranoid 2022-02-12 04:39:34 +01:00
Duncan Ogilvie 4de1500bb3 Refactor cmake injection 2022-02-12 04:14:47 +01:00
Duncan Ogilvie 85370968f8 Improved fetch-content support
Closes #38
2022-02-12 04:05:51 +01:00
Duncan Ogilvie 06a4c04df6 Add support for 'shallow' in fetch-content 2022-02-12 03:10:25 +01:00
Duncan Ogilvie fef2a0215b Error when recursively globbing in the project root
Closes #48
2022-02-12 03:03:57 +01:00
Duncan Ogilvie fc74a5e802 Bump to 0.2.10 2022-02-12 02:45:10 +01:00
Duncan Ogilvie e48d402e21 Improve version bumping script 2022-02-12 02:44:45 +01:00
Duncan Ogilvie f6a245618e Merge pull request #53 from build-cpp/better-validation
Better TOML validation
2022-02-12 02:44:05 +01:00
Duncan Ogilvie c713606fd4 Error when using an unknown key in the root of the TOML
[error] Unknown key 'fetch-cntent'
 --> cmake.toml:2
   |
 2 | [fetch-cntent.blah]
   |  ~~~~~~~~~~~~
2022-02-12 02:19:37 +01:00
Duncan Ogilvie fe9b6587a9 Error when trying to parse an empty cmake.toml file 2022-02-12 02:07:30 +01:00
Duncan Ogilvie a2f8d9e5c9 Bump to 0.2.9 2022-02-11 02:51:18 +01:00
Duncan Ogilvie c7925e7110 Merge pull request #52 from build-cpp/fix-options
Do not omit non-optional documentation in option()
2022-02-11 02:49:21 +01:00
Duncan Ogilvie c778a5fe2b Do not omit non-optional documentation in option() 2022-02-11 02:42:20 +01:00
Duncan Ogilvie 16b2cbbb57 Add sitemap.xml and robots.txt 2022-02-10 01:20:54 +01:00
Duncan Ogilvie 2f5c7823be Redirect /getting-started to https://cmkr.build 2022-02-10 00:54:32 +01:00
Duncan Ogilvie a29b6a8aaa SEO-friendly permalink 2022-02-10 00:15:28 +01:00
Duncan Ogilvie 5ac8b15c9a Bump to 0.2.8 2022-02-02 17:25:10 +01:00
Duncan Ogilvie e7d9faa6bb Merge pull request #51 from cursey/improvement/install-component
Allow specifying install component name
2022-01-29 11:31:29 +01:00
cursey 6f8b07a801 Allow specifying install component name 2022-01-28 17:16:19 -08:00
Duncan Ogilvie e8c6ccb698 Minor improvements to bootstrapping code 2022-01-26 01:07:18 +01:00
Duncan Ogilvie 539ebc8f4b Host documentation on https://cmkr.build 2022-01-14 18:06:04 +01:00
Duncan Ogilvie 37ca385673 Show vcpkg version when configuring 2022-01-14 13:42:03 +01:00
Duncan Ogilvie 8ae029dab1 Bump to 0.2.7 2022-01-14 01:04:11 +01:00
Duncan Ogilvie fb16cc34b1 Fix a regression with invalid names being generated in vcpkg.json 2022-01-14 01:03:37 +01:00
Duncan Ogilvie 47703871e1 Bump to 0.2.6 2022-01-13 00:35:13 +01:00
Duncan Ogilvie a8d6b15dcb Merge pull request #47 from cursey/templates
Add support for target templates
2022-01-13 00:33:05 +01:00
Duncan Ogilvie 87b3c7ec6c Document template targets 2022-01-13 00:24:04 +01:00
Duncan Ogilvie d905be1d13 Final touches 2022-01-12 23:57:14 +01:00
Duncan Ogilvie 8d13ccbeaa Delete driver test to add later 2022-01-12 23:56:53 +01:00
cursey bcbc9d2b20 Remove use of std::make_unique for C++11 compliance 2022-01-11 00:12:02 -08:00
cursey 05324f593b Improve code reuse for target cmds 2022-01-11 00:04:01 -08:00
cursey 2b7ee72e86 Separate template parsing and generation
* Reuse type for specifying template to use
* Add support for template add-function
* Add support for template pass-sources-to-add-function
2022-01-10 23:29:26 -08:00
cursey 61d5e64d87 Fix parser expecting to always find templates 2022-01-10 18:59:34 -08:00
cursey eed1e38407 Add support for target templates 2022-01-10 18:59:33 -08:00
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
48 changed files with 1760 additions and 636 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
+1
View File
@@ -0,0 +1 @@
github: [mrexodia]
+9 -6
View File
@@ -15,7 +15,7 @@ jobs:
BUILD_TYPE: Release
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Tag cmkr.cmake
if: ${{ startsWith(github.ref, 'refs/tags/') }}
@@ -35,31 +35,34 @@ jobs:
- name: Test
run: |
cd build/tests
ctest -C ${{ env.BUILD_TYPE }}
ctest -C ${{ env.BUILD_TYPE }} --verbose
- name: Upload artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: ${{ github.event.repository.name }}-${{ matrix.os }}
path: install/bin/*
- name: Get lowercase OS name
id: osname
uses: ASzc/change-string-case-action@v1
uses: ASzc/change-string-case-action@07c1e24a97f0951e13f88870b99c058fcf0b14cf # v5
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
string: ${{ runner.os }}
- name: Compress artifacts
uses: papeloto/action-zip@v1
uses: vimtor/action-zip@26a249fb00d43ca98dad77a4b3838025fc226aa1 # v1.1
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
files: install/bin/
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
- name: Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
files: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1
View File
@@ -9,3 +9,4 @@ build*/
.idea/
cmake-build*/
CMakeLists.txt.user
.vscode/
+15 -43
View File
@@ -7,17 +7,14 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
endif()
# 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)
# 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)
# Create a configure-time dependency on cmake.toml to improve IDE support
configure_file(cmake.toml cmake.toml COPYONLY)
endif()
@@ -25,7 +22,7 @@ project(cmkr
LANGUAGES
CXX
VERSION
0.1.4
0.2.19
DESCRIPTION
"CMakeLists generator from TOML"
)
@@ -33,7 +30,7 @@ project(cmkr
include("cmake/generate_documentation.cmake")
include("cmake/generate_resources.cmake")
# third_party
# Subdirectory: third_party
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party")
@@ -43,7 +40,7 @@ endif()
add_subdirectory(third_party)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# tests
# Subdirectory: tests
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests")
@@ -53,29 +50,14 @@ endif()
add_subdirectory(tests)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Target cmkr_generate_documentation
set(CMKR_TARGET cmkr_generate_documentation)
set(cmkr_generate_documentation_SOURCES "")
set(CMKR_SOURCES ${cmkr_generate_documentation_SOURCES})
# Target: cmkr_generate_documentation
add_library(cmkr_generate_documentation INTERFACE)
if(cmkr_generate_documentation_SOURCES)
target_sources(cmkr_generate_documentation INTERFACE ${cmkr_generate_documentation_SOURCES})
endif()
set(CMKR_TARGET cmkr_generate_documentation)
generate_documentation()
unset(CMKR_TARGET)
unset(CMKR_SOURCES)
# Target cmkr
set(CMKR_TARGET cmkr)
set(cmkr_SOURCES "")
list(APPEND cmkr_SOURCES
# Target: cmkr
set(cmkr_SOURCES
"src/arguments.cpp"
"src/build.cpp"
"src/cmake_generator.cpp"
@@ -86,7 +68,6 @@ list(APPEND cmkr_SOURCES
"include/arguments.hpp"
"include/build.hpp"
"include/cmake_generator.hpp"
"include/enum_helper.hpp"
"include/error.hpp"
"include/fs.hpp"
"include/help.hpp"
@@ -94,24 +75,12 @@ list(APPEND cmkr_SOURCES
"include/project_parser.hpp"
"cmake/cmkr.cmake"
"cmake/version.hpp.in"
)
list(APPEND cmkr_SOURCES
cmake.toml
)
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)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmkr)
endif()
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES})
target_compile_features(cmkr PRIVATE
@@ -130,10 +99,13 @@ target_link_libraries(cmkr PRIVATE
nlohmann_json
)
generate_resources(${CMKR_TARGET})
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 cmkr)
endif()
unset(CMKR_TARGET)
unset(CMKR_SOURCES)
set(CMKR_TARGET cmkr)
generate_resources(${CMKR_TARGET})
install(
TARGETS
+35 -12
View File
@@ -7,37 +7,58 @@
```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 and you only need CMake and a C++ compiler 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, customize [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) for your project and run 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` when necessary.
<sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub>
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional 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]
arguments:
init [executable|library|shared|static|interface] Starts a new project in the same directory.
init [executable|library|shared|static|interface] Create a project.
gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build.
install Run cmake --install. Needs admin privileges.
install Run cmake --install.
clean Clean the build directory.
help Show help.
version Current cmkr version.
@@ -45,8 +66,10 @@ arguments:
## 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
- [gulrak/filesystem](https://github.com/gulrak/filesystem)
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
- [mpark/variant](https://github.com/mpark/variant)
- [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer)
- [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
+2 -2
View File
@@ -4,14 +4,14 @@ cmkr-include = false
[project]
name = "cmkr"
version = "0.1.4"
version = "0.2.19"
description = "CMakeLists generator from TOML"
languages = ["CXX"]
subdirs = ["third_party", "tests"]
include-after = [
"cmake/generate_documentation.cmake",
"cmake/generate_resources.cmake"
]
subdirs = ["third_party", "tests"]
[target.cmkr_generate_documentation]
type = "interface"
+78
View File
@@ -0,0 +1,78 @@
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()
# Validate branch
find_package(Git REQUIRED)
execute_process(COMMAND "${GIT_EXECUTABLE}" branch --show-current OUTPUT_VARIABLE GIT_BRANCH)
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
if(NOT GIT_BRANCH STREQUAL "main")
message(FATAL_ERROR "You need to be on the main branch, you are on: ${GIT_BRANCH}")
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" PATH_SUFFIXES Debug Release RelWithDebInfo MinSizeRel 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}")
+73 -16
View File
@@ -1,17 +1,22 @@
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.19" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" 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")
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration")
mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE)
# Disable cmkr if generation is disabled
if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION)
@@ -23,7 +28,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()
@@ -53,22 +58,41 @@ else()
endif()
# Use cached cmkr if found
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
if(DEFINED ENV{CMKR_CACHE})
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
endif()
# Build in release mode for the cache
set(CMKR_BUILD_TYPE "Release")
else()
set(CMKR_DIRECTORY_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_")
endif()
set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
# Helper function to check if a string starts with a prefix
# Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61
function(cmkr_startswith str prefix result)
string(LENGTH "${prefix}" prefix_length)
string(LENGTH "${str}" str_length)
if(prefix_length LESS_EQUAL str_length)
string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix)
if(prefix STREQUAL str_prefix)
set("${result}" ON PARENT_SCOPE)
return()
endif()
endif()
set("${result}" OFF PARENT_SCOPE)
endfunction()
# Handle upgrading logic
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
if(CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD)
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE)
if(CMKR_STARTSWITH_BUILD)
if(DEFINED ENV{CMKR_CACHE})
message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
@@ -79,7 +103,7 @@ if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE)
endif()
elseif(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}" AND CMKR_EXECUTABLE MATCHES "^${CMKR_DIRECTORY_PREFIX}")
elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE)
message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE)
endif()
@@ -95,7 +119,7 @@ elseif(NOT CMKR_EXECUTABLE AND EXISTS "${CMKR_CACHED_EXECUTABLE}")
else()
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...")
if(EXISTS "${CMKR_DIRECTORY}")
cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}")
@@ -109,6 +133,16 @@ else()
${CMKR_REPO}
"${CMKR_DIRECTORY}"
)
if(CMKR_COMMIT_HASH)
execute_process(
COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}"
RESULT_VARIABLE CMKR_EXEC_RESULT
WORKING_DIRECTORY "${CMKR_DIRECTORY}"
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'")
endif()
endif()
message(STATUS "[cmkr] Building cmkr (using system compiler)...")
cmkr_exec("${CMAKE_COMMAND}"
--no-warn-unused-cli
@@ -143,6 +177,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
@@ -176,18 +233,18 @@ macro(cmkr)
# Copy the now-generated CMakeLists.txt to CMakerLists.txt
# This is done because you cannot include() a file you are currently in
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()
+14 -14
View File
@@ -1,20 +1,20 @@
option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT})
set(CMKR_TESTS "" CACHE INTERNAL "List of test directories in the order declared in tests/cmake.toml")
if(CMKR_GENERATE_DOCUMENTATION)
# Hook the add_test function to capture the tests in the order declared in tests/cmake.toml
function(add_test)
cmake_parse_arguments(TEST "" "WORKING_DIRECTORY" "" ${ARGN})
get_filename_component(TEST_WORKING_DIRECTORY "${TEST_WORKING_DIRECTORY}" NAME)
list(APPEND CMKR_TESTS "${TEST_WORKING_DIRECTORY}")
set(CMKR_TESTS "${CMKR_TESTS}" CACHE INTERNAL "")
_add_test(${test} ${ARGN})
endfunction()
endif()
function(generate_documentation)
if(CMKR_GENERATE_DOCUMENTATION)
message(STATUS "[cmkr] Generating documentation...")
# Extract the order of the tests
set(CMKR_TESTS "")
file(READ "${PROJECT_SOURCE_DIR}/tests/cmake.toml" tests_toml NO_HEX_CONVERSION)
string(REGEX MATCHALL "working-directory = \"([^\"]+)\"" tests_match "${tests_toml}")
foreach(match ${tests_match})
if(match MATCHES "working-directory = \"([^\"]+)\"")
list(APPEND CMKR_TESTS "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "This should not happen (wrong regex?)")
endif()
endforeach()
# Delete previously generated examples
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")
@@ -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/
+1
View File
@@ -0,0 +1 @@
cmkr.build
+1
View File
@@ -3,6 +3,7 @@ gem "minima", "~> 2.5"
gem "github-pages", group: :jekyll_plugins
gem "jekyll-remote-theme"
gem "jekyll-sitemap", "~> 1.4.0"
platforms :mingw, :x64_mingw, :mswin, :jruby do
gem "tzinfo", "~> 1.2"
+1
View File
@@ -271,6 +271,7 @@ PLATFORMS
DEPENDENCIES
github-pages
jekyll-remote-theme
jekyll-sitemap (~> 1.4.0)
just-the-docs
minima (~> 2.5)
tzinfo (~> 1.2)
+23 -2
View File
@@ -2,6 +2,7 @@ title: cmkr
plugins:
- jekyll-remote-theme
- jekyll-sitemap
# Automatically deduce dark theme preference https://github.com/pmarsceill/just-the-docs/pull/464
remote_theme: build-cpp/just-the-docs@light-switch
@@ -9,7 +10,7 @@ search_enabled: true
color_scheme: light-or-dark
heading_anchors: true
aux_links:
"cmkr":
"GitHub":
- "https://github.com/build-cpp/cmkr"
aux_links_new_tab: true
@@ -18,4 +19,24 @@ gh_edit_link_text: "Edit this page on GitHub."
gh_edit_repository: "https://github.com/build-cpp/cmkr"
gh_edit_branch: "main"
gh_edit_view_mode: "edit"
gh_edit_source: docs
gh_edit_source: docs
ga_tracking: G-DY172KY7Z9
production_url : https://cmkr.build
footer_content: |
<script src="https://giscus.app/client.js"
data-repo="build-cpp/cmkr"
data-repo-id="MDEwOlJlcG9zaXRvcnkyOTQwMDc3MzY="
data-category="Documentation"
data-category-id="DIC_kwDOEYYzuM4CTXeA"
data-mapping="pathname"
data-strict="1"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="en"
crossorigin="anonymous"
async>
</script>
+76
View File
@@ -0,0 +1,76 @@
---
layout: page
title: Basics
nav_order: 1
---
# Basics
To effectively use cmkr it helps to understand the basic concepts of CMake.
## Projects
A CMake **project** is a collection of targets. In the context of libraries the project usually corresponds to the **package** that other projects can depend on.
<sub>Visual Studio: a CMake **project** corresponds to a _solution_.</sub>
## Targets
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or library you can build. There are also [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but we ignore them for now.
<sub>Visual Studio: a **target** corresponds to a _project_.</sub>
## Target Properties
Targets have a collection of **properties** that describe how to build them.
Examples of properties:
- _Sources_: the `*.cpp` files used to build the target.
- _Compile options_: Command line flags for the compiler.
- _Link libraries_: The **dependencies** required to build this target.
<sub>See the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for an exhaustive list of target properties.</sub>
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line of the linker, CMake also propagates properties of the target you link to.
<sub>You can think of **linking** as _depending on_.</sub>
The propagation of properties depends on their **visibility**:
- **Private**: properties that are only used when building the target itself.
- **Interface**: properties that are used when depending on this target.
- **Public**: combination of private and interface.
In practice you default to **private**, unless consumers of your library _require_ the property to build their target. In that case you use **public**.
### Example
The most intuitive example is with _include directories_. Imagine there are two targets:
1. `StringUtils`: A library with string utilities.
- _Sources_: `StringUtils/src/stringutils.cpp`
- _Include directories_: `StringUtils/include`
2. `DataProcessor`: An executable that uses functionality from `StringUtils` to process some data.
- _Sources_: `DataProcessor/src/main.cpp`
- _Link libraries_: `StringUtils`
The _include directories_ property of `StringUtils` has to be **public**. If it was **private**, the `DataProcessor` target would fail to `#include <stringutils.hpp>` since the _include directories_ property is not propagated.
The `cmake.toml` for this example would look something like this:
```toml
[project]
name = "DataProcessor"
[target.StringUtils]
type = "static"
sources = ["StringUtils/src/stringutils.cpp"]
headers = ["StringUtils/include/stringutils.hpp"]
include-directories = ["StringUtils/include"]
[target.DataProcessor]
type = "executable"
sources = ["DataProcessor/src/main.cpp"]
link-libraries = ["StringUtils"]
```
+65 -23
View File
@@ -1,15 +1,16 @@
---
layout: page
title: cmake.toml
title: Reference
permalink: /cmake-toml/
nav_order: 3
---
# 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.
This is a reference page. Check out the [examples](/examples) and the [cmkr topic](https://github.com/topics/cmkr) as well.
{:.info}
## CMake configuration
@@ -27,6 +28,7 @@ name = "myproject"
version = "1.0.0"
description = "Description of the project"
languages = ["C", "CXX"]
msvc-runtime = "" # dynamic (implicit default), static
cmake-before = """
message(STATUS "CMake injected before the project() call")
"""
@@ -43,10 +45,12 @@ You can specify your own conditions and use them in any `condition` field:
```toml
[conditions]
arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8"
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4"
arch64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
arch32 = "CMAKE_SIZEOF_VOID_P EQUAL 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 +60,9 @@ 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):
### Predefined conditions
The following conditions are predefined (you can override them if you desire):
```toml
[conditions]
@@ -66,15 +72,18 @@ unix = "UNIX"
bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\""
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\""
clang = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
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"
"""
@@ -85,6 +94,26 @@ include-before = ["cmake/before-subdir.cmake"]
include-after = ["cmake/after-subdir.cmake"]
```
## Options
```toml
[options]
MYPROJECT_BUILD_TESTS = false
MYPROJECT_SPECIAL_OPTION = { value = true, help = "Docstring for this option." }
```
Options correspond to [CMake cache variables](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html) that can be used to customize your project at configure-time. You can configure with `cmake -DMYPROJECT_BUILD_TESTS=ON` to enable the option. Every options automatically gets a corresponding [condition](#conditions).
## Variables
```toml
[variables]
MYBOOL = true
MYSTRING = "hello"
```
Variables emit a [`set`](https://cmake.org/cmake/help/latest/command/set.html) and can be used to configure subprojects and packages.
## Vcpkg
```toml
@@ -96,14 +125,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,26 +143,36 @@ 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"
shallow = false
[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"
# These are equivalent, supported algorithms:
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
```
## 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"]
msvc-runtime = "" # dynamic (implicit default), static
# The keys below match the target_xxx CMake commands
# Keys prefixed with private- will get PRIVATE visibility
@@ -178,6 +216,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,9 +226,12 @@ working-directory = "mytest-dir"
```toml
[[install]]
condition = "mycondition"
targets = ["mytarget", "mytest"]
destination = ["bin"]
component = "mycomponent"
files = ["content/my.png"]
dirs = [""]
configs = [""]
dirs = ["include"]
configs = ["Release", "Debug"]
optional = false
```
-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.
```
+1
View File
@@ -19,6 +19,7 @@ description = "Header-only library"
[target.mylib]
type = "interface"
include-directories = ["include"]
compile-features = ["cxx_std_11"]
[target.example]
type = "executable"
+34
View File
@@ -0,0 +1,34 @@
---
# Automatically generated from tests/msvc-runtime/cmake.toml - DO NOT EDIT
layout: default
title: Static MSVC runtime
permalink: /examples/msvc-runtime
parent: Examples
nav_order: 8
---
# Static MSVC runtime
```toml
[project]
name = "msvc-runtime"
description = "Static MSVC runtime"
msvc-runtime = "static"
# This target will compile with a static runtime
[target.static-runtime]
type = "executable"
sources = ["src/main.cpp"]
# This target overrides the [project].msvc-runtime
[target.dynamic-runtime]
type = "executable"
sources = ["src/main.cpp"]
msvc-runtime = "dynamic"
```
<sup><sub>This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml).</sub></sup>
+40
View File
@@ -0,0 +1,40 @@
---
# Automatically generated from tests/templates/cmake.toml - DO NOT EDIT
layout: default
title: Target templates
permalink: /examples/templates
parent: Examples
nav_order: 7
---
# Target templates
To avoid repeating yourself in targets you can create your own target type (template). All properties of the template are inherited when used as a target type.
```toml
[project]
name = "templates"
description = "Target templates"
[template.app]
type = "executable"
sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties
[template.app.properties]
CXX_STANDARD = "11"
CXX_STANDARD_REQUIRED = true
[target.app-a]
type = "app"
compile-definitions = ["APP_A"]
[target.app-b]
type = "app"
compile-definitions = ["APP_B"]
```
**Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
<sup><sub>This page was automatically generated from [tests/templates/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/templates/cmake.toml).</sub></sup>
+3 -1
View File
@@ -19,7 +19,7 @@ description = "Dependencies from vcpkg"
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
# See https://vcpkg.io/en/packages.html for available packages
[vcpkg]
version = "2021.05.12"
version = "2022.11.14"
packages = ["fmt"]
[find-package]
@@ -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>
+10
View File
@@ -0,0 +1,10 @@
---
layout: null
permalink: /getting-started/
---
<html>
<head>
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
</head>
</html>
-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.
+63 -5
View File
@@ -1,23 +1,81 @@
---
layout: home
title: Index
title: Documentation
nav_order: 0
---
# Index
# Documentation
`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`](https://github.com/build-cpp/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, and you only need CMake and a C++ compiler 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, customize [`cmake.toml`](./cmake-toml) for your project and run 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` when necessary.
<sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub>
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional 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] Create a project.
gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build.
install Run cmake --install.
clean Clean the build directory.
help Show help.
version Current cmkr version.
```
## Credits
- [gulrak/filesystem](https://github.com/gulrak/filesystem)
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
- [mpark/variant](https://github.com/mpark/variant)
- [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer)
- [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
+83
View File
@@ -0,0 +1,83 @@
---
layout: page
title: Philosophy
nav_order: 2
published: false
---
# Philosophy
## Problem statement
Similar to writing good C++, writing good CMake is difficult. The main difference is that nobody actually wants to learn CMake. The build system is something that should "just work".
There have been many attempts at creating new build systems (with varying levels of success). Naturally this causes [competing standards](https://xkcd.com/927/), which is undesirable. CMake is pretty much the de facto standard, and it has seen extensive use in complex software projects.
One of the main issues of CMake is the turing-complete scripting language you use to describe your projects. As your project gets more complex this can be very helpful, but it also unnecessarily complicates simple projects. There have been discussions about a declarative language on the [CMake issue tracker](https://gitlab.kitware.com/cmake/cmake/-/issues/19891) to solve this problem, but it looks like a "LISP-like" language is seriously being considered...
## The solution
The way cmkr (pronounced "cmaker") solves this problem is by using [TOML](https://toml.io/). Below is a minimal `cmake.toml` project:
```toml
[project]
name = "cmkr_for_beginners"
[target.hello_world]
type = "executable"
sources = ["src/main.cpp"]
```
The key difference between `cmkr` and other build systems is that it _generates_ CMake. This means that your projects are fully compatible with the CMake ecosystem, and you can try it out without having to rewrite your whole build system.
TODO: link/include more examples? Talk about conditions, packaging (missing)
### Another layer?!
A common issue people have with cmkr is that it introduces an additional layer of indirection to your build system. CMake is already a meta-buildsystem, which means you could call cmkr a "meta-meta-buildsystem".
Presumably the reason for this friction is that additional layers of abstraction introduce additional complexity. Because of this cmkr has been designed to be completely seamless:
- The user doesn't have to install any additional software to use cmkr. All you need is a semi-recent version of CMake and a C++ compiler.
- Modifying `cmake.toml` automatically triggers a regeneration of `CMakeLists.txt`. There is no change to your build process.
- The `CMakeLists.txt` is generated to be human-readable. This means you can easily "eject" from cmkr and go back to CMake.
An additional argument for cmkr is that anecdotally people say it "just works". Because of its simplicity it's also easy to teach, even to people without programming experience.
There is also precedent in the JavaScript community. Bundlers and translators are the norm there and their developer experience is miles ahead of C++.
<sub>Not to say the JavaScript ecosystem is without its flaws, but generators does not appear to be one of them</sub>
### Unsupported features
Because cmkr is still in early in development there are many missing/unfinished features. It was decided that users can bridge the gap by including CMake at arbitrary locations.
This has the advantage that it forces complexity in the build system to be self-contained and modular, a problem all too common in projects as they age.
## Enterprise
Words like "bootstrapping" and "generating code" might worry engineers working in an enterprise environment, and rightly so. From the beginning it has been a priority to make cmkr suitable for use in big corporations with strict protocols for security and reproducibility.
### No additional dependencies
As mentioned above, the only thing you need is a working C++ compiler and a semi-recent version of CMake. It is assumed that you are already building (and executing) C++ projects on your servers, so cmkr does not introduce additional requirements.
All the logic for downloading and compiling the `cmkr` executable is self-contained in a ~250 line `cmkr.cmake` script. You can easily audit it and see if it's up to your standards.
### Reproducibility
Per default the `cmkr.cmake` bootstrapping script contains the exact version of cmkr used to generate your project. As long as the cmkr repository is available you will build the exact same version of cmkr, which will generate the exact same `CMakeLists.txt` file.
This also means that cmkr can decide to break backwards compatibility without affecting legacy projects. An effort will always be made to maintain backwards compatibility though.
### Integrity
As an additional safeguard you can modify `cmkr.cmake` to pin the version tag to a commit hash. This hash is checked to ensure the integrity of the upstream repository.
### Availability
You can easily point `cmkr.cmake` to a mirror of the cmkr repository to ensure availability should something catastrophic happen.
### Not executed in CI
The final (and key) feature is that the bootstrapping process is never executed in CI environments. This means `cmkr` is only ever executed on your developer's machines and not on your infrastructure.
View File
-2
View File
@@ -1,2 +0,0 @@
bundle exec just-the-docs rake search:init
bundle exec jekyll serve --livereload
+1 -1
View File
@@ -1,2 +1,2 @@
bundle install
bundle exec jekyll serve --force_polling --livereload --incremental
bundle exec jekyll serve --force_polling --livereload --incremental
-2
View File
@@ -7,5 +7,3 @@ const char *handle_args(int argc, char **argv);
} // namespace args
} // namespace cmkr
const char *cmkr_args_handle_args(int, char **);
-69
View File
@@ -1,69 +0,0 @@
// https://codereview.stackexchange.com/a/14315
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
// This is the type that will hold all the strings.
// Each enumeration type will declare its own specialization.
// Any enum that does not have a specialization will generate a compiler error
// indicating that there is no definition of this variable (as there should be
// be no definition of a generic version).
template <typename T>
struct enumStrings {
static char const *data[];
};
// This is a utility type.
// Created automatically. Should not be used directly.
template <typename T>
struct enumRefHolder {
T &enumVal;
enumRefHolder(T &enumVal) : enumVal(enumVal) {}
};
template <typename T>
struct enumConstRefHolder {
T const &enumVal;
enumConstRefHolder(T const &enumVal) : enumVal(enumVal) {}
};
// The next two functions do the actual work of reading/writing an
// enum as a string.
template <typename T>
std::ostream &operator<<(std::ostream &str, enumConstRefHolder<T> const &data) {
return str << enumStrings<T>::data[data.enumVal];
}
template <typename T>
std::istream &operator>>(std::istream &str, enumRefHolder<T> const &data) {
std::string value;
str >> value;
// These two can be made easier to read in C++11
// using std::begin() and std::end()
//
static auto begin = std::begin(enumStrings<T>::data);
static auto end = std::end(enumStrings<T>::data);
auto find = std::find(begin, end, value);
if (find != end) {
data.enumVal = static_cast<T>(std::distance(begin, find));
} else {
throw std::invalid_argument("");
}
return str;
}
// This is the public interface:
// use the ability of function to deduce their template type without
// being explicitly told to create the correct type of enumRefHolder<T>
template <typename T>
enumConstRefHolder<T> enumToString(T const &e) {
return enumConstRefHolder<T>(e);
}
template <typename T>
enumRefHolder<T> enumFromString(T &e) {
return enumRefHolder<T>(e);
}
+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
+57 -13
View File
@@ -3,27 +3,34 @@
#include <mpark/variant.hpp>
#include <string>
#include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
#include <vector>
namespace cmkr {
namespace parser {
struct Setting {
template <typename T>
using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>;
struct Variable {
std::string name;
std::string comment;
mpark::variant<bool, std::string> val;
std::string help;
mpark::variant<bool, std::string> value;
bool cache = false;
bool force = false;
};
struct Option {
std::string name;
std::string comment;
bool val = false;
std::string help;
bool value = false;
};
struct Package {
std::string name;
std::string condition;
std::string version;
bool required = true;
bool config = false;
@@ -33,7 +40,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 {
@@ -44,18 +57,17 @@ enum TargetType {
target_interface,
target_custom,
target_object,
target_template,
target_last,
};
template <typename T>
using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>;
extern const char *targetTypeNames[target_last];
struct Target {
std::string name;
TargetType type = {};
TargetType type = target_last;
std::string type_name;
ConditionVector headers;
ConditionVector sources;
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
@@ -93,8 +105,15 @@ struct Target {
ConditionVector include_after;
};
struct Template {
Target outline;
std::string add_function;
bool pass_sources_to_add_function = false;
};
struct Test {
std::string name;
std::string condition;
std::vector<std::string> configurations;
std::string working_directory;
std::string command;
@@ -102,16 +121,20 @@ struct Test {
};
struct Install {
std::string condition;
std::vector<std::string> targets;
std::vector<std::string> files;
std::vector<std::string> dirs;
std::vector<std::string> configs;
std::string destination;
std::string component;
bool optional = false;
};
struct Subdir {
std::string name;
std::string condition;
Condition<std::string> cmake_before;
Condition<std::string> cmake_after;
ConditionVector include_before;
@@ -120,10 +143,26 @@ struct Subdir {
struct Content {
std::string name;
std::string condition;
tsl::ordered_map<std::string, std::string> arguments;
Condition<std::string> cmake_before;
Condition<std::string> cmake_after;
ConditionVector include_before;
ConditionVector include_after;
};
enum MsvcRuntimeType {
msvc_dynamic,
msvc_static,
msvc_last,
};
extern const char *msvcRuntimeTypeNames[msvc_last];
struct Project {
const Project *parent;
// This is the CMake version required to use all cmkr versions.
std::string cmake_version = "3.15";
std::string cmkr_include = "cmkr.cmake";
@@ -141,15 +180,18 @@ struct Project {
std::string project_version;
std::string project_description;
std::vector<std::string> project_languages;
bool project_allow_unknown_languages = false;
MsvcRuntimeType project_msvc_runtime = msvc_last;
Condition<std::string> cmake_before;
Condition<std::string> cmake_after;
ConditionVector include_before;
ConditionVector include_after;
std::vector<Setting> settings;
std::vector<Variable> variables;
std::vector<Option> options;
std::vector<Package> packages;
Vcpkg vcpkg;
std::vector<Content> contents;
std::vector<Template> templates;
std::vector<Target> targets;
std::vector<Test> tests;
std::vector<Install> installs;
@@ -157,6 +199,8 @@ struct Project {
std::vector<Subdir> subdirs;
Project(const Project *parent, const std::string &path, bool build);
const Project *root() const;
bool cmake_minimum_version(int major, int minor) const;
};
bool is_root_path(const std::string &path);
+3 -13
View File
@@ -37,17 +37,17 @@ const char *handle_args(int argc, char **argv) {
} else if (main_arg == "build") {
auto ret = build::run(argc, argv);
if (ret)
return "CMake build error!";
throw std::runtime_error("CMake build failed!");
return "CMake build completed!";
} else if (main_arg == "install") {
auto ret = build::install();
if (ret)
return "CMake install error!";
throw std::runtime_error("CMake install failed!");
return "CMake install completed!";
} else if (main_arg == "clean") {
auto ret = build::clean();
if (ret)
return "CMake clean error!";
throw std::runtime_error("CMake clean failed!");
return "Cleaned build directory!";
} else {
throw std::runtime_error(cmkr::help::message());
@@ -55,13 +55,3 @@ const char *handle_args(int argc, char **argv) {
}
} // namespace args
} // namespace cmkr
const char *cmkr_args_handle_args(int argc, char **argv) {
try {
return cmkr::args::handle_args(argc, argv);
} catch (const std::exception &e) {
return e.what();
} catch (...) {
return "Unknown error!";
}
}
+521 -196
View File
File diff suppressed because it is too large Load Diff
+10 -4
View File
@@ -1,15 +1,21 @@
#include "error.hpp"
#include <cassert>
#include <cstddef>
namespace cmkr {
namespace error {
Status::Status(Code ec) noexcept : ec_(ec) {}
Status::Status(Code ec) noexcept : ec_(ec) {
}
Status::operator int() const noexcept { return static_cast<int>(ec_); }
Status::operator int() const noexcept {
return static_cast<int>(ec_);
}
Status::Code Status::code() const noexcept { return ec_; }
Status::Code Status::code() const noexcept {
return ec_;
}
} // namespace error
} // namespace cmkr
@@ -20,6 +26,6 @@ static const char *err_string[] = {
};
const char *cmkr_error_status(int i) {
assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string))));
assert(i >= 0 && static_cast<size_t>(i) < (sizeof(err_string) / sizeof(*(err_string))));
return err_string[i];
}
+9 -3
View File
@@ -4,7 +4,9 @@
namespace cmkr {
namespace help {
const char *version() noexcept { return "cmkr version " CMKR_VERSION; }
const char *version() noexcept {
return "cmkr version " CMKR_VERSION;
}
const char *message() noexcept {
return R"lit(
@@ -22,6 +24,10 @@ arguments:
} // namespace help
} // namespace cmkr
const char *cmkr_help_version(void) { return cmkr::help::version(); }
const char *cmkr_help_version(void) {
return cmkr::help::version();
}
const char *cmkr_help_message(void) { return cmkr::help::message(); }
const char *cmkr_help_message(void) {
return cmkr::help::message();
}
+412 -148
View File
@@ -1,48 +1,46 @@
#include "project_parser.hpp"
#include "enum_helper.hpp"
#include "fs.hpp"
#include <deque>
#include <stdexcept>
#include <toml.hpp>
#include <tsl/ordered_map.h>
template <>
const char *enumStrings<cmkr::parser::TargetType>::data[] = {"executable", "library", "shared", "static", "interface", "custom", "object"};
namespace cmkr {
namespace parser {
using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>;
const char *targetTypeNames[target_last] = {"executable", "library", "shared", "static", "interface", "custom", "object", "template"};
template <typename EnumType>
static EnumType to_enum(const std::string &str, const std::string &help_name) {
EnumType value;
try {
std::stringstream ss(str);
ss >> enumFromString(value);
} catch (std::invalid_argument &) {
std::string supported;
for (const auto &s : enumStrings<EnumType>::data) {
if (!supported.empty()) {
supported += ", ";
}
supported += s;
static TargetType parse_targetType(const std::string &name) {
for (int i = 0; i < target_last; i++) {
if (name == targetTypeNames[i]) {
return static_cast<TargetType>(i);
}
throw std::runtime_error("Unknown " + help_name + "'" + str + "'! Supported types are: " + supported);
}
return value;
return target_last;
}
static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
const char *msvcRuntimeTypeNames[msvc_last] = {"dynamic", "static"};
static MsvcRuntimeType parse_msvcRuntimeType(const std::string &name) {
for (int i = 0; i < msvc_last; i++) {
if (name == msvcRuntimeTypeNames[i]) {
return static_cast<MsvcRuntimeType>(i);
}
}
return msvc_last;
}
using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>;
static std::string format_key_message(const std::string &message, const toml::key &ky, const TomlBasicValue &value) {
auto loc = value.location();
auto line_number_str = std::to_string(loc.line());
auto line_width = line_number_str.length();
auto line_str = loc.line_str();
std::ostringstream oss;
oss << "[error] " << error << '\n';
oss << " --> " << loc.file_name() << '\n';
oss << message << "\n";
oss << " --> " << loc.file_name() << ':' << loc.line() << '\n';
oss << std::string(line_width + 2, ' ') << "|\n";
oss << ' ' << line_number_str << " | " << line_str << '\n';
@@ -55,19 +53,28 @@ static std::string format_key_error(const std::string &error, const toml::key &k
if (key_start != std::string::npos) {
oss << std::string(key_start + 1, ' ') << std::string(ky.length(), '~');
}
oss << '\n';
return oss.str();
}
static void throw_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
throw std::runtime_error(format_key_message("[error] " + error, ky, value));
}
static void print_key_warning(const std::string &message, const toml::key &ky, const TomlBasicValue &value) {
puts(format_key_message("[warning] " + message, ky, value).c_str());
}
class TomlChecker {
const TomlBasicValue &m_v;
tsl::ordered_map<toml::key, bool> m_visited;
tsl::ordered_map<toml::key, bool> m_conditionVisited;
tsl::ordered_set<toml::key> m_visited;
tsl::ordered_set<toml::key> m_conditionVisited;
public:
TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {}
TomlChecker(const TomlBasicValue &v) : m_v(v) {}
TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {
}
TomlChecker(const TomlBasicValue &v) : m_v(v) {
}
TomlChecker(const TomlChecker &) = delete;
TomlChecker(TomlChecker &&) = delete;
@@ -90,7 +97,7 @@ class TomlChecker {
// Handle visiting logic
for (const auto &itr : destination) {
if (!itr.first.empty()) {
m_conditionVisited.emplace(itr.first, true);
m_conditionVisited.emplace(itr.first);
}
}
visit(ky);
@@ -121,44 +128,63 @@ class TomlChecker {
return toml::find(m_v, ky);
}
void visit(const toml::key &ky) { m_visited.emplace(ky, true); }
void visit(const toml::key &ky) {
m_visited.emplace(ky);
}
bool visisted(const toml::key &ky) const {
return m_visited.contains(ky);
}
void check(const tsl::ordered_map<std::string, std::string> &conditions) const {
for (const auto &itr : m_v.as_table()) {
const auto &ky = itr.first;
if (m_conditionVisited.contains(ky)) {
if (!conditions.contains(ky)) {
throw std::runtime_error(format_key_error("Unknown condition '" + ky + "'", ky, itr.second));
throw_key_error("Unknown condition '" + ky + "'", ky, itr.second);
}
for (const auto &jtr : itr.second.as_table()) {
if (!m_visited.contains(jtr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second));
throw_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second);
}
}
} else if (!m_visited.contains(ky)) {
if (itr.second.is_table()) {
for (const auto &jtr : itr.second.as_table()) {
if (!m_visited.contains(jtr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second));
throw_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second);
}
}
}
throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second));
throw_key_error("Unknown key '" + ky + "'", ky, itr.second);
} else if (ky == "condition") {
std::string condition = itr.second.as_string();
if (!conditions.contains(condition)) {
throw_key_error("Unknown condition '" + condition + "'", condition, itr.second);
}
}
}
}
};
class TomlCheckerRoot {
const TomlBasicValue &m_root;
std::deque<TomlChecker> m_checkers;
tsl::ordered_set<toml::key> m_visisted;
bool m_checked = false;
public:
TomlCheckerRoot() = default;
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
}
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
bool contains(const toml::key &ky) {
m_visisted.emplace(ky);
return m_root.contains(ky);
}
TomlChecker &create(const TomlBasicValue &v) {
m_checkers.emplace_back(v);
return m_checkers.back();
@@ -169,29 +195,39 @@ class TomlCheckerRoot {
return m_checkers.back();
}
void check(const tsl::ordered_map<std::string, std::string> &conditions) {
void check(const tsl::ordered_map<std::string, std::string> &conditions, bool check_root) {
if (check_root) {
for (const auto &itr : m_root.as_table()) {
if (!m_visisted.contains(itr.first)) {
throw_key_error("Unknown key '" + itr.first + "'", itr.first, itr.second);
}
}
}
for (const auto &checker : m_checkers) {
checker.check(conditions);
}
}
};
Project::Project(const Project *parent, const std::string &path, bool build) {
Project::Project(const Project *parent, const std::string &path, bool build) : parent(parent) {
const auto toml_path = fs::path(path) / "cmake.toml";
if (!fs::exists(toml_path)) {
throw std::runtime_error("No cmake.toml was found!");
throw std::runtime_error("File not found '" + toml_path.string() + "'");
}
const auto toml = toml::parse<toml::discard_comments, tsl::ordered_map, std::vector>(toml_path.string());
if (toml.size() == 0) {
throw std::runtime_error("Empty TOML '" + toml_path.string() + "'");
}
TomlCheckerRoot checker;
TomlCheckerRoot checker(toml);
if (toml.contains("cmake")) {
if (checker.contains("cmake")) {
auto &cmake = checker.create(toml, "cmake");
cmake.required("version", cmake_version);
if (cmake.contains("bin-dir")) {
throw std::runtime_error("bin-dir has been renamed to build-dir");
throw_key_error("bin-dir has been renamed to build-dir", "bin-dir", cmake.find("bin-dir"));
}
cmake.optional("build-dir", build_dir);
@@ -217,7 +253,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
// Skip the rest of the parsing when building
if (build) {
checker.check(conditions);
checker.check(conditions, false);
return;
}
@@ -229,33 +265,54 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake";
conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake";
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["clang"] =
R"cmake((CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$") OR (CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$"))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;
templates = parent->templates;
}
if (toml.contains("conditions")) {
if (checker.contains("conditions")) {
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
for (const auto &cond : conds) {
conditions[cond.first] = cond.second;
}
}
if (toml.contains("project")) {
if (checker.contains("project")) {
auto &project = checker.create(toml, "project");
project.required("name", project_name);
project.optional("version", project_version);
project.optional("description", project_description);
project.optional("languages", project_languages);
project.optional("allow-unknown-languages", project_allow_unknown_languages);
project.optional("cmake-before", cmake_before);
project.optional("cmake-after", cmake_after);
project.optional("include-before", include_before);
project.optional("include-after", include_after);
project.optional("subdirs", project_subdirs);
std::string msvc_runtime;
project.optional("msvc-runtime", msvc_runtime);
if (!msvc_runtime.empty()) {
project_msvc_runtime = parse_msvcRuntimeType(msvc_runtime);
if (project_msvc_runtime == msvc_last) {
std::string error = "Unknown runtime '" + msvc_runtime + "'\n";
error += "Available types:\n";
for (std::string type_name : msvcRuntimeTypeNames) {
error += " - " + type_name + "\n";
}
error.pop_back(); // Remove last newline
throw_key_error(error, msvc_runtime, project.find("msvc-runtime"));
}
}
}
if (toml.contains("subdir")) {
if (checker.contains("subdir")) {
const auto &subs = toml::find(toml, "subdir").as_table();
for (const auto &itr : subs) {
Subdir subdir;
@@ -272,36 +329,46 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
}
}
if (toml.contains("settings")) {
if (checker.contains("variables")) {
using set_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &sets = toml::find<set_map>(toml, "settings");
for (const auto &itr : sets) {
Setting s;
auto vars = toml::find<set_map>(toml, "variables");
if (checker.contains("settings")) {
print_key_warning("[settings] has been renamed to [variables]", "settings", toml.at("settings"));
const auto &sets = toml::find<set_map>(toml, "settings");
for (const auto &itr : sets) {
if (!vars.insert(itr).second) {
throw_key_error("Key '" + itr.first + "' shadows existing variable", itr.first, itr.second);
}
}
}
for (const auto &itr : vars) {
Variable s;
s.name = itr.first;
const auto &value = itr.second;
if (value.is_boolean()) {
s.val = value.as_boolean();
s.value = value.as_boolean();
} else if (value.is_string()) {
s.val = value.as_string();
s.value = value.as_string();
} else {
auto &setting = checker.create(value);
setting.optional("comment", s.comment);
setting.optional("help", s.help);
if (setting.contains("value")) {
const auto &v = setting.find("value");
if (v.is_boolean()) {
s.val = v.as_boolean();
s.value = v.as_boolean();
} else {
s.val = v.as_string();
s.value = v.as_string();
}
}
setting.optional("cache", s.cache);
setting.optional("force", s.force);
}
settings.push_back(s);
variables.push_back(s);
}
}
if (toml.contains("options")) {
if (checker.contains("options")) {
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &opts = toml::find<opts_map>(toml, "options");
for (const auto &itr : opts) {
@@ -309,17 +376,18 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
o.name = itr.first;
const auto &value = itr.second;
if (value.is_boolean()) {
o.val = value.as_boolean();
o.value = value.as_boolean();
} else {
auto &option = checker.create(value);
option.optional("comment", o.comment);
option.optional("value", o.val);
option.optional("help", o.help);
option.optional("value", o.value);
}
options.push_back(o);
conditions.emplace(o.name, o.name);
}
}
if (toml.contains("find-package")) {
if (checker.contains("find-package")) {
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
for (const auto &itr : pkgs) {
@@ -330,6 +398,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);
@@ -339,136 +408,281 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
}
}
// TODO: perform checking here
if (toml.contains("fetch-content")) {
if (checker.contains("fetch-content")) {
const auto &fc = toml::find(toml, "fetch-content").as_table();
for (const auto &itr : fc) {
Content content;
content.name = itr.first;
auto &c = checker.create(itr.second);
c.optional("condition", content.condition);
c.optional("cmake-before", content.cmake_before);
c.optional("cmake-after", content.cmake_after);
c.optional("include-before", content.include_before);
c.optional("include-after", content.include_after);
for (const auto &argItr : itr.second.as_table()) {
std::string value;
if (argItr.second.is_array()) {
for (const auto &list_val : argItr.second.as_array()) {
if (!value.empty()) {
value += ';';
}
value += list_val.as_string();
}
} else if (argItr.second.is_boolean()) {
value = argItr.second.as_boolean() ? "ON" : "OFF";
} else {
value = argItr.second.as_string();
}
auto is_cmake_arg = [](const std::string &s) {
for (auto c : s) {
if (!(std::isdigit(c) || std::isupper(c) || c == '_')) {
return false;
}
}
return true;
};
// https://cmake.org/cmake/help/latest/command/string.html#supported-hash-algorithms
tsl::ordered_set<std::string> hash_algorithms = {
"md5", "sha1", "sha224", "sha256", "sha384", "sha512", "sha3_224", "sha3_256", "sha3_384", "sha3_512",
};
auto key = argItr.first;
if (key == "git") {
key = "GIT_REPOSITORY";
} else if (key == "tag") {
key = "GIT_TAG";
} else if (key == "shallow") {
key = "GIT_SHALLOW";
} else if (key == "svn") {
key = "SVN_REPOSITORY";
} else if (key == "rev") {
key = "SVN_REVISION";
} else if (key == "url") {
key = "URL";
} else if (hash_algorithms.contains(key)) {
std::string algo;
for (auto c : key) {
algo.push_back(std::toupper(c));
}
key = "URL_HASH";
value = algo + "=" + value;
} else if (key == "hash") {
key = "URL_HASH";
} else {
// don't change arg
} else if (is_cmake_arg(key)) {
// allow passthrough of ExternalProject options
} else if (!c.visisted(key)) {
throw_key_error("Unknown key '" + argItr.first + "'", argItr.first, argItr.second);
}
content.arguments.emplace(key, argItr.second.as_string());
// Make sure not to emit keys like "condition" in the FetchContent call
if (!c.visisted(key)) {
content.arguments.emplace(key, value);
}
c.visit(argItr.first);
}
contents.emplace_back(std::move(content));
}
}
if (toml.contains("bin")) {
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
if (checker.contains("bin")) {
throw_key_error("[[bin]] has been renamed to [target.<name>]", "", toml.at("bin"));
}
if (toml.contains("target")) {
const auto &ts = toml::find(toml, "target").as_table();
auto parse_target = [&](const std::string &name, TomlChecker &t, bool isTemplate) {
Target target;
target.name = name;
for (const auto &itr : ts) {
const auto &value = itr.second;
t.required("type", target.type_name);
target.type = parse_targetType(target.type_name);
Target target;
target.name = itr.first;
// Users cannot set this target type
if (target.type == target_template) {
target.type = target_last;
}
auto &t = checker.create(value);
std::string type;
t.required("type", type);
target.type = to_enum<TargetType>(type, "target type");
t.optional("headers", target.headers);
t.optional("sources", target.sources);
t.optional("compile-definitions", target.compile_definitions);
t.optional("private-compile-definitions", target.private_compile_definitions);
t.optional("compile-features", target.compile_features);
t.optional("private-compile-features", target.private_compile_features);
t.optional("compile-options", target.compile_options);
t.optional("private-compile-options", target.private_compile_options);
t.optional("include-directories", target.include_directories);
t.optional("private-include-directories", target.private_include_directories);
t.optional("link-directories", target.link_directories);
t.optional("private-link-directories", target.private_link_directories);
t.optional("link-libraries", target.link_libraries);
t.optional("private-link-libraries", target.private_link_libraries);
t.optional("link-options", target.link_options);
t.optional("private-link-options", target.private_link_options);
t.optional("precompile-headers", target.precompile_headers);
t.optional("private-precompile-headers", target.private_precompile_headers);
if (!target.headers.empty()) {
auto &sources = target.sources.nth(0).value();
const auto &headers = target.headers.nth(0)->second;
sources.insert(sources.end(), headers.begin(), headers.end());
if (!isTemplate && target.type == target_last) {
for (const auto &tmplate : templates) {
if (target.type_name == tmplate.outline.name) {
target.type = target_template;
break;
}
}
}
t.optional("condition", target.condition);
t.optional("alias", target.alias);
if (target.type == target_last) {
std::string error = "Unknown target type '" + target.type_name + "'\n";
error += "Available types:\n";
for (std::string type_name : targetTypeNames) {
if (type_name != "template") {
error += " - " + type_name + "\n";
}
}
if (!isTemplate && !templates.empty()) {
error += "Available templates:\n";
for (const auto &tmplate : templates) {
error += " - " + tmplate.outline.name + "\n";
}
}
error.pop_back(); // Remove last newline
throw_key_error(error, target.type_name, t.find("type"));
}
if (t.contains("properties")) {
auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) {
if (v.is_array()) {
std::string property_list;
for (const auto &list_val : v.as_array()) {
if (!property_list.empty()) {
property_list += ';';
}
property_list += list_val.as_string();
t.optional("sources", target.sources);
// Merge the headers into the sources
ConditionVector headers;
t.optional("headers", headers);
for (const auto &itr : headers) {
auto &dest = target.sources[itr.first];
for (const auto &jtr : itr.second) {
dest.push_back(jtr);
}
}
t.optional("compile-definitions", target.compile_definitions);
t.optional("private-compile-definitions", target.private_compile_definitions);
t.optional("compile-features", target.compile_features);
t.optional("private-compile-features", target.private_compile_features);
t.optional("compile-options", target.compile_options);
t.optional("private-compile-options", target.private_compile_options);
t.optional("include-directories", target.include_directories);
t.optional("private-include-directories", target.private_include_directories);
t.optional("link-directories", target.link_directories);
t.optional("private-link-directories", target.private_link_directories);
t.optional("link-libraries", target.link_libraries);
t.optional("private-link-libraries", target.private_link_libraries);
t.optional("link-options", target.link_options);
t.optional("private-link-options", target.private_link_options);
t.optional("precompile-headers", target.precompile_headers);
t.optional("private-precompile-headers", target.private_precompile_headers);
Condition<std::string> msvc_runtime;
t.optional("msvc-runtime", msvc_runtime);
for (const auto &condItr : msvc_runtime) {
switch (parse_msvcRuntimeType(condItr.second)) {
case msvc_dynamic:
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
break;
case msvc_static:
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
break;
default: {
std::string error = "Unknown runtime '" + condItr.second + "'\n";
error += "Available types:\n";
for (std::string type_name : msvcRuntimeTypeNames) {
error += " - " + type_name + "\n";
}
error.pop_back(); // Remove last newline
const TomlBasicValue *report;
if (condItr.first.empty()) {
report = &t.find("msvc-runtime");
} else {
report = &t.find(condItr.first).as_table().find("msvc-runtime").value();
}
throw_key_error(error, condItr.second, *report);
}
}
}
t.optional("condition", target.condition);
t.optional("alias", target.alias);
if (t.contains("properties")) {
auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) {
if (v.is_array()) {
std::string property_list;
for (const auto &list_val : v.as_array()) {
if (!property_list.empty()) {
property_list += ';';
}
target.properties[condition][k] = property_list;
} else if (v.is_boolean()) {
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
} else {
target.properties[condition][k] = v.as_string();
property_list += list_val.as_string();
}
};
target.properties[condition][k] = property_list;
} else if (v.is_boolean()) {
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
} else {
target.properties[condition][k] = v.as_string();
}
};
const auto &props = t.find("properties").as_table();
for (const auto &propKv : props) {
const auto &k = propKv.first;
const auto &v = propKv.second;
if (v.is_table()) {
for (const auto &condKv : v.as_table()) {
store_property(condKv.first, condKv.second, k);
}
} else {
store_property(k, v, "");
const auto &props = t.find("properties").as_table();
for (const auto &propKv : props) {
const auto &k = propKv.first;
const auto &v = propKv.second;
if (v.is_table()) {
for (const auto &condKv : v.as_table()) {
store_property(condKv.first, condKv.second, k);
}
} else {
store_property(k, v, "");
}
}
}
t.optional("cmake-before", target.cmake_before);
t.optional("cmake-after", target.cmake_after);
t.optional("include-before", target.include_before);
t.optional("include-after", target.include_after);
return target;
};
if (checker.contains("template")) {
const auto &ts = toml::find(toml, "template").as_table();
for (const auto &itr : ts) {
auto &t = checker.create(itr.second);
const auto &name = itr.first;
for (const auto &type_name : targetTypeNames) {
if (name == type_name) {
throw_key_error("Reserved template name '" + name + "'", name, itr.second);
}
}
t.optional("cmake-before", target.cmake_before);
t.optional("cmake-after", target.cmake_after);
t.optional("include-before", target.include_before);
t.optional("include-after", target.include_after);
for (const auto &tmplate : templates) {
if (name == tmplate.outline.name) {
throw_key_error("Template '" + name + "' already defined", name, itr.second);
}
}
targets.push_back(target);
Template tmplate;
tmplate.outline = parse_target(name, t, true);
t.optional("add-function", tmplate.add_function);
t.optional("pass-sources-to-add-function", tmplate.pass_sources_to_add_function);
t.optional("pass-sources", tmplate.pass_sources_to_add_function);
templates.push_back(tmplate);
}
}
if (toml.contains("test")) {
if (checker.contains("target")) {
const auto &ts = toml::find(toml, "target").as_table();
for (const auto &itr : ts) {
auto &t = checker.create(itr.second);
targets.push_back(parse_target(itr.first, t, false));
}
}
if (checker.contains("test")) {
const auto &ts = toml::find(toml, "test").as_array();
for (const auto &value : ts) {
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);
@@ -477,28 +691,78 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
}
}
if (toml.contains("install")) {
if (checker.contains("install")) {
const auto &is = toml::find(toml, "install").as_array();
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);
i.optional("configs", inst.configs);
i.required("destination", inst.destination);
i.optional("component", inst.component);
i.optional("optional", inst.optional);
installs.push_back(inst);
}
}
if (toml.contains("vcpkg")) {
if (checker.contains("vcpkg")) {
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_key_error("Invalid package name '" + package_str + "'", "packages", p);
}
vcpkg.packages.emplace_back(std::move(package));
}
}
checker.check(conditions);
checker.check(conditions, true);
}
const Project *Project::root() const {
auto root = this;
while (root->parent != nullptr)
root = root->parent;
return root;
}
bool Project::cmake_minimum_version(int major, int minor) const {
// NOTE: this code is like pulling teeth, sorry
auto root_version = root()->cmake_version;
puts(root_version.c_str());
auto range_index = root_version.find("...");
if (range_index != std::string::npos) {
root_version.resize(range_index);
}
auto period_index = root_version.find('.');
auto root_major = atoi(root_version.substr(0, period_index).c_str());
int root_minor = 0;
if (period_index != std::string::npos) {
auto end_index = root_version.find('.', period_index + 1);
root_minor = atoi(root_version.substr(period_index + 1, end_index).c_str());
}
return std::tie(root_major, root_minor) >= std::tie(major, minor);
}
bool is_root_path(const std::string &path) {
+29 -7
View File
@@ -14,7 +14,7 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/basic"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
@@ -24,7 +24,7 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/interface"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
@@ -34,7 +34,7 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/fetch-content"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
@@ -44,7 +44,7 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/conditions"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
@@ -54,7 +54,7 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/vcpkg"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
@@ -64,7 +64,7 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/cxx-standard"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
@@ -74,7 +74,29 @@ add_test(
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/globbing"
COMMAND
$<TARGET_FILE:cmkr>
"$<TARGET_FILE:cmkr>"
build
)
add_test(
NAME
templates
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/templates"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
if(MSVC) # msvc
add_test(
NAME
msvc-runtime
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/msvc-runtime"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
endif()
+20 -7
View File
@@ -1,41 +1,54 @@
[[test]]
name = "basic"
command = "$<TARGET_FILE:cmkr>"
working-directory = "basic"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "interface"
command = "$<TARGET_FILE:cmkr>"
working-directory = "interface"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "fetch-content"
command = "$<TARGET_FILE:cmkr>"
working-directory = "fetch-content"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "conditions"
command = "$<TARGET_FILE:cmkr>"
working-directory = "conditions"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "vcpkg"
command = "$<TARGET_FILE:cmkr>"
working-directory = "vcpkg"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "cxx-standard"
command = "$<TARGET_FILE:cmkr>"
working-directory = "cxx-standard"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "globbing"
command = "$<TARGET_FILE:cmkr>"
working-directory = "globbing"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "templates"
working-directory = "templates"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
condition = "msvc"
name = "msvc-runtime"
working-directory = "msvc-runtime"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
+1
View File
@@ -5,6 +5,7 @@ description = "Header-only library"
[target.mylib]
type = "interface"
include-directories = ["include"]
compile-features = ["cxx_std_11"]
[target.example]
type = "executable"
+2 -2
View File
@@ -4,5 +4,5 @@
int main()
{
printf("mylib version: %s\n", mylib::version())
}
printf("mylib version: %s\n", mylib::version());
}
+15
View File
@@ -0,0 +1,15 @@
[project]
name = "msvc-runtime"
description = "Static MSVC runtime"
msvc-runtime = "static"
# This target will compile with a static runtime
[target.static-runtime]
type = "executable"
sources = ["src/main.cpp"]
# This target overrides the [project].msvc-runtime
[target.dynamic-runtime]
type = "executable"
sources = ["src/main.cpp"]
msvc-runtime = "dynamic"
+5
View File
@@ -0,0 +1,5 @@
#include <cstdio>
int main() {
puts("Hello from cmkr(msvc-static)!");
}
+25
View File
@@ -0,0 +1,25 @@
# To avoid repeating yourself in targets you can create your own target type (template). All properties of the template are inherited when used as a target type.
[project]
name = "templates"
description = "Target templates"
[template.app]
type = "executable"
sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties
[template.app.properties]
CXX_STANDARD = "11"
CXX_STANDARD_REQUIRED = true
[target.app-a]
type = "app"
compile-definitions = ["APP_A"]
[target.app-b]
type = "app"
compile-definitions = ["APP_B"]
# **Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
+13
View File
@@ -0,0 +1,13 @@
#include <cstdio>
#if !defined(IS_APP)
#error Something went wrong with the template
#endif // IS_APP
int main() {
#if defined(APP_A)
puts("Hello from app A!");
#elif defined(APP_B)
puts("Hello from app B!");
#endif
}
+3 -2
View File
@@ -7,7 +7,7 @@ description = "Dependencies from vcpkg"
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
# See https://vcpkg.io/en/packages.html for available packages
[vcpkg]
version = "2021.05.12"
version = "2022.11.14"
packages = ["fmt"]
[find-package]
@@ -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]`.