mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39b4522bc5 | |||
| da4ec26df0 | |||
| 31f77ab328 | |||
| 6d41c92e37 | |||
| 411eb03db4 | |||
| 9f80ce4429 | |||
| 8650f53174 | |||
| 6cb1009c0e | |||
| 26acf23989 | |||
| 15886fc340 | |||
| d5e34b0bcc | |||
| 916ae11cae | |||
| eaa361611a | |||
| 2300612ba9 | |||
| 25f015bc83 | |||
| fc674b4c6b | |||
| 433dbb0500 | |||
| 4772a2b683 | |||
| fb4931df1f | |||
| e96cd30217 | |||
| 2db8d1ee1d | |||
| 645737afd4 | |||
| 98c2e962c0 | |||
| 438feccf0f | |||
| 80cc29b8e1 | |||
| 1a62034a8a | |||
| c12472cd8f | |||
| 228553d43a | |||
| 8f4b376e42 | |||
| fcbf937c49 | |||
| e609c8a48f | |||
| 735898dbac | |||
| f7126b0158 | |||
| b6702714eb | |||
| efa4f716c2 | |||
| 76b7b8e778 | |||
| 501b26ced5 | |||
| 5bc36275ad | |||
| 35c837a181 | |||
| 710dfa4963 | |||
| c49ee6bafe | |||
| 368273bab2 | |||
| ffe08ae60c | |||
| b1249af365 | |||
| ea2ff31b5a | |||
| a32eb66df4 | |||
| 9b85833240 | |||
| ccb3d0b9ef | |||
| 49cdab39bd | |||
| e318139314 | |||
| 0f2a9a55fb | |||
| 38cec2d5d1 | |||
| 3caf593438 | |||
| c94dbd6416 | |||
| a1eaf601c6 | |||
| 10320c8e55 | |||
| 45e12c8d98 | |||
| e12ebc060d | |||
| c6e3fc159f | |||
| dc6f306d9c | |||
| e08bd5a311 | |||
| 068977e2f5 | |||
| 73147747a4 | |||
| a64fa9cc34 | |||
| eb6ccb39ee | |||
| 91dd8ce51f | |||
| efbfca27bd | |||
| 78bcf2acf7 | |||
| 83559cc5d4 | |||
| 5d85cc83e3 | |||
| dea41d80b5 | |||
| b52cf5b241 | |||
| e9480a625f | |||
| 13a7a8788a | |||
| 45ca74099c | |||
| ae3d73896e | |||
| 57042565d1 | |||
| d02ee3ed24 | |||
| 4a15d2ba55 | |||
| 87c0295f93 | |||
| a608e6b778 | |||
| 523f5f4a8a | |||
| 510cf71af5 | |||
| 87d90138b4 | |||
| 50615e0c27 | |||
| 42ed3048b3 | |||
| b0a1d79a42 | |||
| 5882bc082d | |||
| a90b3c6095 | |||
| 0a887c08f8 | |||
| 9f2cfe4083 | |||
| 15ad1dc6a7 | |||
| dabffeaed9 | |||
| 59fe5c38b8 | |||
| dcdc7ac410 | |||
| 88377a8bfb | |||
| 3a9685be82 | |||
| 5576789c18 | |||
| a362978f99 | |||
| a2f33297e4 | |||
| 674c976647 | |||
| 714a666e88 | |||
| d613e433d5 | |||
| 7c828d8740 | |||
| 559f750c89 | |||
| 4b6b72874e | |||
| 6bffa7cc71 | |||
| f415b432ed | |||
| 6c46664336 | |||
| f32aac63aa | |||
| 33d4cc4156 | |||
| 9288c8a87d | |||
| e36eee4420 | |||
| 53820c9f65 | |||
| ff7e4b8f23 | |||
| 1072cb44e2 | |||
| 926fc15fa2 | |||
| e2c929adb0 | |||
| 0dec7ca4d5 | |||
| 45e346b4a3 | |||
| 7165986190 | |||
| be0ef6d615 | |||
| d607b9028c | |||
| 099b14552c | |||
| 771c80a41a | |||
| 58c7de1d60 | |||
| a61e4bb999 | |||
| 47aaeaf8ce | |||
| 62609a0088 | |||
| e0d8a085db | |||
| 56da4144ae | |||
| 9388b3f29d | |||
| 079644cfeb | |||
| a7ca9f04c0 | |||
| 339c2aac94 | |||
| c573c9a76d | |||
| b24a0a2fdc | |||
| 854e8817c4 | |||
| 1c2947b2fc | |||
| 19e61aa1aa | |||
| bfa1e1fe0d | |||
| 56bd78f7ad | |||
| 4362b3547f | |||
| 08204d9847 | |||
| 2202aa8173 | |||
| d3829fb6cd | |||
| 3fb095210a | |||
| 9f99365a25 | |||
| be387141b5 | |||
| 09ded38f70 | |||
| ad90b1ad3e | |||
| 5bfa03d311 | |||
| bdd9d393d6 | |||
| 1deaec2b41 | |||
| cfee3bbc14 | |||
| bcaa60cabe | |||
| 7b1d33395e | |||
| 240fafc102 | |||
| 7a7ddf2699 | |||
| 605a04e72d | |||
| fd7f078127 | |||
| ee0e0d71d9 | |||
| 9d6897b572 | |||
| f8c34e5545 | |||
| e6d783ea20 | |||
| ee7fe38a7b | |||
| 66b2aad843 | |||
| d2e376080a | |||
| 634908c651 | |||
| 33911f3f1b | |||
| e650073bef | |||
| 96e44f7771 | |||
| cb9ad5cdb3 | |||
| c785b540d3 | |||
| 275b73eefa |
+3
-2
@@ -13,13 +13,14 @@ AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
@@ -72,7 +73,7 @@ PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
|
||||
[*.{c,h,cpp,hpp,toml}]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
|
||||
[{CMakeLists.txt, *.cmake}]
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
insert_final_newline = true
|
||||
|
||||
# Exclude the third_party folder
|
||||
[/third_party/**]
|
||||
charset = unset
|
||||
end_of_line = unset
|
||||
insert_final_newline = unset
|
||||
trim_trailing_whitespace = unset
|
||||
indent_style = unset
|
||||
indent_size = unset
|
||||
@@ -1,21 +1,25 @@
|
||||
name: CMake
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
cmake:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-2019, macos-10.15, ubuntu-20.04]
|
||||
os: [windows-2022, macos-latest, ubuntu-22.04]
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
BUILD_TYPE: 'Release'
|
||||
CMAKE_GENERATOR: 'Ninja'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Visual Studio Development Environment
|
||||
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.1
|
||||
|
||||
- name: Tag cmkr.cmake
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
@@ -38,27 +42,27 @@ jobs:
|
||||
ctest -C ${{ env.BUILD_TYPE }} --verbose
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}-${{ matrix.os }}
|
||||
path: install/bin/*
|
||||
|
||||
- name: Get lowercase OS name
|
||||
id: osname
|
||||
uses: ASzc/change-string-case-action@07c1e24a97f0951e13f88870b99c058fcf0b14cf # v5
|
||||
uses: ASzc/change-string-case-action@ccb130a4e483d3e86287289183704dc9bf53e77e # master 2024-04-05
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
string: ${{ runner.os }}
|
||||
|
||||
- name: Compress artifacts
|
||||
uses: vimtor/action-zip@26a249fb00d43ca98dad77a4b3838025fc226aa1 # v1.1
|
||||
uses: vimtor/action-zip@1379ea20d4c5705669ba81fd626dd01b1c738f26 # v1.2
|
||||
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@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: lint
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: clang-format
|
||||
id: clang-format
|
||||
uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2
|
||||
with:
|
||||
exclude: './third_party'
|
||||
extensions: 'c,h,cpp,hpp'
|
||||
clangFormatVersion: 15
|
||||
style: file
|
||||
|
||||
- name: clang-format instructions
|
||||
if: ${{ failure() && steps.clang-format.outcome == 'failure' }}
|
||||
run: |
|
||||
# Instructions for fixing the formatting errors
|
||||
echo -e "\n\033[0;31mTo fix the formatting, run:\nclang-format -style=file -i \$(git ls-files \"*.c\" \"*.h\" \"*.cpp\" \"*.hpp\")\033[0m\n"
|
||||
exit 1
|
||||
|
||||
editorconfig:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run editorconfig-checker
|
||||
uses: editorconfig-checker/action-editorconfig-checker@27dd658c2b1282c7f1f4dc242cdf0f6d8af728ab # master 2025-05-30
|
||||
+2
-1
@@ -9,4 +9,5 @@ build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
CMakeLists.txt.user
|
||||
.vscode/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
Generated
+12
-15
@@ -1,7 +1,7 @@
|
||||
# This file is automatically generated from cmake.toml - DO NOT EDIT
|
||||
# See https://github.com/build-cpp/cmkr for more information
|
||||
|
||||
cmake_minimum_required(VERSION 2.8...3.8)
|
||||
cmake_minimum_required(VERSION 2.8...3.31)
|
||||
|
||||
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")
|
||||
@@ -22,7 +22,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.20
|
||||
0.2.46
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
@@ -58,24 +58,22 @@ generate_documentation()
|
||||
|
||||
# Target: cmkr
|
||||
set(cmkr_SOURCES
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
"src/error.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"src/project_parser.cpp"
|
||||
cmake.toml
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
"include/arguments.hpp"
|
||||
"include/build.hpp"
|
||||
"include/cmake_generator.hpp"
|
||||
"include/error.hpp"
|
||||
"include/fs.hpp"
|
||||
"include/help.hpp"
|
||||
"include/literals.hpp"
|
||||
"include/project_parser.hpp"
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
cmake.toml
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"src/project_parser.cpp"
|
||||
)
|
||||
|
||||
add_executable(cmkr)
|
||||
@@ -96,7 +94,6 @@ target_link_libraries(cmkr PRIVATE
|
||||
ghc_filesystem
|
||||
mpark_variant
|
||||
ordered_map
|
||||
nlohmann_json
|
||||
)
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
@@ -105,7 +102,7 @@ if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
endif()
|
||||
|
||||
set(CMKR_TARGET cmkr)
|
||||
generate_resources(${CMKR_TARGET})
|
||||
include("cmake/custom_targets.cmake")
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# cmkr
|
||||
|
||||
[](https://deepwiki.com/build-cpp/cmkr)
|
||||
|
||||
`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:
|
||||
|
||||
+3
-6
@@ -1,10 +1,10 @@
|
||||
[cmake]
|
||||
version = "2.8...3.8"
|
||||
version = "2.8...3.31"
|
||||
cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.20"
|
||||
version = "0.2.46"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
include-after = [
|
||||
@@ -36,11 +36,8 @@ link-libraries = [
|
||||
"ghc_filesystem",
|
||||
"mpark_variant",
|
||||
"ordered_map",
|
||||
"nlohmann_json",
|
||||
]
|
||||
cmake-after = """
|
||||
generate_resources(${CMKR_TARGET})
|
||||
"""
|
||||
include-after = ["cmake/custom_targets.cmake"]
|
||||
|
||||
[[install]]
|
||||
targets = ["cmkr"]
|
||||
|
||||
+77
-77
@@ -1,78 +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
|
||||
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}")
|
||||
+8
-1
@@ -2,7 +2,7 @@ include_guard()
|
||||
|
||||
# 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 "v0.2.20" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.46" 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
|
||||
@@ -61,6 +61,13 @@ endif()
|
||||
if(DEFINED ENV{CMKR_CACHE})
|
||||
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
|
||||
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||
if(CMKR_DIRECTORY_PREFIX MATCHES "^~")
|
||||
if(WIN32)
|
||||
string(REGEX REPLACE "^~" "$ENV{USERPROFILE}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||
elseif(UNIX)
|
||||
string(REGEX REPLACE "^~" "$ENV{HOME}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
|
||||
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
generate_resources(cmkr)
|
||||
|
||||
add_custom_target(regenerate-cmake
|
||||
COMMAND "$<TARGET_FILE:cmkr>" gen
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}" -C $<CONFIG>
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
else()
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}"
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
endif()
|
||||
@@ -15,7 +15,7 @@ function(generate_documentation)
|
||||
message(FATAL_ERROR "This should not happen (wrong regex?)")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
# Delete previously generated examples
|
||||
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")
|
||||
file(GLOB example_files "${example_folder}/*.md")
|
||||
@@ -41,21 +41,21 @@ function(generate_documentation)
|
||||
# Read cmake.toml file
|
||||
file(READ "${test_toml}" test_contents NO_HEX_CONVERSION)
|
||||
string(LENGTH "${test_contents}" toml_length)
|
||||
|
||||
|
||||
# Extract header text
|
||||
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\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\n" EXAMPLE_FOOTER "\n${EXAMPLE_FOOTER}")
|
||||
string(STRIP "${EXAMPLE_FOOTER}" EXAMPLE_FOOTER)
|
||||
|
||||
|
||||
# Extract toml body
|
||||
math(EXPR toml_length "${toml_length}-${header_length}-${footer_length}")
|
||||
string(SUBSTRING "${test_contents}" ${header_length} ${toml_length} EXAMPLE_TOML)
|
||||
@@ -64,7 +64,7 @@ function(generate_documentation)
|
||||
# Extract title from description
|
||||
if("${EXAMPLE_TOML}" MATCHES "description *= *\"([^\"]+)\"")
|
||||
set(EXAMPLE_TITLE "${CMAKE_MATCH_1}")
|
||||
|
||||
|
||||
# Generate documentation markdown page
|
||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/example.md.in" "${example_folder}/${EXAMPLE_PERMALINK}.md" @ONLY NEWLINE_STYLE LF)
|
||||
else()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace cmkr {
|
||||
namespace resources {
|
||||
|
||||
static const char* @RESOURCE_NAME@ = R"RESOURCE(@RESOURCE_CONTENTS@)RESOURCE";
|
||||
|
||||
}
|
||||
namespace cmkr {
|
||||
namespace resources {
|
||||
|
||||
static const char* @RESOURCE_NAME@ = R"RESOURCE(@RESOURCE_CONTENTS@)RESOURCE";
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
cmkr.build
|
||||
cmkr.build
|
||||
|
||||
+80
-13
@@ -6,7 +6,7 @@ nav_order: 1
|
||||
|
||||
# Basics
|
||||
|
||||
To effectively use cmkr it helps to understand the basic concepts of CMake.
|
||||
To effectively use cmkr it helps to understand the basic concepts of CMake. This page serves as an introduction to CMake for beginners. Links to the CMake documentation are included for reference.
|
||||
|
||||
## Projects
|
||||
|
||||
@@ -16,25 +16,41 @@ A CMake **project** is a collection of targets. In the context of libraries the
|
||||
|
||||
## 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.
|
||||
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 static/dynamic library you can build.
|
||||
|
||||
<sub>Visual Studio: a **target** corresponds to a _project_.</sub>
|
||||
|
||||
Most important target types:
|
||||
|
||||
|Type|Purpose|
|
||||
|-|-|
|
||||
|Executable|Program you run on your computer (`.exe`)|
|
||||
|Static library|Library code _compiled into_ the final executable (`.lib`, `.a`)|
|
||||
|Dynamic library|Library code loaded at runtime (`.dll`, `.so`, `.dylib`)|
|
||||
|Interface|Used for organizational purposes (common flags/includes/libraries)|
|
||||
|
||||
There are also other [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but they are not important for now.
|
||||
|
||||
## Target Properties
|
||||
|
||||
Targets have a collection of **properties** that describe how to build them.
|
||||
Targets have a collection of **properties** that describe how the compiler builds them.
|
||||
|
||||
Examples of properties:
|
||||
The most commonly-used 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.
|
||||
- [_Sources_](https://cmake.org/cmake/help/latest/command/target_sources.html): the `*.cpp` files used to build the target.
|
||||
- [_Include directories_](https://cmake.org/cmake/help/latest/command/target_include_directories.html): Paths to directories the compiler will search when doing `#include`.
|
||||
- [_Compile definitions_](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html): Preprocessor macro definitions used for compilation (`-DMYOPTION`).
|
||||
- [_Compile options_](https://cmake.org/cmake/help/latest/command/target_compile_options.html): Command line flags for the compiler (platform/compiler specific).
|
||||
- [_Compile features_](https://cmake.org/cmake/help/latest/command/target_compile_features.html): C/C++ standard version to build with.
|
||||
- [_Link libraries_](https://cmake.org/cmake/help/latest/command/target_link_libraries.html): The **dependencies** required to build this target.
|
||||
- [_Link options_](https://cmake.org/cmake/help/latest/command/target_link_options.html): Command line flags for the linker (platform/linker specific).
|
||||
|
||||
<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.
|
||||
**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, CMake also (transitively) propagates properties of the target you link to based on their _visibility_.
|
||||
|
||||
<sub>You can think of **linking** as _depending on_.</sub>
|
||||
You should think of **linking** in CMake as _depending on_.
|
||||
{:.info}
|
||||
|
||||
The propagation of properties depends on their **visibility**:
|
||||
|
||||
@@ -44,7 +60,7 @@ The propagation of properties depends on their **visibility**:
|
||||
|
||||
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
|
||||
## Example
|
||||
|
||||
The most intuitive example is with _include directories_. Imagine there are two targets:
|
||||
|
||||
@@ -67,10 +83,61 @@ name = "DataProcessor"
|
||||
type = "static"
|
||||
sources = ["StringUtils/src/stringutils.cpp"]
|
||||
headers = ["StringUtils/include/stringutils.hpp"]
|
||||
include-directories = ["StringUtils/include"]
|
||||
include-directories = ["StringUtils/include"] # public (default for libraries)
|
||||
|
||||
[target.DataProcessor]
|
||||
type = "executable"
|
||||
sources = ["DataProcessor/src/main.cpp"]
|
||||
link-libraries = ["StringUtils"]
|
||||
```
|
||||
link-libraries = ["StringUtils"] # private (default for executables)
|
||||
```
|
||||
|
||||
The generated `CMakeLists.txt` (simplified) looks like this:
|
||||
|
||||
```cmake
|
||||
project(DataProcessor)
|
||||
|
||||
# Target: StringUtils
|
||||
add_library(StringUtils STATIC
|
||||
"StringUtils/include/stringutils.hpp"
|
||||
"StringUtils/src/stringutils.cpp"
|
||||
)
|
||||
target_include_directories(StringUtils PUBLIC
|
||||
"StringUtils/include"
|
||||
)
|
||||
|
||||
# Target: DataProcessor
|
||||
add_executable(DataProcessor
|
||||
"DataProcessor/src/main.cpp"
|
||||
)
|
||||
target_link_libraries(DataProcessor PRIVATE
|
||||
StringUtils
|
||||
)
|
||||
```
|
||||
|
||||
## CMake Phases
|
||||
|
||||
CMake works in three phases:
|
||||
1. **Configure**: Execute `CMakeLists.txt` (find dependencies, create targets, OS/compiler-specific conditions, etc).
|
||||
2. **Generate**: Generate build files (propagates target properties and handles [_generator expressions_](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html)).
|
||||
3. **Build**: Execute the compiler to produce executables/libraries.
|
||||
|
||||
CMake keeps generated files separate from your source in a **build directory**. You specify a [**generator**](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) (Visual Studio/Ninja) and [**build type**](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations) (Debug/Release) during configuration.
|
||||
|
||||
```bash
|
||||
# Configure project and generate build files in build directory
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build the project
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
We specify both `CMAKE_BUILD_TYPE` and `--config` to be compatible with generators that support multiple configurations at once ((like Visual Studio/Xcode/Ninja Multi-Config).
|
||||
{:.info}
|
||||
|
||||
**Build Types**:
|
||||
- `Debug`: No optimization, debug symbols
|
||||
- `Release`: Full optimization, no debug symbols
|
||||
- `RelWithDebInfo`: Medium optimizations, debug symbols
|
||||
- `MinSizeRel`: Optimize for size, no debug symbols
|
||||
|
||||
**Generators**: CMake auto-detects (Makefiles on Linux/macOS, Visual Studio on Windows), but you can specify explicitly with `-G` during the _configure_ step.
|
||||
|
||||
+211
-29
@@ -39,17 +39,45 @@ include-before = ["cmake/before-project.cmake"]
|
||||
include-after = ["cmake/after-project.cmake"]
|
||||
```
|
||||
|
||||
### Languages
|
||||
|
||||
Supported languages are (see [`enable_language`](https://cmake.org/cmake/help/latest/command/enable_language.html) for more information):
|
||||
|
||||
- `C`
|
||||
- `CXX` → C++
|
||||
- `CSharp` → C#
|
||||
- `CUDA`
|
||||
- `OBJC` → Objective-C
|
||||
- `OBJCXX` → Objective-C++
|
||||
- `Fortran`
|
||||
- `HIP`
|
||||
- `ISPC`
|
||||
- `Swift`
|
||||
- `ASM`
|
||||
- `ASM_MASM` → [Microsoft Macro Assembler (MASM)](https://learn.microsoft.com/en-US/cpp/assembler/masm/masm-for-x64-ml64-exe)
|
||||
- `ASM_NASM` → [Netwide Assembler (NASM)](https://www.nasm.us)
|
||||
- `ASM_MARMASM` [Microsoft ARM Assembler](https://learn.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-command-line-reference)
|
||||
- `ASM-ATT`
|
||||
- `Java` (undocumented)
|
||||
- `RC` (undocumented)
|
||||
|
||||
After a language is enabled, adding sources files with the corresponding extension to your target will automatically use the appropriate compiler/assembler for it.
|
||||
|
||||
_Note_: It is generally discouraged to disable the `C` language, unless you are absolutely sure it is not used. Sometimes projects added with `fetch-content` implicitly require it and the error messages can be extremely confusing.
|
||||
|
||||
## Conditions
|
||||
|
||||
You can specify your own conditions and use them in any `condition` field:
|
||||
You can specify your own named conditions and use them in any `condition` field:
|
||||
|
||||
```toml
|
||||
[conditions]
|
||||
arch64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
arch32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
ptr64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
ptr32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
```
|
||||
|
||||
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
|
||||
This will make the `ptr64` and `ptr32` conditions available with their respective CMake expressions.
|
||||
|
||||
**Note**: condition names can only contain lower-case alphanumeric characters (`[0-9a-z]`) and dashes (`-`).
|
||||
|
||||
You can also prefix most keys with `condition.` to represent a conditional:
|
||||
|
||||
@@ -57,9 +85,11 @@ You can also prefix most keys with `condition.` to represent a conditional:
|
||||
[target]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
ptr64.sources = ["src/ptr64_only.cpp"]
|
||||
```
|
||||
|
||||
Instead of a named condition you can also specify a [CMake expression](https://cmake.org/cmake/help/latest/command/if.html#condition-syntax) in quotes. Instances of `$<name>` are replaced with the corresponding condition. For example: `"CONDITIONS_BUILD_TESTS AND $<linux>"` becomes `CONDITIONS_BUILD_TESTS AND (CMAKE_SYSTEM_NAME MATCHES "Linux")` in the final `CMakeLists.txt` file.
|
||||
|
||||
### Predefined conditions
|
||||
|
||||
The following conditions are predefined (you can override them if you desire):
|
||||
@@ -72,11 +102,20 @@ 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\" 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"
|
||||
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$\")"
|
||||
clang-cl = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
|
||||
clang-any = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
|
||||
root = "CMKR_ROOT_PROJECT"
|
||||
x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
android = "ANDROID"
|
||||
apple = "APPLE"
|
||||
bsd = "BSD"
|
||||
cygwin = "CYGWIN"
|
||||
ios = "IOS"
|
||||
xcode = "XCODE"
|
||||
wince = "WINCE"
|
||||
```
|
||||
|
||||
## Subdirectories
|
||||
@@ -103,7 +142,7 @@ MYPROJECT_SPECIAL_OPTION = { value = true, help = "Docstring for this option." }
|
||||
MYPROJECT_BUILD_EXAMPLES = "root"
|
||||
```
|
||||
|
||||
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 option automatically gets a corresponding [condition](#conditions).
|
||||
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 option automatically gets a corresponding [condition](#conditions). Additionally, a normalized condition is created based on the `[project].name` (i.e. `MYPROJECT_BUILD_TESTS` becomes `build-tests`).
|
||||
|
||||
The special value `root` can be used to set the option to `true` if the project is compiled as the root project (it will be `false` if someone is including your project via `[fetch-content]` or `[subdir]`).
|
||||
|
||||
@@ -121,14 +160,20 @@ Variables emit a [`set`](https://cmake.org/cmake/help/latest/command/set.html) a
|
||||
|
||||
```toml
|
||||
[vcpkg]
|
||||
version = "2021.05.12"
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2021.05.12.tar.gz"
|
||||
version = "2024.11.16"
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2024.11.16.tar.gz"
|
||||
packages = ["fmt", "zlib"]
|
||||
overlay-ports = ["my-ports"]
|
||||
overlay-triplets = ["my-triplets"]
|
||||
```
|
||||
|
||||
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).
|
||||
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) or [vcpkg.link](https://vcpkg.link).
|
||||
|
||||
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
|
||||
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`. To disable the [default features](https://learn.microsoft.com/en-us/vcpkg/concepts/default-features) you can do: `cpp-httplib[core,openssl]`
|
||||
|
||||
The `overlay-ports` feature allows you to embed vcpkg ports inside your project, without having to fork the main vcpkg registry or creating a custom registry. You can find more information in the relevant [documentation](https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports). The `overlay-triplets` feature allows you to customize triplets and change the default behavior (for example always preferring static libraries on Windows). To specify both in one go you can do `[vcpkg].overlay = "vcpkg-overlay"`.
|
||||
|
||||
For a concrete example of all the features, take a look at the [vcpkg_template](https://github.com/build-cpp/vcpkg_template) repository.
|
||||
|
||||
## Packages
|
||||
|
||||
@@ -146,55 +191,90 @@ components = ["mycomponent"]
|
||||
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
|
||||
|
||||
```toml
|
||||
# Include CMake project from git
|
||||
[fetch-content.gitcontent]
|
||||
condition = "mycondition"
|
||||
git = "https://github.com/myuser/gitcontent"
|
||||
tag = "v0.1"
|
||||
shallow = false
|
||||
shallow = false # shallow clone (--depth 1)
|
||||
system = false
|
||||
subdir = "" # folder containing CMakeLists.txt
|
||||
# Set cache variables before FetchContent_MakeAvailable
|
||||
options = { BUILD_TESTS = false, BUILD_EXAMPLES = false }
|
||||
|
||||
# Include a CMake project from a URL
|
||||
[fetch-content.urlcontent]
|
||||
condition = "mycondition"
|
||||
url = "https://content-host.com/urlcontent.zip"
|
||||
# Other supported algorithms:
|
||||
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
|
||||
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
|
||||
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
|
||||
|
||||
[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"
|
||||
# For many options, you can also use a separate table:
|
||||
# [fetch-content.bigproject.options]
|
||||
# BUILD_TESTS = false
|
||||
# BUILD_EXAMPLES = false
|
||||
# SOME_STRING_OPTION = "value"
|
||||
# SOME_LIST_OPTION = ["item1", "item2"] # becomes "item1;item2"
|
||||
```
|
||||
|
||||
Table keys that match CMake variable names (`[A-Z_]+`) will be passed to the [`FetchContent_Declare`](https://cmake.org/cmake/help/latest/module/FetchContent.html#command:fetchcontent_declare) command.
|
||||
|
||||
The `options` field allows setting CACHE variables before `FetchContent_MakeAvailable` is called. This is useful for configuring options on the fetched dependency (e.g., disabling tests). Boolean values become `ON`/`OFF`, arrays become semicolon-separated strings.
|
||||
|
||||
## Targets
|
||||
|
||||
```toml
|
||||
[target.mytarget]
|
||||
condition = "mycondition"
|
||||
alias = "mytarget::mytarget"
|
||||
type = "static" # executable, shared (DLL), static, interface, object, library, custom
|
||||
type = "static" # executable, library, shared (DLL), static, interface, object, 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
|
||||
compile-definitions = [""]
|
||||
compile-definitions = [""] # preprocessor define (-DFOO)
|
||||
private-compile-definitions = [""]
|
||||
compile-features = [""]
|
||||
compile-features = [""] # C++ standard version (cxx_std_20)
|
||||
private-compile-features = [""]
|
||||
compile-options = [""]
|
||||
compile-options = [""] # compiler flags
|
||||
private-compile-options = [""]
|
||||
include-directories = [""]
|
||||
include-directories = [""] # include paths/directories
|
||||
private-include-directories = [""]
|
||||
link-directories = [""]
|
||||
link-directories = [""] # library directories
|
||||
private-link-directories = [""]
|
||||
link-libraries = [""]
|
||||
link-libraries = [""] # dependencies
|
||||
private-link-libraries = [""]
|
||||
link-options = [""]
|
||||
link-options = [""] # linker flags
|
||||
private-link-options = [""]
|
||||
precompile-headers = [""]
|
||||
precompile-headers = [""] # precompiled headers
|
||||
private-precompile-headers = [""]
|
||||
dependencies = [""] # add_dependencies(target ...)
|
||||
|
||||
# Keys below are for type = "custom" (add_custom_target)
|
||||
all = false
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Hello"] # one command line
|
||||
commands = [ # multiple command lines
|
||||
["${CMAKE_COMMAND}", "-E", "echo", "First"],
|
||||
["${CMAKE_COMMAND}", "-E", "echo", "Second"],
|
||||
]
|
||||
depends = ["generated.txt"] # add_custom_target(... DEPENDS ...)
|
||||
byproducts = ["generated.txt"]
|
||||
working-directory = "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
comment = "Run custom target"
|
||||
job-pool = "pool"
|
||||
job-server-aware = true
|
||||
verbatim = true
|
||||
uses-terminal = false # incompatible with job-pool when true
|
||||
command-expand-lists = true
|
||||
|
||||
cmake-before = """
|
||||
message(STATUS "CMake injected before the target")
|
||||
@@ -212,6 +292,108 @@ CXX_STANDARD_REQUIRED = true
|
||||
FOLDER = "MyFolder"
|
||||
```
|
||||
|
||||
### Custom commands
|
||||
|
||||
Use `[[target.<name>.custom-command]]` to map directly to [`add_custom_command`](https://cmake.org/cmake/help/latest/command/add_custom_command.html). A custom command can use either the `OUTPUT` form or the `TARGET` form:
|
||||
|
||||
```toml
|
||||
# OUTPUT form (add_custom_command(OUTPUT ...))
|
||||
[[target.mytarget.custom-command]]
|
||||
condition = "mycondition"
|
||||
outputs = ["generated.cpp"] # relative paths are interpreted from ${CMAKE_CURRENT_BINARY_DIR}
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/generated.cpp",
|
||||
"-P",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate.cmake",
|
||||
]
|
||||
depends = ["cmake/generate.cmake"]
|
||||
byproducts = ["generated.hpp"]
|
||||
main-dependency = "cmake/generate.cmake"
|
||||
implicit-depends = [["CXX", "src/input.idl"]]
|
||||
working-directory = "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
comment = "Generate source"
|
||||
depfile = "${CMAKE_CURRENT_BINARY_DIR}/generated.d"
|
||||
job-pool = "pool" # incompatible with uses-terminal when uses-terminal = true
|
||||
job-server-aware = true
|
||||
verbatim = true
|
||||
append = false
|
||||
uses-terminal = false
|
||||
codegen = false
|
||||
command-expand-lists = false
|
||||
depends-explicit-only = false
|
||||
|
||||
# TARGET form (add_custom_command(TARGET ...))
|
||||
[[target.mytarget.custom-command]]
|
||||
build-event = "post-build" # pre-build, pre-link, post-build
|
||||
command = ["${CMAKE_COMMAND}", "-E", "touch", "${CMAKE_CURRENT_BINARY_DIR}/post-build.stamp"]
|
||||
byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/post-build.stamp"]
|
||||
working-directory = "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
comment = "Post-build step"
|
||||
verbatim = true
|
||||
command-expand-lists = false
|
||||
uses-terminal = false
|
||||
```
|
||||
|
||||
For each custom command entry, exactly one of `outputs` or `build-event` is required. Relative `outputs`/`byproducts` paths follow CMake and are interpreted from the current binary directory. `build-event` is only valid for non-`interface` targets, and `pre-link` is not supported for `type = "custom"`. In both `add_custom_command(OUTPUT ...)` and `add_custom_target(...)`, `job-pool` cannot be combined with `uses-terminal = true`. When `append = true` is used with the `OUTPUT` form, at least one `command`/`commands` entry or `depends` must also be provided.
|
||||
|
||||
A table mapping the cmkr features to the relevant CMake construct and the relevant documentation pages:
|
||||
|
||||
| cmkr | CMake construct | Description |
|
||||
| ---- | ----- | ----------- |
|
||||
| `alias` | [Alias Libraries](https://cmake.org/cmake/help/latest/command/add_library.html#alias-libraries) | Create an [alias target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#alias-targets), used for namespacing or clarity. |
|
||||
| `sources` | [`target_sources`](https://cmake.org/cmake/help/latest/command/target_sources.html) | Source files (`PRIVATE` except `interface` targets). |
|
||||
| `headers` | [`target_sources`](https://cmake.org/cmake/help/latest/command/target_sources.html) | For readability (and future packaging). |
|
||||
| `msvc-runtime` | [`MSVC_RUNTIME_LIBRARY`](https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html) | The [CMP0091](https://cmake.org/cmake/help/latest/policy/CMP0091.html) policy is set automatically. |
|
||||
| `compile-definitions` | [`target_compile_definitions`](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html) | Adds a macro definition (define, `-DMYMACRO=XXX`). |
|
||||
| `compile-features` | [`target_compile_features`](https://cmake.org/cmake/help/latest/command/target_compile_features.html) | Specifies the C++ standard version (`cxx_std_20`). |
|
||||
| `compile-options` | [`target_compile_options`](https://cmake.org/cmake/help/latest/command/target_compile_options.html) | Adds compiler flags. |
|
||||
| `include-directories` | [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) | Adds include directories. |
|
||||
| `link-directories` | [`target_link_directories`](https://cmake.org/cmake/help/latest/command/target_link_directories.html) | Adds library directories. |
|
||||
| `link-libraries` | [`target_link_libraries`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) | Adds library dependencies. Use `::mylib` to make sure a target exists. |
|
||||
| `link-options` | [`target_link_options`](https://cmake.org/cmake/help/latest/command/target_link_options.html) | Adds linker flags. |
|
||||
| `precompile-headers` | [`target_precompile_headers`](https://cmake.org/cmake/help/latest/command/target_precompile_headers.html) | Specifies precompiled headers. |
|
||||
| `dependencies` | [`add_dependencies`](https://cmake.org/cmake/help/latest/command/add_dependencies.html) | Adds target-level dependencies. |
|
||||
| `type = "custom"` + custom target keys | [`add_custom_target`](https://cmake.org/cmake/help/latest/command/add_custom_target.html) | Creates a custom target with command/dependency options. |
|
||||
| `[[target.<name>.custom-command]]` | [`add_custom_command`](https://cmake.org/cmake/help/latest/command/add_custom_command.html) | Supports both `OUTPUT` and `TARGET` forms. |
|
||||
| `properties` | [`set_target_properties`](https://cmake.org/cmake/help/latest/command/set_target_properties.html) | See [properties on targets](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for more information. |
|
||||
|
||||
The default [visibility](/basics) is as follows:
|
||||
|
||||
| Type | Visibility |
|
||||
| ------------ | ----------- |
|
||||
| `executable` | `PRIVATE` |
|
||||
| `library` | `PUBLIC` |
|
||||
| `shared` | `PUBLIC` |
|
||||
| `static` | `PUBLIC` |
|
||||
| `object` | `PUBLIC` |
|
||||
| `interface` | `INTERFACE` |
|
||||
|
||||
## Templates
|
||||
|
||||
To avoid repeating yourself you can create your own target type and use it in your targets:
|
||||
|
||||
```toml
|
||||
[template.example]
|
||||
condition = "MYPROJECT_BUILD_EXAMPLES"
|
||||
type = "executable"
|
||||
link-libraries = ["myproject::mylib"]
|
||||
add-function = ""
|
||||
add-arguments = ["myoption"]
|
||||
pass-sources = false
|
||||
|
||||
# Properties from the template are merged with the ones here
|
||||
[target.myexample]
|
||||
type = "example"
|
||||
sources = ["src/myexample.cpp"]
|
||||
```
|
||||
|
||||
The properties declared on a `template` are the same as the ones you use for targets. The only exceptions are:
|
||||
|
||||
- `add-function`: Specifies a custom add function. Projects like [pybind11](https://pybind11.readthedocs.io/en/stable/cmake/index.html#new-findpython-mode) have their own `add_xxx` function, which you can specify here.
|
||||
- `add-arguments`: Arguments to pass to the `add-function` before the list of sources. See [cmake_parse_arguments](https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html) for more details.
|
||||
- `pass-sources`: Pass sources directly to the add function instead of using `target_sources`.
|
||||
|
||||
## Tests and installation (unfinished)
|
||||
|
||||
**Note**: The `[[test]]` and `[[install]]` are unfinished features and will likely change in a future release.
|
||||
@@ -237,4 +419,4 @@ files = ["content/my.png"]
|
||||
dirs = ["include"]
|
||||
configs = ["Release", "Debug"]
|
||||
optional = false
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
# Automatically generated from tests/compile-options/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Compiler flags
|
||||
permalink: /examples/compile-options
|
||||
parent: Examples
|
||||
nav_order: 9
|
||||
---
|
||||
|
||||
# Compiler flags
|
||||
|
||||
Example project that sets compiler flags and preprocessor defines for various platforms.
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "compile-options"
|
||||
description = "Compiler flags"
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc.compile-options = ["/W2"]
|
||||
msvc.compile-definitions = ["PLATFORM=\"msvc\""]
|
||||
gcc.compile-options = ["-Wall"]
|
||||
gcc.compile-definitions = ["PLATFORM=\"gcc\""]
|
||||
clang.compile-options = ["-Wall"]
|
||||
clang.compile-definitions = ["PLATFORM=\"clang\""]
|
||||
```
|
||||
|
||||
The `hello` target uses [conditions](/cmake-toml#conditions) to set different compiler flags and definitions depending on the platform. See the [targets](/cmake-toml/#targets) documentation for other things you can set.
|
||||
|
||||
_Note_: In general you only want to specify flags _required_ to compile your code without errors.
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/compile-options/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/compile-options/cmake.toml).</sub></sup>
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
# Automatically generated from tests/custom-command/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Tests add_custom_command and add_custom_target support
|
||||
permalink: /examples/custom-command
|
||||
parent: Examples
|
||||
nav_order: 12
|
||||
---
|
||||
|
||||
# Tests add_custom_command and add_custom_target support
|
||||
|
||||
This test demonstrates add_custom_command and add_custom_target support in cmkr.
|
||||
|
||||
```toml
|
||||
#
|
||||
# There are two forms of add_custom_command in CMake:
|
||||
# 1. Output form: generates files that can be consumed by other targets
|
||||
# 2. Target form: runs commands at build time for a specific target (pre-build, pre-link, post-build)
|
||||
|
||||
[project]
|
||||
name = "custom-command"
|
||||
description = "Tests add_custom_command and add_custom_target support"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Simple executable demonstrating post-build events
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/hello.cpp"]
|
||||
|
||||
# This post-build command runs after the 'hello' executable is built.
|
||||
# build-event can be: "pre-build", "pre-link", or "post-build"
|
||||
[[target.hello.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Built executable: $<TARGET_FILE_NAME:hello>"]
|
||||
comment = "Print the built executable name"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Executable with code generation (output form custom command)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.custom-command]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Output form: This custom command generates source files before building.
|
||||
# Relative outputs follow CMake and are interpreted from the binary directory.
|
||||
# The outputs are automatically added as sources to the target.
|
||||
[[target.custom-command.custom-command]]
|
||||
outputs = ["generated/generated.cpp"]
|
||||
byproducts = ["generated/generated.hpp"]
|
||||
depends = ["cmake/generate_source.cmake"]
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-DOUTPUT_CPP=${CMAKE_CURRENT_BINARY_DIR}/generated/generated.cpp",
|
||||
"-DOUTPUT_HPP=${CMAKE_CURRENT_BINARY_DIR}/generated/generated.hpp",
|
||||
"-P",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_source.cmake",
|
||||
]
|
||||
comment = "Generate source files"
|
||||
verbatim = true
|
||||
|
||||
# Target form: This post-build command runs after 'custom-command' is built.
|
||||
[[target.custom-command.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-E",
|
||||
"touch",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/custom-command-post-build.stamp",
|
||||
]
|
||||
byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/custom-command-post-build.stamp"]
|
||||
comment = "Create a post-build stamp file"
|
||||
verbatim = true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Custom target (add_custom_target)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# A custom target runs commands independently of any executable/library.
|
||||
# Setting 'all = true' makes it run as part of the default build.
|
||||
[target.custom-codegen]
|
||||
type = "custom"
|
||||
all = true
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-E",
|
||||
"touch",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/custom-target.stamp",
|
||||
]
|
||||
byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/custom-target.stamp"]
|
||||
comment = "Run custom target"
|
||||
verbatim = true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Template-based custom target
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Custom target options should also work when the target type comes from a template.
|
||||
[template.generated-custom-target]
|
||||
type = "custom"
|
||||
all = true
|
||||
verbatim = true
|
||||
|
||||
[target.custom-codegen-template]
|
||||
type = "generated-custom-target"
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-DINPUT=${CMAKE_CURRENT_BINARY_DIR}/template-custom-target.stamp",
|
||||
"-P",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/verify_file_exists.cmake",
|
||||
]
|
||||
comment = "Verify the generated stamp exists"
|
||||
|
||||
# The output-form custom command should automatically become a dependency of the
|
||||
# custom target, so the verification command sees the stamp file.
|
||||
[[target.custom-codegen-template.custom-command]]
|
||||
outputs = ["template-custom-target.stamp"]
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-E",
|
||||
"touch",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/template-custom-target.stamp",
|
||||
]
|
||||
comment = "Create a stamp file for the template-based custom target"
|
||||
verbatim = true
|
||||
|
||||
# A target-level all = false should override the template's all = true.
|
||||
# If this target is incorrectly included in the default build, the build fails.
|
||||
[target.custom-codegen-template-disabled]
|
||||
type = "generated-custom-target"
|
||||
all = false
|
||||
command = ["${CMAKE_COMMAND}", "-E", "false"]
|
||||
comment = "This target must not run as part of the default build"
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/custom-command/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/custom-command/cmake.toml).</sub></sup>
|
||||
@@ -16,8 +16,9 @@ Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) from [GitHub](https://github.com)
|
||||
name = "fetch-content"
|
||||
description = "Fetching from git"
|
||||
|
||||
[fetch-content]
|
||||
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
|
||||
[fetch-content.fmt]
|
||||
git = "https://github.com/fmtlib/fmt"
|
||||
tag = "7.1.3"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
# Automatically generated from tests/generator-executable/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Tests using an executable to generate sources for another target
|
||||
permalink: /examples/generator-executable
|
||||
parent: Examples
|
||||
nav_order: 13
|
||||
---
|
||||
|
||||
# Tests using an executable to generate sources for another target
|
||||
|
||||
This test demonstrates a common pattern: using an executable to generate sources
|
||||
|
||||
for another target. The generator runs at build time and produces code that is
|
||||
|
||||
consumed by the main executable.
|
||||
|
||||
```toml
|
||||
#
|
||||
# CMake handles the dependency automatically: the generator executable is built
|
||||
# first, then it runs to produce the generated sources, and finally the main
|
||||
# executable is built using those sources.
|
||||
|
||||
[project]
|
||||
name = "generator-executable"
|
||||
description = "Tests using an executable to generate sources for another target"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generator executable: produces code at build time
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.generate_numbers]
|
||||
type = "executable"
|
||||
sources = ["src/generate_numbers.cpp"]
|
||||
|
||||
# Post-build: confirm the generator was built
|
||||
[[target.generate_numbers.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Generator built successfully: $<TARGET_FILE_NAME:generate_numbers>"]
|
||||
comment = "Confirm generator executable was built"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main executable: uses generated sources
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.main]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Create the generated directory before the generator runs.
|
||||
cmake-before = """
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
"""
|
||||
|
||||
# Output form custom command: runs the generate_numbers executable to generate sources.
|
||||
# The outputs are automatically added as sources to this target.
|
||||
# The DEPENDS on "generate_numbers" ensures the generator is built before this runs.
|
||||
[[target.main.custom-command]]
|
||||
outputs = ["${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
depends = ["generate_numbers"]
|
||||
command = ["$<TARGET_FILE:generate_numbers>", "${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
comment = "Run generate_numbers to generate numbers.cpp"
|
||||
|
||||
# Post-build: run the executable to verify it works
|
||||
[[target.main.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["$<TARGET_FILE:main>"]
|
||||
comment = "Run the main executable to verify generated code works"
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/generator-executable/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/generator-executable/cmake.toml).</sub></sup>
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
# Automatically generated from tests/objective-c/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Objective-C
|
||||
permalink: /examples/objective-c
|
||||
parent: Examples
|
||||
nav_order: 11
|
||||
---
|
||||
|
||||
# Objective-C
|
||||
|
||||
Add Objective-C sources on Apple platforms:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "objective-c"
|
||||
description = "Objective-C"
|
||||
languages = ["C"]
|
||||
apple.languages = ["OBJC"]
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.c"]
|
||||
apple.sources = ["src/apple.m"]
|
||||
apple.link-libraries = ["$<LINK_LIBRARY:FRAMEWORK,Foundation>"]
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/objective-c/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/objective-c/cmake.toml).</sub></sup>
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
# Automatically generated from tests/protobuf/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Demonstrates protobuf integration with cmkr custom commands
|
||||
permalink: /examples/protobuf
|
||||
parent: Examples
|
||||
nav_order: 14
|
||||
---
|
||||
|
||||
# Demonstrates protobuf integration with cmkr custom commands
|
||||
|
||||
This test demonstrates using protobuf with cmkr for code generation.
|
||||
|
||||
```toml
|
||||
#
|
||||
# The example:
|
||||
# 1. Fetches protobuf from GitHub using FetchContent
|
||||
# 2. Uses a custom command to generate C++ sources from a .proto file
|
||||
# 3. Links an executable against the generated sources and protobuf library
|
||||
# 4. Serializes and deserializes a simple message
|
||||
#
|
||||
# Note: protobuf v21.x is used because newer versions (v22+) require abseil-cpp
|
||||
# as a dependency, which significantly complicates the build.
|
||||
|
||||
[cmake]
|
||||
version = "3.18...3.31"
|
||||
|
||||
[project]
|
||||
name = "protobuf-example"
|
||||
description = "Demonstrates protobuf integration with cmkr custom commands"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Fetch protobuf from GitHub
|
||||
# Using v21.12 (last version before abseil-cpp became required)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[fetch-content.protobuf]
|
||||
git = "https://github.com/protocolbuffers/protobuf"
|
||||
tag = "v21.12"
|
||||
options = {
|
||||
protobuf_BUILD_TESTS = false,
|
||||
protobuf_BUILD_EXAMPLES = false,
|
||||
protobuf_BUILD_LIBPROTOC = false,
|
||||
protobuf_BUILD_SHARED_LIBS = false,
|
||||
protobuf_MSVC_STATIC_RUNTIME = false,
|
||||
SKIP_INSTALL_ALL = true,
|
||||
protobuf_WITH_ZLIB = false,
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main executable using protobuf
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.protobuf_example]
|
||||
type = "executable"
|
||||
sources = [
|
||||
"src/main.cpp",
|
||||
]
|
||||
include-directories = [
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated",
|
||||
]
|
||||
link-libraries = ["protobuf::libprotobuf"]
|
||||
compile-features = ["cxx_std_17"]
|
||||
|
||||
# Create the generated directory before running protoc
|
||||
cmake-before = """
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
"""
|
||||
|
||||
# Custom command to generate C++ sources from .proto file
|
||||
# This uses protoc from the fetched protobuf repository
|
||||
[[target.protobuf_example.custom-command]]
|
||||
outputs = [
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.h",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.cc",
|
||||
]
|
||||
depends = [
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
|
||||
"protobuf::protoc", # Ensure protoc is built first
|
||||
]
|
||||
command = [
|
||||
"$<TARGET_FILE:protobuf::protoc>",
|
||||
"--cpp_out=${CMAKE_CURRENT_BINARY_DIR}/generated",
|
||||
"-I${CMAKE_CURRENT_SOURCE_DIR}/proto",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
|
||||
]
|
||||
comment = "Generate C++ sources from addressbook.proto"
|
||||
verbatim = true
|
||||
|
||||
# Post-build: run the executable to verify it works
|
||||
[[target.protobuf_example.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["$<TARGET_FILE:protobuf_example>"]
|
||||
comment = "Run protobuf_example to verify serialization/deserialization works"
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/protobuf/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/protobuf/cmake.toml).</sub></sup>
|
||||
@@ -20,11 +20,11 @@ description = "Target templates"
|
||||
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
|
||||
properties = {
|
||||
CXX_STANDARD = "11",
|
||||
CXX_STANDARD_REQUIRED = true,
|
||||
}
|
||||
|
||||
[target.app-a]
|
||||
type = "app"
|
||||
|
||||
@@ -9,7 +9,7 @@ nav_order: 4
|
||||
|
||||
# Dependencies from vcpkg
|
||||
|
||||
Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
|
||||
Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://github.com/microsoft/vcpkg) and links an `example` target to it:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
@@ -17,13 +17,12 @@ name = "vcpkg"
|
||||
description = "Dependencies from vcpkg"
|
||||
|
||||
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
|
||||
# See https://vcpkg.io/en/packages.html for available packages
|
||||
# See https://vcpkg.io/en/packages or https://vcpkg.link for available packages
|
||||
[vcpkg]
|
||||
version = "2022.11.14"
|
||||
version = "2024.11.16"
|
||||
packages = ["fmt"]
|
||||
|
||||
[find-package]
|
||||
fmt = {}
|
||||
[find-package.fmt]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
layout: null
|
||||
permalink: /getting-started/
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
|
||||
</head>
|
||||
---
|
||||
layout: null
|
||||
permalink: /getting-started/
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
|
||||
</head>
|
||||
</html>
|
||||
@@ -11,9 +11,3 @@ int install();
|
||||
|
||||
} // namespace build
|
||||
} // namespace cmkr
|
||||
|
||||
int cmkr_build_run(int argc, char **argv);
|
||||
|
||||
int cmkr_build_clean();
|
||||
|
||||
int cmkr_build_install();
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
struct Status {
|
||||
enum class Code {
|
||||
Success = 0,
|
||||
RuntimeError,
|
||||
InitError,
|
||||
GenerationError,
|
||||
BuildError,
|
||||
CleanError,
|
||||
InstallError,
|
||||
};
|
||||
Status(Code ec) noexcept;
|
||||
operator int() const noexcept;
|
||||
Code code() const noexcept;
|
||||
|
||||
private:
|
||||
Code ec_ = Code::Success;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_error_status_string(int);
|
||||
+2
-4
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || \
|
||||
(defined(__cplusplus) && __cplusplus >= 201703L)) && \
|
||||
defined(__has_include)
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
|
||||
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
|
||||
#define GHC_USE_STD_FS
|
||||
#include <filesystem>
|
||||
@@ -12,4 +10,4 @@ namespace fs = std::filesystem;
|
||||
#ifndef GHC_USE_STD_FS
|
||||
#include <ghc/filesystem.hpp>
|
||||
namespace fs = ghc::filesystem;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,3 @@ const char *message() noexcept;
|
||||
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_help_version(void);
|
||||
|
||||
const char *cmkr_help_message(void);
|
||||
|
||||
+126
-4
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <mpark/variant.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <mpark/variant.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
#include <tsl/ordered_set.h>
|
||||
#include <vector>
|
||||
|
||||
namespace cmkr {
|
||||
namespace parser {
|
||||
@@ -44,9 +45,18 @@ struct Vcpkg {
|
||||
struct Package {
|
||||
std::string name;
|
||||
std::vector<std::string> features;
|
||||
bool default_features = true;
|
||||
};
|
||||
|
||||
std::vector<Package> packages;
|
||||
|
||||
// https://github.com/Microsoft/vcpkg-docs/blob/main/vcpkg/users/buildsystems/cmake-integration.md
|
||||
std::vector<std::string> overlay_ports;
|
||||
std::vector<std::string> overlay_triplets;
|
||||
|
||||
bool enabled() const {
|
||||
return !packages.empty();
|
||||
}
|
||||
};
|
||||
|
||||
enum TargetType {
|
||||
@@ -64,6 +74,104 @@ enum TargetType {
|
||||
extern const char *targetTypeNames[target_last];
|
||||
|
||||
struct Target {
|
||||
struct CustomTarget {
|
||||
bool has_all = false;
|
||||
bool all = false;
|
||||
|
||||
bool has_command = false;
|
||||
bool has_commands = false;
|
||||
std::vector<std::vector<std::string>> commands;
|
||||
std::vector<std::string> depends;
|
||||
std::vector<std::string> byproducts;
|
||||
|
||||
bool has_working_directory = false;
|
||||
std::string working_directory;
|
||||
|
||||
bool has_comment = false;
|
||||
std::string comment;
|
||||
|
||||
bool has_job_pool = false;
|
||||
std::string job_pool;
|
||||
|
||||
bool has_job_server_aware = false;
|
||||
bool job_server_aware = false;
|
||||
|
||||
bool has_verbatim = false;
|
||||
bool verbatim = false;
|
||||
|
||||
bool has_uses_terminal = false;
|
||||
bool uses_terminal = false;
|
||||
|
||||
bool has_command_expand_lists = false;
|
||||
bool command_expand_lists = false;
|
||||
|
||||
bool empty() const {
|
||||
return !has_all && !has_command && !has_commands && commands.empty() && depends.empty() && byproducts.empty() && !has_working_directory &&
|
||||
!has_comment && !has_job_pool && !has_job_server_aware && !has_verbatim && !has_uses_terminal && !has_command_expand_lists;
|
||||
}
|
||||
};
|
||||
|
||||
struct CustomCommand {
|
||||
struct ImplicitDependency {
|
||||
std::string language;
|
||||
std::string file;
|
||||
};
|
||||
|
||||
std::string condition;
|
||||
std::vector<std::vector<std::string>> commands;
|
||||
|
||||
std::vector<std::string> outputs;
|
||||
std::string build_event;
|
||||
|
||||
bool has_append = false;
|
||||
bool append = false;
|
||||
|
||||
bool has_main_dependency = false;
|
||||
std::string main_dependency;
|
||||
|
||||
std::vector<std::string> depends;
|
||||
std::vector<std::string> byproducts;
|
||||
std::vector<ImplicitDependency> implicit_depends;
|
||||
|
||||
bool has_working_directory = false;
|
||||
std::string working_directory;
|
||||
|
||||
bool has_comment = false;
|
||||
std::string comment;
|
||||
|
||||
bool has_depfile = false;
|
||||
std::string depfile;
|
||||
|
||||
bool has_job_pool = false;
|
||||
std::string job_pool;
|
||||
|
||||
bool has_job_server_aware = false;
|
||||
bool job_server_aware = false;
|
||||
|
||||
bool has_verbatim = false;
|
||||
bool verbatim = false;
|
||||
|
||||
bool has_uses_terminal = false;
|
||||
bool uses_terminal = false;
|
||||
|
||||
bool has_codegen = false;
|
||||
bool codegen = false;
|
||||
|
||||
bool has_command_expand_lists = false;
|
||||
bool command_expand_lists = false;
|
||||
|
||||
bool has_depends_explicit_only = false;
|
||||
bool depends_explicit_only = false;
|
||||
|
||||
bool is_output_form() const {
|
||||
return !outputs.empty();
|
||||
}
|
||||
|
||||
bool is_target_form() const {
|
||||
return !build_event.empty();
|
||||
}
|
||||
};
|
||||
|
||||
std::string name;
|
||||
TargetType type = target_last;
|
||||
std::string type_name;
|
||||
@@ -95,6 +203,11 @@ struct Target {
|
||||
ConditionVector precompile_headers;
|
||||
ConditionVector private_precompile_headers;
|
||||
|
||||
ConditionVector dependencies;
|
||||
|
||||
CustomTarget custom_target;
|
||||
std::vector<CustomCommand> custom_commands;
|
||||
|
||||
std::string condition;
|
||||
std::string alias;
|
||||
Condition<tsl::ordered_map<std::string, std::string>> properties;
|
||||
@@ -108,6 +221,7 @@ struct Target {
|
||||
struct Template {
|
||||
Target outline;
|
||||
std::string add_function;
|
||||
std::vector<std::string> add_arguments;
|
||||
bool pass_sources_to_add_function = false;
|
||||
};
|
||||
|
||||
@@ -141,15 +255,22 @@ struct Subdir {
|
||||
ConditionVector include_after;
|
||||
};
|
||||
|
||||
struct ContentOption {
|
||||
mpark::variant<bool, std::string> value;
|
||||
};
|
||||
|
||||
struct Content {
|
||||
std::string name;
|
||||
std::string condition;
|
||||
tsl::ordered_map<std::string, std::string> arguments;
|
||||
tsl::ordered_map<std::string, ContentOption> options;
|
||||
|
||||
Condition<std::string> cmake_before;
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
ConditionVector include_after;
|
||||
bool system = false;
|
||||
std::string subdir;
|
||||
};
|
||||
|
||||
enum MsvcRuntimeType {
|
||||
@@ -179,7 +300,7 @@ struct Project {
|
||||
std::string project_name;
|
||||
std::string project_version;
|
||||
std::string project_description;
|
||||
std::vector<std::string> project_languages;
|
||||
ConditionVector project_languages;
|
||||
bool project_allow_unknown_languages = false;
|
||||
MsvcRuntimeType project_msvc_runtime = msvc_last;
|
||||
Condition<std::string> cmake_before;
|
||||
@@ -201,9 +322,10 @@ struct Project {
|
||||
Project(const Project *parent, const std::string &path, bool build);
|
||||
const Project *root() const;
|
||||
bool cmake_minimum_version(int major, int minor) const;
|
||||
static bool is_condition_name(const std::string &name);
|
||||
};
|
||||
|
||||
bool is_root_path(const std::string &path);
|
||||
|
||||
} // namespace parser
|
||||
} // namespace cmkr
|
||||
} // namespace cmkr
|
||||
|
||||
+1
-3
@@ -2,10 +2,8 @@
|
||||
#include "build.hpp"
|
||||
#include "cmake_generator.hpp"
|
||||
#include "help.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
+4
-38
@@ -1,14 +1,10 @@
|
||||
#include "build.hpp"
|
||||
#include "cmake_generator.hpp"
|
||||
#include "error.hpp"
|
||||
#include "project_parser.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
|
||||
namespace cmkr {
|
||||
namespace build {
|
||||
@@ -17,7 +13,7 @@ int run(int argc, char **argv) {
|
||||
parser::Project project(nullptr, ".", true);
|
||||
if (argc > 2) {
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
project.build_args.push_back(argv[i]);
|
||||
project.build_args.emplace_back(argv[i]);
|
||||
}
|
||||
}
|
||||
std::stringstream ss;
|
||||
@@ -48,13 +44,13 @@ int run(int argc, char **argv) {
|
||||
}
|
||||
|
||||
int clean() {
|
||||
bool ret = false;
|
||||
bool success = false;
|
||||
parser::Project project(nullptr, ".", true);
|
||||
if (fs::exists(project.build_dir)) {
|
||||
ret = fs::remove_all(project.build_dir);
|
||||
success = fs::remove_all(project.build_dir);
|
||||
fs::create_directory(project.build_dir);
|
||||
}
|
||||
return !ret;
|
||||
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int install() {
|
||||
@@ -64,33 +60,3 @@ int install() {
|
||||
}
|
||||
} // namespace build
|
||||
} // namespace cmkr
|
||||
|
||||
int cmkr_build_run(int argc, char **argv) {
|
||||
try {
|
||||
return cmkr::build::run(argc, argv);
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::BuildError);
|
||||
}
|
||||
}
|
||||
|
||||
int cmkr_build_clean(void) {
|
||||
try {
|
||||
return cmkr::build::clean();
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::CleanError);
|
||||
}
|
||||
}
|
||||
|
||||
int cmkr_build_install(void) {
|
||||
try {
|
||||
return cmkr::build::install();
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::InstallError);
|
||||
}
|
||||
}
|
||||
|
||||
+811
-220
File diff suppressed because it is too large
Load Diff
@@ -1,31 +0,0 @@
|
||||
#include "error.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
Status::Status(Code ec) noexcept : ec_(ec) {
|
||||
}
|
||||
|
||||
Status::operator int() const noexcept {
|
||||
return static_cast<int>(ec_);
|
||||
}
|
||||
|
||||
Status::Code Status::code() const noexcept {
|
||||
return ec_;
|
||||
}
|
||||
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
// strings for cmkr::error::Status::Code
|
||||
static const char *err_string[] = {
|
||||
"Success", "Runtime error", "Initialization error", "CMake generation error", "Build error", "Clean error", "Install error",
|
||||
};
|
||||
|
||||
const char *cmkr_error_status(int i) {
|
||||
assert(i >= 0 && static_cast<size_t>(i) < (sizeof(err_string) / sizeof(*(err_string))));
|
||||
return err_string[i];
|
||||
}
|
||||
@@ -23,11 +23,3 @@ arguments:
|
||||
}
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_help_version(void) {
|
||||
return cmkr::help::version();
|
||||
}
|
||||
|
||||
const char *cmkr_help_message(void) {
|
||||
return cmkr::help::message();
|
||||
}
|
||||
+534
-19
@@ -1,6 +1,7 @@
|
||||
#include "project_parser.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <cctype>
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
@@ -36,7 +37,7 @@ static std::string format_key_message(const std::string &message, const toml::ke
|
||||
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();
|
||||
const auto &line_str = loc.line_str();
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << message << "\n";
|
||||
@@ -73,7 +74,7 @@ class TomlChecker {
|
||||
public:
|
||||
TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {
|
||||
}
|
||||
TomlChecker(const TomlBasicValue &v) : m_v(v) {
|
||||
explicit TomlChecker(const TomlBasicValue &v) : m_v(v) {
|
||||
}
|
||||
TomlChecker(const TomlChecker &) = delete;
|
||||
TomlChecker(TomlChecker &&) = delete;
|
||||
@@ -140,7 +141,7 @@ class TomlChecker {
|
||||
for (const auto &itr : m_v.as_table()) {
|
||||
const auto &ky = itr.first;
|
||||
if (m_conditionVisited.contains(ky)) {
|
||||
if (!conditions.contains(ky)) {
|
||||
if (!conditions.contains(ky) && Project::is_condition_name(ky)) {
|
||||
throw_key_error("Unknown condition '" + ky + "'", ky, itr.second);
|
||||
}
|
||||
|
||||
@@ -160,7 +161,7 @@ class TomlChecker {
|
||||
throw_key_error("Unknown key '" + ky + "'", ky, itr.second);
|
||||
} else if (ky == "condition") {
|
||||
std::string condition = itr.second.as_string();
|
||||
if (!conditions.contains(condition)) {
|
||||
if (!conditions.contains(condition) && Project::is_condition_name(condition)) {
|
||||
throw_key_error("Unknown condition '" + condition + "'", condition, itr.second);
|
||||
}
|
||||
}
|
||||
@@ -175,7 +176,7 @@ class TomlCheckerRoot {
|
||||
bool m_checked = false;
|
||||
|
||||
public:
|
||||
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
|
||||
explicit TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
|
||||
}
|
||||
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
|
||||
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
|
||||
@@ -209,6 +210,105 @@ class TomlCheckerRoot {
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<std::string> parse_string_array(const TomlBasicValue &value, const toml::key &key) {
|
||||
if (!value.is_array()) {
|
||||
throw_key_error("Expected an array", key, value);
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
for (const auto &entry : value.as_array()) {
|
||||
if (!entry.is_string()) {
|
||||
throw_key_error("Expected an array of strings", key, entry);
|
||||
}
|
||||
values.push_back(entry.as_string());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<std::string>> parse_command_arguments(const TomlBasicValue &value, const toml::key &key) {
|
||||
if (!value.is_array()) {
|
||||
throw_key_error("Expected an array", key, value);
|
||||
}
|
||||
const auto &array = value.as_array();
|
||||
if (array.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool all_strings = true;
|
||||
bool all_arrays = true;
|
||||
for (const auto &entry : array) {
|
||||
if (!entry.is_string()) {
|
||||
all_strings = false;
|
||||
}
|
||||
if (!entry.is_array()) {
|
||||
all_arrays = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::string>> commands;
|
||||
if (all_strings) {
|
||||
commands.push_back(parse_string_array(value, key));
|
||||
if (commands[0].empty()) {
|
||||
throw_key_error("Command arrays cannot be empty", key, value);
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
if (!all_arrays) {
|
||||
throw_key_error("Expected an array of command arrays", key, value);
|
||||
}
|
||||
|
||||
for (const auto &entry : array) {
|
||||
auto command = parse_string_array(entry, key);
|
||||
if (command.empty()) {
|
||||
throw_key_error("Command arrays cannot be empty", key, entry);
|
||||
}
|
||||
commands.push_back(std::move(command));
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
static std::vector<Target::CustomCommand::ImplicitDependency> parse_implicit_dependencies(const TomlBasicValue &value, const toml::key &key) {
|
||||
if (!value.is_array()) {
|
||||
throw_key_error("Expected an array", key, value);
|
||||
}
|
||||
std::vector<Target::CustomCommand::ImplicitDependency> implicit_dependencies;
|
||||
for (const auto &entry : value.as_array()) {
|
||||
auto values = parse_string_array(entry, key);
|
||||
if (values.size() != 2) {
|
||||
throw_key_error("Each implicit dependency must have exactly two strings: [language, file]", key, entry);
|
||||
}
|
||||
Target::CustomCommand::ImplicitDependency implicit_dependency;
|
||||
implicit_dependency.language = std::move(values[0]);
|
||||
implicit_dependency.file = std::move(values[1]);
|
||||
implicit_dependencies.push_back(std::move(implicit_dependency));
|
||||
}
|
||||
return implicit_dependencies;
|
||||
}
|
||||
|
||||
static std::string normalize_build_event(std::string build_event) {
|
||||
for (auto &ch : build_event) {
|
||||
if (ch == '-') {
|
||||
ch = '_';
|
||||
} else {
|
||||
ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
|
||||
}
|
||||
}
|
||||
return build_event;
|
||||
}
|
||||
|
||||
static TargetType resolve_target_type(const Target &target, const std::vector<Template> &templates) {
|
||||
if (target.type != target_template) {
|
||||
return target.type;
|
||||
}
|
||||
for (const auto &tmplate : templates) {
|
||||
if (target.type_name == tmplate.outline.name) {
|
||||
return tmplate.outline.type;
|
||||
}
|
||||
}
|
||||
return target.type;
|
||||
}
|
||||
|
||||
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)) {
|
||||
@@ -265,12 +365,22 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
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["msvc"] = R"cmake(MSVC)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["clang-cl"] =
|
||||
R"cmake((CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$") OR (CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$"))cmake";
|
||||
conditions["clang-any"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")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";
|
||||
conditions["android"] = R"cmake(ANDROID)cmake";
|
||||
conditions["apple"] = R"cmake(APPLE)cmake";
|
||||
conditions["bsd"] = R"cmake(BSD)cmake";
|
||||
conditions["cygwin"] = R"cmake(CYGWIN)cmake";
|
||||
conditions["ios"] = R"cmake(IOS)cmake";
|
||||
conditions["xcode"] = R"cmake(XCODE)cmake";
|
||||
conditions["wince"] = R"cmake(WINCE)cmake";
|
||||
} else {
|
||||
conditions = parent->conditions;
|
||||
templates = parent->templates;
|
||||
@@ -279,6 +389,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
if (checker.contains("conditions")) {
|
||||
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
|
||||
for (const auto &cond : conds) {
|
||||
if (!is_condition_name(cond.first)) {
|
||||
throw_key_error("Invalid condition name '" + cond.first + "'", cond.first, toml::find(toml::find(toml, "conditions"), cond.first));
|
||||
}
|
||||
conditions[cond.first] = cond.second;
|
||||
}
|
||||
}
|
||||
@@ -288,6 +401,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
project.required("name", project_name);
|
||||
project.optional("version", project_version);
|
||||
project.optional("description", project_description);
|
||||
|
||||
// The implicit default is ["C", "CXX"], so make sure this list isn't
|
||||
// empty or projects without languages explicitly defined will error.
|
||||
project_languages[""] = {"C", "CXX"};
|
||||
|
||||
project.optional("languages", project_languages);
|
||||
project.optional("allow-unknown-languages", project_allow_unknown_languages);
|
||||
project.optional("cmake-before", cmake_before);
|
||||
@@ -369,6 +487,25 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
}
|
||||
|
||||
if (checker.contains("options")) {
|
||||
auto normalize = [](const std::string &name) {
|
||||
std::string normalized;
|
||||
for (char ch : name) {
|
||||
if (ch == '_') {
|
||||
normalized += '-';
|
||||
} else if (ch >= 'A' && ch <= 'Z') {
|
||||
ch += ('a' - 'A');
|
||||
normalized += ch;
|
||||
} else if (ch == '-' || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')) {
|
||||
normalized += ch;
|
||||
} else {
|
||||
// Ignore all other characters
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
};
|
||||
auto nproject_prefix = normalize(project_name);
|
||||
nproject_prefix += '-';
|
||||
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto &itr : opts) {
|
||||
@@ -406,7 +543,22 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
throw_key_error(toml::concat_to_string("Unsupported value type: ", itr.second.type()), itr.first, itr.second);
|
||||
}
|
||||
options.push_back(o);
|
||||
conditions.emplace(o.name, o.name);
|
||||
|
||||
// Add a condition matching the option name
|
||||
if (!conditions.emplace(o.name, o.name).second) {
|
||||
print_key_warning("Option '" + o.name + "' would create a condition '" + o.name + "' that already exists", o.name, value);
|
||||
}
|
||||
|
||||
// Add an implicit condition for the option
|
||||
auto ncondition = normalize(o.name);
|
||||
if (ncondition.find(nproject_prefix) == 0) {
|
||||
ncondition = ncondition.substr(nproject_prefix.size());
|
||||
}
|
||||
if (!ncondition.empty() && ncondition != o.name) {
|
||||
if (!conditions.emplace(ncondition, o.name).second) {
|
||||
print_key_warning("Option '" + o.name + "' would create a condition '" + ncondition + "' that already exists", o.name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,8 +595,47 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
c.optional("cmake-after", content.cmake_after);
|
||||
c.optional("include-before", content.include_before);
|
||||
c.optional("include-after", content.include_after);
|
||||
c.optional("system", content.system);
|
||||
|
||||
// Check if the minimum version requirement is satisfied (CMake 3.25)
|
||||
if (c.contains("system") && !this->cmake_minimum_version(3, 25)) {
|
||||
throw_key_error("The system argument is only supported on CMake version 3.25 and above.\nSet the CMake version in cmake.toml:\n"
|
||||
"[cmake]\n"
|
||||
"version = \"3.25\"\n",
|
||||
"system", "");
|
||||
}
|
||||
|
||||
// Parse options table - these are CACHE variables set before FetchContent
|
||||
if (c.contains("options")) {
|
||||
c.visit("options");
|
||||
const auto &opts = toml::find(itr.second, "options").as_table();
|
||||
for (const auto &optItr : opts) {
|
||||
ContentOption opt;
|
||||
if (optItr.second.is_boolean()) {
|
||||
opt.value = optItr.second.as_boolean();
|
||||
} else if (optItr.second.is_array()) {
|
||||
// Arrays become semicolon-separated lists
|
||||
std::string list_value;
|
||||
for (const auto &list_val : optItr.second.as_array()) {
|
||||
if (!list_value.empty()) {
|
||||
list_value += ';';
|
||||
}
|
||||
list_value += list_val.as_string();
|
||||
}
|
||||
opt.value = list_value;
|
||||
} else {
|
||||
opt.value = optItr.second.as_string();
|
||||
}
|
||||
content.options.emplace(optItr.first, opt);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &argItr : itr.second.as_table()) {
|
||||
// Skip keys that were already handled (like "options")
|
||||
if (c.visisted(argItr.first)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string value;
|
||||
if (argItr.second.is_array()) {
|
||||
for (const auto &list_val : argItr.second.as_array()) {
|
||||
@@ -488,13 +679,26 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
key = "URL";
|
||||
} else if (hash_algorithms.contains(key)) {
|
||||
std::string algo;
|
||||
for (auto c : key) {
|
||||
algo.push_back(std::toupper(c));
|
||||
for (auto ch : key) {
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
ch -= ('a' - 'A');
|
||||
}
|
||||
algo.push_back(ch);
|
||||
}
|
||||
key = "URL_HASH";
|
||||
if (value.empty()) {
|
||||
throw_key_error("Empty hash value", argItr.first, argItr.second);
|
||||
}
|
||||
for (char c : value) {
|
||||
if (!std::isxdigit(c)) {
|
||||
throw_key_error("Hash value must be a hex string", argItr.first, argItr.second);
|
||||
}
|
||||
}
|
||||
value = algo + "=" + value;
|
||||
} else if (key == "hash") {
|
||||
key = "URL_HASH";
|
||||
} else if (key == "subdir") {
|
||||
key = "SOURCE_SUBDIR";
|
||||
} else if (is_cmake_arg(key)) {
|
||||
// allow passthrough of ExternalProject options
|
||||
} else if (!c.visisted(key)) {
|
||||
@@ -585,36 +789,316 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
t.optional("link-libraries", target.link_libraries);
|
||||
t.optional("private-link-libraries", target.private_link_libraries);
|
||||
|
||||
// Add support for relative paths for (private-)link-libraries
|
||||
const auto fix_relative_paths = [&name, &path](ConditionVector &libraries, const char *key) {
|
||||
for (const auto &library_entries : libraries) {
|
||||
for (auto &library : libraries[library_entries.first]) {
|
||||
// Skip processing paths with potential CMake macros in them (this check isn't perfect)
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references
|
||||
if ((library.find("${") != std::string::npos || library.find("$ENV{") != std::string::npos ||
|
||||
library.find("$CACHE{") != std::string::npos) &&
|
||||
library.find('}') != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto library_path = fs::path(path) / library;
|
||||
if (fs::exists(library_path)) {
|
||||
if (!fs::is_directory(library_path)) {
|
||||
// If the file path is relative (and not a directory), prepend ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
library.insert(0, "${CMAKE_CURRENT_SOURCE_DIR}/");
|
||||
}
|
||||
} else if (library.find_first_of(R"(\/)") != std::string::npos) {
|
||||
// Error if the path contains a directory separator and the file doesn't exist
|
||||
throw std::runtime_error("Attempted to link against a library file that doesn't exist for target \"" + name + "\" in \"" +
|
||||
key + "\": " + library);
|
||||
} else {
|
||||
// NOTE: We cannot check if system libraries exist, so we leave them as-is
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
fix_relative_paths(target.link_libraries, "link-libraries");
|
||||
fix_relative_paths(target.private_link_libraries, "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);
|
||||
|
||||
t.optional("dependencies", target.dependencies);
|
||||
|
||||
const auto resolved_target_type = resolve_target_type(target, templates);
|
||||
|
||||
if (t.contains("all")) {
|
||||
target.custom_target.has_all = true;
|
||||
target.custom_target.all = t.find("all").as_boolean();
|
||||
}
|
||||
|
||||
if (t.contains("command")) {
|
||||
target.custom_target.has_command = true;
|
||||
auto commands = parse_command_arguments(t.find("command"), "command");
|
||||
for (auto &command : commands) {
|
||||
target.custom_target.commands.push_back(std::move(command));
|
||||
}
|
||||
}
|
||||
|
||||
if (t.contains("commands")) {
|
||||
target.custom_target.has_commands = true;
|
||||
auto commands = parse_command_arguments(t.find("commands"), "commands");
|
||||
for (auto &command : commands) {
|
||||
target.custom_target.commands.push_back(std::move(command));
|
||||
}
|
||||
}
|
||||
|
||||
t.optional("depends", target.custom_target.depends);
|
||||
t.optional("byproducts", target.custom_target.byproducts);
|
||||
|
||||
if (t.contains("working-directory")) {
|
||||
target.custom_target.has_working_directory = true;
|
||||
target.custom_target.working_directory = t.find("working-directory").as_string();
|
||||
}
|
||||
|
||||
if (t.contains("comment")) {
|
||||
target.custom_target.has_comment = true;
|
||||
target.custom_target.comment = t.find("comment").as_string();
|
||||
}
|
||||
|
||||
if (t.contains("job-pool")) {
|
||||
target.custom_target.has_job_pool = true;
|
||||
target.custom_target.job_pool = t.find("job-pool").as_string();
|
||||
}
|
||||
|
||||
if (t.contains("job-server-aware")) {
|
||||
target.custom_target.has_job_server_aware = true;
|
||||
target.custom_target.job_server_aware = t.find("job-server-aware").as_boolean();
|
||||
}
|
||||
|
||||
if (t.contains("verbatim")) {
|
||||
target.custom_target.has_verbatim = true;
|
||||
target.custom_target.verbatim = t.find("verbatim").as_boolean();
|
||||
}
|
||||
|
||||
if (t.contains("uses-terminal")) {
|
||||
target.custom_target.has_uses_terminal = true;
|
||||
target.custom_target.uses_terminal = t.find("uses-terminal").as_boolean();
|
||||
}
|
||||
|
||||
if (t.contains("command-expand-lists")) {
|
||||
target.custom_target.has_command_expand_lists = true;
|
||||
target.custom_target.command_expand_lists = t.find("command-expand-lists").as_boolean();
|
||||
}
|
||||
|
||||
if (t.contains("custom-command")) {
|
||||
const auto &custom_commands = t.find("custom-command");
|
||||
if (!custom_commands.is_array()) {
|
||||
throw_key_error("Expected an array of tables", "custom-command", custom_commands);
|
||||
}
|
||||
for (const auto &custom_command_value : custom_commands.as_array()) {
|
||||
if (!custom_command_value.is_table()) {
|
||||
throw_key_error("Expected an array of tables", "custom-command", custom_command_value);
|
||||
}
|
||||
|
||||
auto &custom = checker.create(custom_command_value);
|
||||
Target::CustomCommand custom_command;
|
||||
|
||||
custom.optional("condition", custom_command.condition);
|
||||
|
||||
if (custom.contains("command")) {
|
||||
auto commands = parse_command_arguments(custom.find("command"), "command");
|
||||
for (auto &command : commands) {
|
||||
custom_command.commands.push_back(std::move(command));
|
||||
}
|
||||
}
|
||||
|
||||
if (custom.contains("commands")) {
|
||||
auto commands = parse_command_arguments(custom.find("commands"), "commands");
|
||||
for (auto &command : commands) {
|
||||
custom_command.commands.push_back(std::move(command));
|
||||
}
|
||||
}
|
||||
|
||||
custom.optional("outputs", custom_command.outputs);
|
||||
|
||||
if (custom.contains("build-event")) {
|
||||
custom_command.build_event = normalize_build_event(custom.find("build-event").as_string());
|
||||
}
|
||||
|
||||
if (custom.contains("append")) {
|
||||
custom_command.has_append = true;
|
||||
custom_command.append = custom.find("append").as_boolean();
|
||||
}
|
||||
|
||||
if (custom.contains("main-dependency")) {
|
||||
custom_command.has_main_dependency = true;
|
||||
custom_command.main_dependency = custom.find("main-dependency").as_string();
|
||||
}
|
||||
|
||||
custom.optional("depends", custom_command.depends);
|
||||
custom.optional("byproducts", custom_command.byproducts);
|
||||
|
||||
if (custom.contains("implicit-depends")) {
|
||||
custom_command.implicit_depends = parse_implicit_dependencies(custom.find("implicit-depends"), "implicit-depends");
|
||||
}
|
||||
|
||||
if (custom.contains("working-directory")) {
|
||||
custom_command.has_working_directory = true;
|
||||
custom_command.working_directory = custom.find("working-directory").as_string();
|
||||
}
|
||||
|
||||
if (custom.contains("comment")) {
|
||||
custom_command.has_comment = true;
|
||||
custom_command.comment = custom.find("comment").as_string();
|
||||
}
|
||||
|
||||
if (custom.contains("depfile")) {
|
||||
custom_command.has_depfile = true;
|
||||
custom_command.depfile = custom.find("depfile").as_string();
|
||||
}
|
||||
|
||||
if (custom.contains("job-pool")) {
|
||||
custom_command.has_job_pool = true;
|
||||
custom_command.job_pool = custom.find("job-pool").as_string();
|
||||
}
|
||||
|
||||
if (custom.contains("job-server-aware")) {
|
||||
custom_command.has_job_server_aware = true;
|
||||
custom_command.job_server_aware = custom.find("job-server-aware").as_boolean();
|
||||
}
|
||||
|
||||
if (custom.contains("verbatim")) {
|
||||
custom_command.has_verbatim = true;
|
||||
custom_command.verbatim = custom.find("verbatim").as_boolean();
|
||||
}
|
||||
|
||||
if (custom.contains("uses-terminal")) {
|
||||
custom_command.has_uses_terminal = true;
|
||||
custom_command.uses_terminal = custom.find("uses-terminal").as_boolean();
|
||||
}
|
||||
|
||||
if (custom.contains("codegen")) {
|
||||
custom_command.has_codegen = true;
|
||||
custom_command.codegen = custom.find("codegen").as_boolean();
|
||||
}
|
||||
|
||||
if (custom.contains("command-expand-lists")) {
|
||||
custom_command.has_command_expand_lists = true;
|
||||
custom_command.command_expand_lists = custom.find("command-expand-lists").as_boolean();
|
||||
}
|
||||
|
||||
if (custom.contains("depends-explicit-only")) {
|
||||
custom_command.has_depends_explicit_only = true;
|
||||
custom_command.depends_explicit_only = custom.find("depends-explicit-only").as_boolean();
|
||||
}
|
||||
|
||||
if (custom_command.is_output_form() == custom_command.is_target_form()) {
|
||||
throw_key_error("Specify exactly one of outputs or build-event", "custom-command", custom_command_value);
|
||||
}
|
||||
|
||||
if (custom_command.is_target_form()) {
|
||||
if (custom_command.build_event != "PRE_BUILD" && custom_command.build_event != "PRE_LINK" &&
|
||||
custom_command.build_event != "POST_BUILD") {
|
||||
throw_key_error("build-event must be one of: pre-build, pre-link, post-build", "build-event", custom.find("build-event"));
|
||||
}
|
||||
if (resolved_target_type == target_interface) {
|
||||
throw_key_error("build-event cannot be used with type = \"interface\"", "build-event", custom.find("build-event"));
|
||||
}
|
||||
if (resolved_target_type == target_custom && custom_command.build_event == "PRE_LINK") {
|
||||
throw_key_error("build-event = \"pre-link\" cannot be used with type = \"custom\"", "build-event",
|
||||
custom.find("build-event"));
|
||||
}
|
||||
if (custom_command.has_append || custom_command.has_main_dependency || !custom_command.depends.empty() ||
|
||||
!custom_command.implicit_depends.empty() || custom_command.has_depfile || custom_command.has_job_pool ||
|
||||
custom_command.has_job_server_aware || custom_command.has_codegen || custom_command.has_depends_explicit_only) {
|
||||
throw_key_error("Unsupported option for TARGET custom commands", "custom-command", custom_command_value);
|
||||
}
|
||||
} else {
|
||||
if (custom_command.has_depfile && !custom_command.implicit_depends.empty()) {
|
||||
throw_key_error("depfile cannot be used with implicit-depends", "depfile", custom.find("depfile"));
|
||||
}
|
||||
if (custom_command.has_job_pool && custom_command.has_uses_terminal && custom_command.uses_terminal) {
|
||||
throw_key_error("job-pool cannot be used with uses-terminal", "job-pool", custom.find("job-pool"));
|
||||
}
|
||||
if (custom_command.append &&
|
||||
(custom_command.has_depfile || custom_command.has_job_pool || custom_command.has_job_server_aware ||
|
||||
custom_command.has_codegen || custom_command.has_command_expand_lists || custom_command.has_uses_terminal ||
|
||||
custom_command.has_verbatim || custom_command.has_depends_explicit_only || !custom_command.byproducts.empty())) {
|
||||
throw_key_error("append cannot be used with depfile, job-pool, job-server-aware, codegen, command-expand-lists, "
|
||||
"uses-terminal, verbatim, depends-explicit-only, or byproducts",
|
||||
"append", custom.find("append"));
|
||||
}
|
||||
}
|
||||
|
||||
if (custom_command.is_output_form() && custom_command.append && custom_command.commands.empty() && custom_command.depends.empty()) {
|
||||
throw_key_error("append requires at least one command or depends", "append", custom.find("append"));
|
||||
}
|
||||
|
||||
if (custom_command.commands.empty() &&
|
||||
!(custom_command.is_output_form() && custom_command.append && !custom_command.depends.empty())) {
|
||||
throw_key_error("At least one command is required", "custom-command", custom_command_value);
|
||||
}
|
||||
|
||||
target.custom_commands.push_back(std::move(custom_command));
|
||||
}
|
||||
}
|
||||
|
||||
if (resolved_target_type == target_custom && target.custom_target.has_job_pool && target.custom_target.has_uses_terminal &&
|
||||
target.custom_target.uses_terminal) {
|
||||
throw_key_error("job-pool cannot be used with uses-terminal", "job-pool", t.find("job-pool"));
|
||||
}
|
||||
|
||||
if (resolved_target_type != target_custom && !target.custom_target.empty()) {
|
||||
const char *custom_key = "all";
|
||||
if (target.custom_target.has_command) {
|
||||
custom_key = "command";
|
||||
} else if (target.custom_target.has_commands) {
|
||||
custom_key = "commands";
|
||||
} else if (!target.custom_target.depends.empty()) {
|
||||
custom_key = "depends";
|
||||
} else if (!target.custom_target.byproducts.empty()) {
|
||||
custom_key = "byproducts";
|
||||
} else if (target.custom_target.has_working_directory) {
|
||||
custom_key = "working-directory";
|
||||
} else if (target.custom_target.has_comment) {
|
||||
custom_key = "comment";
|
||||
} else if (target.custom_target.has_job_pool) {
|
||||
custom_key = "job-pool";
|
||||
} else if (target.custom_target.has_job_server_aware) {
|
||||
custom_key = "job-server-aware";
|
||||
} else if (target.custom_target.has_verbatim) {
|
||||
custom_key = "verbatim";
|
||||
} else if (target.custom_target.has_uses_terminal) {
|
||||
custom_key = "uses-terminal";
|
||||
} else if (target.custom_target.has_command_expand_lists) {
|
||||
custom_key = "command-expand-lists";
|
||||
}
|
||||
throw_key_error("Custom target options can only be used with type = \"custom\"", custom_key, t.find(custom_key));
|
||||
}
|
||||
|
||||
Condition<std::string> msvc_runtime;
|
||||
t.optional("msvc-runtime", msvc_runtime);
|
||||
for (const auto &condItr : msvc_runtime) {
|
||||
switch (parse_msvcRuntimeType(condItr.second)) {
|
||||
for (const auto &cond_itr : msvc_runtime) {
|
||||
switch (parse_msvcRuntimeType(cond_itr.second)) {
|
||||
case msvc_dynamic:
|
||||
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
|
||||
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
|
||||
break;
|
||||
case msvc_static:
|
||||
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
|
||||
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
|
||||
break;
|
||||
default: {
|
||||
std::string error = "Unknown runtime '" + condItr.second + "'\n";
|
||||
std::string error = "Unknown runtime '" + cond_itr.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()) {
|
||||
if (cond_itr.first.empty()) {
|
||||
report = &t.find("msvc-runtime");
|
||||
} else {
|
||||
report = &t.find(condItr.first).as_table().find("msvc-runtime").value();
|
||||
report = &t.find(cond_itr.first).as_table().find("msvc-runtime").value();
|
||||
}
|
||||
throw_key_error(error, condItr.second, *report);
|
||||
throw_key_error(error, cond_itr.second, *report);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,6 +1117,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
property_list += list_val.as_string();
|
||||
}
|
||||
target.properties[condition][k] = property_list;
|
||||
} else if (v.is_integer()) {
|
||||
target.properties[condition][k] = std::to_string(v.as_integer());
|
||||
} else if (v.is_boolean()) {
|
||||
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
|
||||
} else {
|
||||
@@ -684,6 +1170,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
tmplate.outline = parse_target(name, t, true);
|
||||
|
||||
t.optional("add-function", tmplate.add_function);
|
||||
t.optional("add-arguments", tmplate.add_arguments);
|
||||
t.optional("pass-sources-to-add-function", tmplate.pass_sources_to_add_function);
|
||||
t.optional("pass-sources", tmplate.pass_sources_to_add_function);
|
||||
|
||||
@@ -749,13 +1236,33 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
std::istringstream feature_stream{features};
|
||||
std::string feature;
|
||||
while (std::getline(feature_stream, feature, ',')) {
|
||||
package.features.emplace_back(feature);
|
||||
// Disable default features with package-name[core,feature1]
|
||||
if (feature == "core") {
|
||||
package.default_features = false;
|
||||
} else {
|
||||
package.features.emplace_back(feature);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw_key_error("Invalid package name '" + package_str + "'", "packages", p);
|
||||
}
|
||||
vcpkg.packages.emplace_back(std::move(package));
|
||||
}
|
||||
|
||||
if (v.contains("overlay")) {
|
||||
std::string overlay;
|
||||
v.optional("overlay", overlay);
|
||||
vcpkg.overlay_triplets = vcpkg.overlay_ports = {overlay};
|
||||
if (v.contains("overlay-ports")) {
|
||||
throw_key_error("[vcpkg].overlay was already specified", "overlay-ports", v.find("overlay-ports"));
|
||||
}
|
||||
if (v.contains("overlay-triplets")) {
|
||||
throw_key_error("[vcpkg].overlay was already specified", "overlay-triplets", v.find("overlay-triplets"));
|
||||
}
|
||||
} else {
|
||||
v.optional("overlay-ports", vcpkg.overlay_ports);
|
||||
v.optional("overlay-triplets", vcpkg.overlay_triplets);
|
||||
}
|
||||
}
|
||||
|
||||
checker.check(conditions, true);
|
||||
@@ -771,7 +1278,6 @@ const Project *Project::root() const {
|
||||
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);
|
||||
@@ -788,6 +1294,15 @@ bool Project::cmake_minimum_version(int major, int minor) const {
|
||||
return std::tie(root_major, root_minor) >= std::tie(major, minor);
|
||||
}
|
||||
|
||||
bool Project::is_condition_name(const std::string &name) {
|
||||
for (auto ch : name) {
|
||||
if (!std::isalnum(ch) && ch != '-' && ch != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_root_path(const std::string &path) {
|
||||
const auto toml_path = fs::path(path) / "cmake.toml";
|
||||
if (!fs::exists(toml_path)) {
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
# These will be generated by cmkr, so no point in tracking them
|
||||
**/CMakeLists.txt
|
||||
**/cmkr.cmake
|
||||
**/cmkr.cmake
|
||||
**/vcpkg.json
|
||||
Generated
+61
@@ -100,3 +100,64 @@ if(MSVC) # msvc
|
||||
)
|
||||
|
||||
endif()
|
||||
add_test(
|
||||
NAME
|
||||
compile-options
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/compile-options"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
if(WIN32) # windows
|
||||
add_test(
|
||||
NAME
|
||||
relative-paths
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/relative-paths"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
endif()
|
||||
add_test(
|
||||
NAME
|
||||
objective-c
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/objective-c"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
custom-command
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/custom-command"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
generator-executable
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/generator-executable"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
protobuf
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/protobuf"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -8,4 +8,4 @@ description = "Minimal example"
|
||||
type = "executable"
|
||||
sources = ["src/basic.cpp"]
|
||||
|
||||
# Declares an executable target called `basic` with `src/basic.cpp` as a source file. Equivalent to CMake's [add_executable](https://cmake.org/cmake/help/latest/command/add_executable.html)`(basic src/basic.cpp)`.
|
||||
# Declares an executable target called `basic` with `src/basic.cpp` as a source file. Equivalent to CMake's [add_executable](https://cmake.org/cmake/help/latest/command/add_executable.html)`(basic src/basic.cpp)`.
|
||||
|
||||
+38
-1
@@ -51,4 +51,41 @@ condition = "msvc"
|
||||
name = "msvc-runtime"
|
||||
working-directory = "msvc-runtime"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "compile-options"
|
||||
working-directory = "compile-options"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
condition = "windows"
|
||||
name = "relative-paths"
|
||||
working-directory = "relative-paths"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "objective-c"
|
||||
working-directory = "objective-c"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "custom-command"
|
||||
working-directory = "custom-command"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "generator-executable"
|
||||
working-directory = "generator-executable"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "protobuf"
|
||||
working-directory = "protobuf"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Example project that sets compiler flags and preprocessor defines for various platforms.
|
||||
|
||||
[project]
|
||||
name = "compile-options"
|
||||
description = "Compiler flags"
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc.compile-options = ["/W2"]
|
||||
msvc.compile-definitions = ["PLATFORM=\"msvc\""]
|
||||
gcc.compile-options = ["-Wall"]
|
||||
gcc.compile-definitions = ["PLATFORM=\"gcc\""]
|
||||
clang.compile-options = ["-Wall"]
|
||||
clang.compile-definitions = ["PLATFORM=\"clang\""]
|
||||
|
||||
# The `hello` target uses [conditions](/cmake-toml#conditions) to set different compiler flags and definitions depending on the platform. See the [targets](/cmake-toml/#targets) documentation for other things you can set.
|
||||
# _Note_: In general you only want to specify flags _required_ to compile your code without errors.
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr!");
|
||||
}
|
||||
+28
-23
@@ -1,23 +1,28 @@
|
||||
[project]
|
||||
name = "conditions"
|
||||
cmake-after = "set(CUSTOM ON)"
|
||||
|
||||
[conditions]
|
||||
custom = "CUSTOM"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
cmake-after = "message(STATUS cmake-after)"
|
||||
windows.cmake-after = "message(STATUS win32-after)"
|
||||
macos.cmake-after = "message(STATUS macos-after)"
|
||||
linux.cmake-after = "message(STATUS linux-after)"
|
||||
unix.cmake-after = "message(STATUS unix-after)"
|
||||
custom.cmake-after = "message(STATUS custom-after)"
|
||||
|
||||
[target.example.properties]
|
||||
AUTOMOC = false
|
||||
custom.OUTPUT_NAME = "example2"
|
||||
custom.AUTORCC = true
|
||||
AUTOGEN = "ON"
|
||||
[project]
|
||||
name = "conditions"
|
||||
cmake-after = "set(CUSTOM ON)"
|
||||
|
||||
[options]
|
||||
CONDITIONS_BUILD_TESTS = "root"
|
||||
|
||||
[conditions]
|
||||
custom = "CUSTOM"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
cmake-after = "message(STATUS cmake-after)"
|
||||
windows.cmake-after = "message(STATUS win32-after)"
|
||||
macos.cmake-after = "message(STATUS macos-after)"
|
||||
linux.cmake-after = "message(STATUS linux-after)"
|
||||
unix.cmake-after = "message(STATUS unix-after)"
|
||||
custom.cmake-after = "message(STATUS custom-after)"
|
||||
build-tests.cmake-after = "message(STATUS build-tests)"
|
||||
"CONDITIONS_BUILD_TESTS AND $<linux>".cmake-after = "message(STATUS linux-tests)"
|
||||
|
||||
[target.example.properties]
|
||||
AUTOMOC = false
|
||||
custom.OUTPUT_NAME = "example2"
|
||||
custom.AUTORCC = true
|
||||
AUTOGEN = "ON"
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
int main() { }
|
||||
int main() {
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <Windows.h>
|
||||
|
||||
void foo() { }
|
||||
#include <Windows.h>
|
||||
|
||||
void foo() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# This test demonstrates add_custom_command and add_custom_target support in cmkr.
|
||||
#
|
||||
# There are two forms of add_custom_command in CMake:
|
||||
# 1. Output form: generates files that can be consumed by other targets
|
||||
# 2. Target form: runs commands at build time for a specific target (pre-build, pre-link, post-build)
|
||||
|
||||
[project]
|
||||
name = "custom-command"
|
||||
description = "Tests add_custom_command and add_custom_target support"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Simple executable demonstrating post-build events
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/hello.cpp"]
|
||||
|
||||
# This post-build command runs after the 'hello' executable is built.
|
||||
# build-event can be: "pre-build", "pre-link", or "post-build"
|
||||
[[target.hello.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Built executable: $<TARGET_FILE_NAME:hello>"]
|
||||
comment = "Print the built executable name"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Executable with code generation (output form custom command)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.custom-command]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Output form: This custom command generates source files before building.
|
||||
# Relative outputs follow CMake and are interpreted from the binary directory.
|
||||
# The outputs are automatically added as sources to the target.
|
||||
[[target.custom-command.custom-command]]
|
||||
outputs = ["generated/generated.cpp"]
|
||||
byproducts = ["generated/generated.hpp"]
|
||||
depends = ["cmake/generate_source.cmake"]
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-DOUTPUT_CPP=${CMAKE_CURRENT_BINARY_DIR}/generated/generated.cpp",
|
||||
"-DOUTPUT_HPP=${CMAKE_CURRENT_BINARY_DIR}/generated/generated.hpp",
|
||||
"-P",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_source.cmake",
|
||||
]
|
||||
comment = "Generate source files"
|
||||
verbatim = true
|
||||
|
||||
# Target form: This post-build command runs after 'custom-command' is built.
|
||||
[[target.custom-command.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-E",
|
||||
"touch",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/custom-command-post-build.stamp",
|
||||
]
|
||||
byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/custom-command-post-build.stamp"]
|
||||
comment = "Create a post-build stamp file"
|
||||
verbatim = true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Custom target (add_custom_target)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# A custom target runs commands independently of any executable/library.
|
||||
# Setting 'all = true' makes it run as part of the default build.
|
||||
[target.custom-codegen]
|
||||
type = "custom"
|
||||
all = true
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-E",
|
||||
"touch",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/custom-target.stamp",
|
||||
]
|
||||
byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/custom-target.stamp"]
|
||||
comment = "Run custom target"
|
||||
verbatim = true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Template-based custom target
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Custom target options should also work when the target type comes from a template.
|
||||
[template.generated-custom-target]
|
||||
type = "custom"
|
||||
all = true
|
||||
verbatim = true
|
||||
|
||||
[target.custom-codegen-template]
|
||||
type = "generated-custom-target"
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-DINPUT=${CMAKE_CURRENT_BINARY_DIR}/template-custom-target.stamp",
|
||||
"-P",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/verify_file_exists.cmake",
|
||||
]
|
||||
comment = "Verify the generated stamp exists"
|
||||
|
||||
# The output-form custom command should automatically become a dependency of the
|
||||
# custom target, so the verification command sees the stamp file.
|
||||
[[target.custom-codegen-template.custom-command]]
|
||||
outputs = ["template-custom-target.stamp"]
|
||||
command = [
|
||||
"${CMAKE_COMMAND}",
|
||||
"-E",
|
||||
"touch",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/template-custom-target.stamp",
|
||||
]
|
||||
comment = "Create a stamp file for the template-based custom target"
|
||||
verbatim = true
|
||||
|
||||
# A target-level all = false should override the template's all = true.
|
||||
# If this target is incorrectly included in the default build, the build fails.
|
||||
[target.custom-codegen-template-disabled]
|
||||
type = "generated-custom-target"
|
||||
all = false
|
||||
command = ["${CMAKE_COMMAND}", "-E", "false"]
|
||||
comment = "This target must not run as part of the default build"
|
||||
@@ -0,0 +1,13 @@
|
||||
if(NOT DEFINED OUTPUT_CPP)
|
||||
message(FATAL_ERROR "OUTPUT_CPP is required")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED OUTPUT_HPP)
|
||||
message(FATAL_ERROR "OUTPUT_HPP is required")
|
||||
endif()
|
||||
|
||||
get_filename_component(_output_dir "${OUTPUT_CPP}" DIRECTORY)
|
||||
file(MAKE_DIRECTORY "${_output_dir}")
|
||||
|
||||
file(WRITE "${OUTPUT_HPP}" "#pragma once\n#define GENERATED_VALUE 42\n")
|
||||
file(WRITE "${OUTPUT_CPP}" "#include \"generated.hpp\"\nint generated_value() { return GENERATED_VALUE; }\n")
|
||||
@@ -0,0 +1,9 @@
|
||||
if(NOT DEFINED INPUT)
|
||||
message(FATAL_ERROR "INPUT is required")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${INPUT}")
|
||||
message(FATAL_ERROR "Expected file not found: ${INPUT}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Verified file exists: ${INPUT}")
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, world!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "generated.hpp"
|
||||
|
||||
int generated_value();
|
||||
|
||||
int main() {
|
||||
return generated_value() == GENERATED_VALUE ? 0 : 1;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
# Require a C++11 compiler for the target `example`.
|
||||
|
||||
[project]
|
||||
name = "cxx-standard"
|
||||
description = "Changing C++ standard"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
# This is equivalent to CMake's [target_compile_features](https://cmake.org/cmake/help/latest/command/target_compile_features.html)`(example PRIVATE cxx_std_11)`. For more information on available C/C++ standards and features see [cmake-compile-features(7)](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html).
|
||||
# Require a C++11 compiler for the target `example`.
|
||||
|
||||
[project]
|
||||
name = "cxx-standard"
|
||||
description = "Changing C++ standard"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
# This is equivalent to CMake's [target_compile_features](https://cmake.org/cmake/help/latest/command/target_compile_features.html)`(example PRIVATE cxx_std_11)`. For more information on available C/C++ standards and features see [cmake-compile-features(7)](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html).
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
||||
int main()
|
||||
{
|
||||
auto tpl = std::make_tuple(1, 2);
|
||||
printf("Hello from C++11 %d\n", std::get<0>(tpl));
|
||||
}
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
||||
int main() {
|
||||
auto tpl = std::make_tuple(1, 2);
|
||||
printf("Hello from C++11 %d\n", std::get<0>(tpl));
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
name = "fetch-content"
|
||||
description = "Fetching from git"
|
||||
|
||||
[fetch-content]
|
||||
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
|
||||
[fetch-content.fmt]
|
||||
git = "https://github.com/fmtlib/fmt"
|
||||
tag = "7.1.3"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["fmt::fmt"]
|
||||
|
||||
# This is equivalent to calling CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html).
|
||||
# This is equivalent to calling CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html).
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int main() {
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# This test demonstrates a common pattern: using an executable to generate sources
|
||||
# for another target. The generator runs at build time and produces code that is
|
||||
# consumed by the main executable.
|
||||
#
|
||||
# CMake handles the dependency automatically: the generator executable is built
|
||||
# first, then it runs to produce the generated sources, and finally the main
|
||||
# executable is built using those sources.
|
||||
|
||||
[project]
|
||||
name = "generator-executable"
|
||||
description = "Tests using an executable to generate sources for another target"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generator executable: produces code at build time
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.generate_numbers]
|
||||
type = "executable"
|
||||
sources = ["src/generate_numbers.cpp"]
|
||||
|
||||
# Post-build: confirm the generator was built
|
||||
[[target.generate_numbers.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Generator built successfully: $<TARGET_FILE_NAME:generate_numbers>"]
|
||||
comment = "Confirm generator executable was built"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main executable: uses generated sources
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.main]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Create the generated directory before the generator runs.
|
||||
cmake-before = """
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
"""
|
||||
|
||||
# Output form custom command: runs the generate_numbers executable to generate sources.
|
||||
# The outputs are automatically added as sources to this target.
|
||||
# The DEPENDS on "generate_numbers" ensures the generator is built before this runs.
|
||||
[[target.main.custom-command]]
|
||||
outputs = ["${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
depends = ["generate_numbers"]
|
||||
command = ["$<TARGET_FILE:generate_numbers>", "${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
comment = "Run generate_numbers to generate numbers.cpp"
|
||||
|
||||
# Post-build: run the executable to verify it works
|
||||
[[target.main.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["$<TARGET_FILE:main>"]
|
||||
comment = "Run the main executable to verify generated code works"
|
||||
@@ -0,0 +1,44 @@
|
||||
// Simple code generator that produces a C++ source file with a function
|
||||
// that returns a vector of numbers.
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Usage: " << argv[0] << " <output_file>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string output_path = argv[1];
|
||||
|
||||
// The example ensures the output directory exists before invoking the generator.
|
||||
|
||||
std::ofstream out(output_path);
|
||||
if (!out) {
|
||||
std::cerr << "Failed to open: " << output_path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
out << "// Auto-generated by codegen - DO NOT EDIT\n";
|
||||
out << "#include <vector>\n";
|
||||
out << "#include <cstddef>\n";
|
||||
out << "\n";
|
||||
out << "std::vector<int> get_numbers() {\n";
|
||||
out << " return {";
|
||||
for (int i = 1; i <= 10; ++i) {
|
||||
if (i > 1)
|
||||
out << ", ";
|
||||
out << i * i; // 1, 4, 9, 16, 25, 36, 49, 64, 81, 100
|
||||
}
|
||||
out << "};\n";
|
||||
out << "}\n";
|
||||
out << "\n";
|
||||
out << "std::size_t get_numbers_count() {\n";
|
||||
out << " return 10;\n";
|
||||
out << "}\n";
|
||||
|
||||
std::cout << "Generated: " << output_path << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Main executable that uses the generated code
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
// Functions generated by codegen executable
|
||||
std::vector<int> get_numbers();
|
||||
std::size_t get_numbers_count();
|
||||
|
||||
int main() {
|
||||
std::cout << "Numbers from generated code:" << std::endl;
|
||||
|
||||
auto numbers = get_numbers();
|
||||
for (std::size_t i = 0; i < numbers.size(); ++i) {
|
||||
std::cout << " numbers[" << i << "] = " << numbers[i] << std::endl;
|
||||
}
|
||||
|
||||
// Verify the count
|
||||
if (numbers.size() == get_numbers_count()) {
|
||||
std::cout << "Success: Generated " << numbers.size() << " numbers!" << std::endl;
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "Error: Count mismatch!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+18
-18
@@ -1,18 +1,18 @@
|
||||
[project]
|
||||
name = "globbing"
|
||||
description = "Globbing sources"
|
||||
|
||||
# Recursively glob in the mylib/ folder
|
||||
[target.mylib]
|
||||
type = "static"
|
||||
alias = "mylib::mylib"
|
||||
sources = ["mylib/**.hpp", "mylib/**.cpp"]
|
||||
include-directories = ["mylib/include"]
|
||||
|
||||
# Single-folder glob in example/src/
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["example/src/*.cpp"]
|
||||
link-libraries = ["mylib::mylib"]
|
||||
|
||||
# As you can see in the example above you can use `**.ext` to glob recursively and `*.ext` to glob non-recursively. This **does not** generate `file(GLOB ...)` commands, but instead globs when cmkr is run. Files are sorted to give deterministic results regardless of the platform used.
|
||||
[project]
|
||||
name = "globbing"
|
||||
description = "Globbing sources"
|
||||
|
||||
# Recursively glob in the mylib/ folder
|
||||
[target.mylib]
|
||||
type = "static"
|
||||
alias = "mylib::mylib"
|
||||
sources = ["mylib/**.hpp", "mylib/**.cpp"]
|
||||
include-directories = ["mylib/include"]
|
||||
|
||||
# Single-folder glob in example/src/
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["example/src/*.cpp"]
|
||||
link-libraries = ["mylib::mylib"]
|
||||
|
||||
# As you can see in the example above you can use `**.ext` to glob recursively and `*.ext` to glob non-recursively. This **does not** generate `file(GLOB ...)` commands, but instead globs when cmkr is run. Files are sorted to give deterministic results regardless of the platform used.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
int main() {
|
||||
std::cout << mylib::message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
#include <iostream>
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
int main() {
|
||||
std::cout << mylib::message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mylib
|
||||
{
|
||||
std::string message();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mylib {
|
||||
std::string message();
|
||||
} // namespace mylib
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
std::string mylib::message()
|
||||
{
|
||||
return "cmkr is awesome!";
|
||||
}
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
std::string mylib::message() {
|
||||
return "cmkr is awesome!";
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,13 +1,13 @@
|
||||
[project]
|
||||
name = "interface"
|
||||
description = "Header-only library"
|
||||
|
||||
[target.mylib]
|
||||
type = "interface"
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["mylib"]
|
||||
[project]
|
||||
name = "interface"
|
||||
description = "Header-only library"
|
||||
|
||||
[target.mylib]
|
||||
type = "interface"
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["mylib"]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace mylib
|
||||
{
|
||||
static const char* version() { return "v1.0"; }
|
||||
} // namespace mylib
|
||||
namespace mylib {
|
||||
static const char *version() {
|
||||
return "v1.0";
|
||||
}
|
||||
} // namespace mylib
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include "mylib/mylib.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("mylib version: %s\n", mylib::version());
|
||||
}
|
||||
#include <cstdio>
|
||||
|
||||
#include "mylib/mylib.hpp"
|
||||
|
||||
int main() {
|
||||
printf("mylib version: %s\n", mylib::version());
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ sources = ["src/main.cpp"]
|
||||
[target.dynamic-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc-runtime = "dynamic"
|
||||
msvc-runtime = "dynamic"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Add Objective-C sources on Apple platforms:
|
||||
|
||||
[project]
|
||||
name = "objective-c"
|
||||
description = "Objective-C"
|
||||
languages = ["C"]
|
||||
apple.languages = ["OBJC"]
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.c"]
|
||||
apple.sources = ["src/apple.m"]
|
||||
apple.link-libraries = ["$<LINK_LIBRARY:FRAMEWORK,Foundation>"]
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void platform_specific() {
|
||||
NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
|
||||
NSString *message = @"Hello from Objective-C!\n";
|
||||
[stdout writeData:[message dataUsingEncoding:NSUTF8StringEncoding] error:nil];
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
void platform_specific();
|
||||
#else
|
||||
void platform_specific() {
|
||||
puts("This is not an Apple platform.");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
platform_specific();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
build/
|
||||
*.bin
|
||||
@@ -0,0 +1,81 @@
|
||||
# This test demonstrates using protobuf with cmkr for code generation.
|
||||
#
|
||||
# The example:
|
||||
# 1. Fetches protobuf from GitHub using FetchContent
|
||||
# 2. Uses a custom command to generate C++ sources from a .proto file
|
||||
# 3. Links an executable against the generated sources and protobuf library
|
||||
# 4. Serializes and deserializes a simple message
|
||||
#
|
||||
# Note: protobuf v21.x is used because newer versions (v22+) require abseil-cpp
|
||||
# as a dependency, which significantly complicates the build.
|
||||
|
||||
[cmake]
|
||||
version = "3.18...3.31"
|
||||
|
||||
[project]
|
||||
name = "protobuf-example"
|
||||
description = "Demonstrates protobuf integration with cmkr custom commands"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Fetch protobuf from GitHub
|
||||
# Using v21.12 (last version before abseil-cpp became required)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[fetch-content.protobuf]
|
||||
git = "https://github.com/protocolbuffers/protobuf"
|
||||
tag = "v21.12"
|
||||
options = {
|
||||
protobuf_BUILD_TESTS = false,
|
||||
protobuf_BUILD_EXAMPLES = false,
|
||||
protobuf_BUILD_LIBPROTOC = false,
|
||||
protobuf_BUILD_SHARED_LIBS = false,
|
||||
protobuf_MSVC_STATIC_RUNTIME = false,
|
||||
SKIP_INSTALL_ALL = true,
|
||||
protobuf_WITH_ZLIB = false,
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main executable using protobuf
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.protobuf_example]
|
||||
type = "executable"
|
||||
sources = [
|
||||
"src/main.cpp",
|
||||
]
|
||||
include-directories = [
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated",
|
||||
]
|
||||
link-libraries = ["protobuf::libprotobuf"]
|
||||
compile-features = ["cxx_std_17"]
|
||||
|
||||
# Create the generated directory before running protoc
|
||||
cmake-before = """
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
"""
|
||||
|
||||
# Custom command to generate C++ sources from .proto file
|
||||
# This uses protoc from the fetched protobuf repository
|
||||
[[target.protobuf_example.custom-command]]
|
||||
outputs = [
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.h",
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.cc",
|
||||
]
|
||||
depends = [
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
|
||||
"protobuf::protoc", # Ensure protoc is built first
|
||||
]
|
||||
command = [
|
||||
"$<TARGET_FILE:protobuf::protoc>",
|
||||
"--cpp_out=${CMAKE_CURRENT_BINARY_DIR}/generated",
|
||||
"-I${CMAKE_CURRENT_SOURCE_DIR}/proto",
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
|
||||
]
|
||||
comment = "Generate C++ sources from addressbook.proto"
|
||||
verbatim = true
|
||||
|
||||
# Post-build: run the executable to verify it works
|
||||
[[target.protobuf_example.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["$<TARGET_FILE:protobuf_example>"]
|
||||
comment = "Run protobuf_example to verify serialization/deserialization works"
|
||||
@@ -0,0 +1,31 @@
|
||||
// A simple address book example demonstrating protobuf serialization.
|
||||
// Based on the official protobuf tutorial example.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package tutorial;
|
||||
|
||||
// A person with basic contact information
|
||||
message Person {
|
||||
string name = 1;
|
||||
int32 id = 2;
|
||||
string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
message PhoneNumber {
|
||||
string number = 1;
|
||||
PhoneType type = 2;
|
||||
}
|
||||
|
||||
repeated PhoneNumber phones = 4;
|
||||
}
|
||||
|
||||
// An address book containing multiple people
|
||||
message AddressBook {
|
||||
repeated Person people = 1;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// Protobuf serialization/deserialization example
|
||||
// This demonstrates using protobuf with cmkr for code generation
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "addressbook.pb.h"
|
||||
|
||||
// Creates a sample address book with some people
|
||||
void create_address_book(tutorial::AddressBook &book) {
|
||||
// Add first person
|
||||
tutorial::Person *person1 = book.add_people();
|
||||
person1->set_name("Alice Smith");
|
||||
person1->set_id(1);
|
||||
person1->set_email("alice@example.com");
|
||||
|
||||
// Add phone numbers for Alice
|
||||
tutorial::Person::PhoneNumber *phone1 = person1->add_phones();
|
||||
phone1->set_number("555-1234");
|
||||
phone1->set_type(tutorial::Person::HOME);
|
||||
|
||||
tutorial::Person::PhoneNumber *phone2 = person1->add_phones();
|
||||
phone2->set_number("555-5678");
|
||||
phone2->set_type(tutorial::Person::WORK);
|
||||
|
||||
// Add second person
|
||||
tutorial::Person *person2 = book.add_people();
|
||||
person2->set_name("Bob Johnson");
|
||||
person2->set_id(2);
|
||||
person2->set_email("bob@example.com");
|
||||
|
||||
tutorial::Person::PhoneNumber *phone3 = person2->add_phones();
|
||||
phone3->set_number("555-9999");
|
||||
phone3->set_type(tutorial::Person::MOBILE);
|
||||
}
|
||||
|
||||
// Prints the contents of an address book
|
||||
void print_address_book(const tutorial::AddressBook &book) {
|
||||
std::cout << "Address Book (" << book.people_size() << " people):\n";
|
||||
std::cout << "==========================================\n\n";
|
||||
|
||||
for (int i = 0; i < book.people_size(); ++i) {
|
||||
const tutorial::Person &person = book.people(i);
|
||||
|
||||
std::cout << "Person ID: " << person.id() << "\n";
|
||||
std::cout << " Name: " << person.name() << "\n";
|
||||
std::cout << " Email: " << person.email() << "\n";
|
||||
|
||||
for (int j = 0; j < person.phones_size(); ++j) {
|
||||
const tutorial::Person::PhoneNumber &phone = person.phones(j);
|
||||
std::cout << " Phone #" << (j + 1) << ": " << phone.number() << " (";
|
||||
|
||||
switch (phone.type()) {
|
||||
case tutorial::Person::MOBILE:
|
||||
std::cout << "mobile";
|
||||
break;
|
||||
case tutorial::Person::HOME:
|
||||
std::cout << "home";
|
||||
break;
|
||||
case tutorial::Person::WORK:
|
||||
std::cout << "work";
|
||||
break;
|
||||
default:
|
||||
std::cout << "unknown";
|
||||
}
|
||||
std::cout << ")\n";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Verify that the version of the library that we linked against is
|
||||
// compatible with the version of the headers we compiled against.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
std::cout << "Protobuf cmkr Example\n";
|
||||
std::cout << "=====================\n\n";
|
||||
|
||||
// Create an address book
|
||||
tutorial::AddressBook original_book;
|
||||
create_address_book(original_book);
|
||||
|
||||
std::cout << "Original Address Book:\n";
|
||||
print_address_book(original_book);
|
||||
|
||||
// Serialize to string
|
||||
std::string serialized;
|
||||
if (!original_book.SerializeToString(&serialized)) {
|
||||
std::cerr << "Error: Failed to serialize address book\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << "Serialized size: " << serialized.size() << " bytes\n\n";
|
||||
|
||||
// Deserialize into a new object
|
||||
tutorial::AddressBook deserialized_book;
|
||||
if (!deserialized_book.ParseFromString(serialized)) {
|
||||
std::cerr << "Error: Failed to deserialize address book\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Deserialized Address Book:\n";
|
||||
print_address_book(deserialized_book);
|
||||
|
||||
// Write to file
|
||||
const char *filename = "addressbook.bin";
|
||||
{
|
||||
std::ofstream output(filename, std::ios::binary);
|
||||
if (!original_book.SerializeToOstream(&output)) {
|
||||
std::cerr << "Error: Failed to write to file\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::cout << "Written to file: " << filename << "\n";
|
||||
|
||||
// Read back from file
|
||||
tutorial::AddressBook file_book;
|
||||
{
|
||||
std::ifstream input(filename, std::ios::binary);
|
||||
if (!file_book.ParseFromIstream(&input)) {
|
||||
std::cerr << "Error: Failed to read from file\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::cout << "Read back from file successfully!\n\n";
|
||||
|
||||
// Verify all three versions are identical
|
||||
bool all_match = true;
|
||||
if (original_book.SerializeAsString() != deserialized_book.SerializeAsString()) {
|
||||
std::cerr << "Error: Original and deserialized don't match!\n";
|
||||
all_match = false;
|
||||
}
|
||||
if (original_book.SerializeAsString() != file_book.SerializeAsString()) {
|
||||
std::cerr << "Error: Original and file don't match!\n";
|
||||
all_match = false;
|
||||
}
|
||||
|
||||
if (all_match) {
|
||||
std::cout << "SUCCESS: All serialization methods produced identical results!\n";
|
||||
}
|
||||
|
||||
// Clean up any global objects allocated by protobuf
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
return all_match ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[project]
|
||||
name = "relative-paths"
|
||||
|
||||
[target.test-library]
|
||||
type = "static"
|
||||
sources = ["src/library-code.cpp"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.link-libraries = ["libs/test-library-x64-Release.lib"]
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
// Created by Anthony Printup on 9/18/2023.
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" void library_function() {
|
||||
std::puts("Hello from library_function!");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Created by Anthony Printup on 9/18/2023.
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef WIN32
|
||||
extern "C" void library_function();
|
||||
#endif
|
||||
int main() {
|
||||
puts("Hello from cmkr(relative-paths)!");
|
||||
#ifdef WIN32
|
||||
library_function();
|
||||
#endif
|
||||
}
|
||||
@@ -8,11 +8,11 @@ description = "Target templates"
|
||||
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
|
||||
properties = {
|
||||
CXX_STANDARD = "11",
|
||||
CXX_STANDARD_REQUIRED = true,
|
||||
}
|
||||
|
||||
[target.app-a]
|
||||
type = "app"
|
||||
@@ -22,4 +22,4 @@ compile-definitions = ["APP_A"]
|
||||
type = "app"
|
||||
compile-definitions = ["APP_B"]
|
||||
|
||||
# **Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
|
||||
# **Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
|
||||
# Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://github.com/microsoft/vcpkg) and links an `example` target to it:
|
||||
|
||||
[project]
|
||||
name = "vcpkg"
|
||||
description = "Dependencies from vcpkg"
|
||||
|
||||
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
|
||||
# See https://vcpkg.io/en/packages.html for available packages
|
||||
# See https://vcpkg.io/en/packages or https://vcpkg.link for available packages
|
||||
[vcpkg]
|
||||
version = "2022.11.14"
|
||||
version = "2024.11.16"
|
||||
packages = ["fmt"]
|
||||
|
||||
[find-package]
|
||||
fmt = {}
|
||||
[find-package.fmt]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
@@ -19,4 +18,4 @@ 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`.
|
||||
# To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
|
||||
# To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int main() {
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"$cmkr": "This file is automatically generated from cmake.toml - DO NOT EDIT",
|
||||
"$cmkr-url": "https://github.com/build-cpp/cmkr",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
"fmt"
|
||||
],
|
||||
"description": "Dependencies from vcpkg",
|
||||
"name": "vcpkg",
|
||||
"version-string": ""
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
# Disable clang-format in this folder
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
||||
+4
-7
@@ -9,14 +9,11 @@ add_library(ordered_map INTERFACE)
|
||||
target_include_directories(ordered_map INTERFACE ordered-map-1.0.0/include)
|
||||
target_compile_definitions(ordered_map INTERFACE NOMINMAX)
|
||||
|
||||
# https://github.com/ToruNiina/toml11 (MIT)
|
||||
add_library(toml11 INTERFACE)
|
||||
target_include_directories(toml11 INTERFACE toml11-3.6.0)
|
||||
# https://github.com/ToruNiina/toml11 (MIT)
|
||||
add_library(toml11 INTERFACE)
|
||||
target_include_directories(toml11 INTERFACE toml11-3.8.1)
|
||||
target_compile_definitions(toml11 INTERFACE TOML11_USE_UNRELEASED_TOML_FEATURES)
|
||||
|
||||
# https://github.com/mpark/variant (BSL-1.0)
|
||||
add_library(mpark_variant INTERFACE)
|
||||
target_include_directories(mpark_variant INTERFACE variant-1.4.0/include)
|
||||
|
||||
# https://github.com/nlohmann/json (MIT)
|
||||
add_library(nlohmann_json INTERFACE)
|
||||
target_include_directories(nlohmann_json INTERFACE nlohmann-3.9.1/include)
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2021 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-25447
File diff suppressed because it is too large
Load Diff
@@ -25,21 +25,14 @@
|
||||
#ifndef TOML_FOR_MODERN_CPP
|
||||
#define TOML_FOR_MODERN_CPP
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error "__cplusplus is not defined"
|
||||
#endif
|
||||
|
||||
#if __cplusplus < 201103L && _MSC_VER < 1900
|
||||
# error "toml11 requires C++11 or later."
|
||||
#endif
|
||||
|
||||
#define TOML11_VERSION_MAJOR 3
|
||||
#define TOML11_VERSION_MINOR 5
|
||||
#define TOML11_VERSION_PATCH 0
|
||||
#define TOML11_VERSION_MINOR 8
|
||||
#define TOML11_VERSION_PATCH 1
|
||||
|
||||
#include "toml/parser.hpp"
|
||||
#include "toml/literal.hpp"
|
||||
#include "toml/serializer.hpp"
|
||||
#include "toml/get.hpp"
|
||||
#include "toml/macros.hpp"
|
||||
|
||||
#endif// TOML_FOR_MODERN_CPP
|
||||
+45
@@ -17,11 +17,41 @@ namespace color_ansi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
inline int colorize_index()
|
||||
{
|
||||
static const int index = std::ios_base::xalloc();
|
||||
return index;
|
||||
}
|
||||
|
||||
// Control color mode globally
|
||||
class color_mode
|
||||
{
|
||||
public:
|
||||
inline void enable()
|
||||
{
|
||||
should_color_ = true;
|
||||
}
|
||||
inline void disable()
|
||||
{
|
||||
should_color_ = false;
|
||||
}
|
||||
|
||||
inline bool should_color() const
|
||||
{
|
||||
return should_color_;
|
||||
}
|
||||
|
||||
static color_mode& status()
|
||||
{
|
||||
static color_mode status_;
|
||||
return status_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool should_color_ = false;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
inline std::ostream& colorize(std::ostream& os)
|
||||
@@ -55,6 +85,21 @@ inline std::ostream& cyan (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
|
||||
inline std::ostream& white (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
|
||||
|
||||
inline void enable()
|
||||
{
|
||||
return detail::color_mode::status().enable();
|
||||
}
|
||||
inline void disable()
|
||||
{
|
||||
return detail::color_mode::status().disable();
|
||||
}
|
||||
|
||||
inline bool should_color()
|
||||
{
|
||||
return detail::color_mode::status().should_color();
|
||||
}
|
||||
|
||||
} // color_ansi
|
||||
|
||||
// ANSI escape sequence is the only and default colorization method currently
|
||||
Vendored
+2
-2
@@ -29,7 +29,7 @@ namespace detail
|
||||
// to output character as an error message.
|
||||
inline std::string show_char(const char c)
|
||||
{
|
||||
// It supress an error that occurs only in Debug mode of MSVC++ on Windows.
|
||||
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
|
||||
// I'm not completely sure but they check the value of char to be in the
|
||||
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
|
||||
// has negative value (if char has sign). So here it re-interprets c as
|
||||
@@ -154,7 +154,7 @@ struct sequence<Head, Tail...>
|
||||
invoke(location& loc)
|
||||
{
|
||||
const auto first = loc.iter();
|
||||
const auto rslt = Head::invoke(loc);
|
||||
auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
loc.reset(first);
|
||||
+26
-7
@@ -4,11 +4,18 @@
|
||||
#define TOML11_COMMENTS_HPP
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
|
||||
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
|
||||
#else
|
||||
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
|
||||
#endif
|
||||
|
||||
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
|
||||
// Those two are a container that have the same interface as `std::vector<std::string>`
|
||||
// but bahaves in the opposite way. `preserve_comments` is just the same as
|
||||
@@ -339,7 +346,7 @@ operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::differ
|
||||
//
|
||||
// Why this is chose as the default type is because the last version (2.x.y)
|
||||
// does not contain any comments in a value. To minimize the impact on the
|
||||
// efficiency, this is choosed as a default.
|
||||
// efficiency, this is chosen as a default.
|
||||
//
|
||||
// To reduce the memory footprint, later we can try empty base optimization (EBO).
|
||||
struct discard_comments
|
||||
@@ -418,14 +425,14 @@ struct discard_comments
|
||||
// empty, so accessing through operator[], front/back, data causes address
|
||||
// error.
|
||||
|
||||
reference operator[](const size_type) noexcept {return *data();}
|
||||
const_reference operator[](const size_type) const noexcept {return *data();}
|
||||
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
|
||||
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
|
||||
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
|
||||
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
|
||||
reference front() noexcept {return *data();}
|
||||
const_reference front() const noexcept {return *data();}
|
||||
reference back() noexcept {return *data();}
|
||||
const_reference back() const noexcept {return *data();}
|
||||
reference front() noexcept {never_call("toml::discard_comment::front");}
|
||||
const_reference front() const noexcept {never_call("toml::discard_comment::front");}
|
||||
reference back() noexcept {never_call("toml::discard_comment::back");}
|
||||
const_reference back() const noexcept {never_call("toml::discard_comment::back");}
|
||||
|
||||
pointer data() noexcept {return nullptr;}
|
||||
const_pointer data() const noexcept {return nullptr;}
|
||||
@@ -443,6 +450,18 @@ struct discard_comments
|
||||
const_reverse_iterator rend() const noexcept {return const_iterator{};}
|
||||
const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
|
||||
const_reverse_iterator crend() const noexcept {return const_iterator{};}
|
||||
|
||||
private:
|
||||
|
||||
[[noreturn]] static void never_call(const char *const this_function)
|
||||
{
|
||||
#ifdef __has_builtin
|
||||
# if __has_builtin(__builtin_unreachable)
|
||||
__builtin_unreachable();
|
||||
# endif
|
||||
#endif
|
||||
throw std::logic_error{this_function};
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
|
||||
+32
-32
@@ -21,22 +21,7 @@ namespace toml
|
||||
namespace detail
|
||||
{
|
||||
// TODO: find more sophisticated way to handle this
|
||||
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::localtime_r(src, &dst);
|
||||
if (!result) { throw std::runtime_error("localtime_r failed."); }
|
||||
return dst;
|
||||
}
|
||||
inline std::tm gmtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::gmtime_r(src, &dst);
|
||||
if (!result) { throw std::runtime_error("gmtime_r failed."); }
|
||||
return dst;
|
||||
}
|
||||
#elif defined(_MSC_VER)
|
||||
#if defined(_MSC_VER)
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
@@ -51,6 +36,21 @@ inline std::tm gmtime_s(const std::time_t* src)
|
||||
if (result) { throw std::runtime_error("gmtime_s failed."); }
|
||||
return dst;
|
||||
}
|
||||
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::localtime_r(src, &dst);
|
||||
if (!result) { throw std::runtime_error("localtime_r failed."); }
|
||||
return dst;
|
||||
}
|
||||
inline std::tm gmtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::gmtime_r(src, &dst);
|
||||
if (!result) { throw std::runtime_error("gmtime_r failed."); }
|
||||
return dst;
|
||||
}
|
||||
#else // fallback. not threadsafe
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
@@ -85,9 +85,9 @@ enum class month_t : std::uint8_t
|
||||
|
||||
struct local_date
|
||||
{
|
||||
std::int16_t year; // A.D. (like, 2018)
|
||||
std::uint8_t month; // [0, 11]
|
||||
std::uint8_t day; // [1, 31]
|
||||
std::int16_t year{}; // A.D. (like, 2018)
|
||||
std::uint8_t month{}; // [0, 11]
|
||||
std::uint8_t day{}; // [1, 31]
|
||||
|
||||
local_date(int y, month_t m, int d)
|
||||
: year (static_cast<std::int16_t>(y)),
|
||||
@@ -181,12 +181,12 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
|
||||
|
||||
struct local_time
|
||||
{
|
||||
std::uint8_t hour; // [0, 23]
|
||||
std::uint8_t minute; // [0, 59]
|
||||
std::uint8_t second; // [0, 60]
|
||||
std::uint16_t millisecond; // [0, 999]
|
||||
std::uint16_t microsecond; // [0, 999]
|
||||
std::uint16_t nanosecond; // [0, 999]
|
||||
std::uint8_t hour{}; // [0, 23]
|
||||
std::uint8_t minute{}; // [0, 59]
|
||||
std::uint8_t second{}; // [0, 60]
|
||||
std::uint16_t millisecond{}; // [0, 999]
|
||||
std::uint16_t microsecond{}; // [0, 999]
|
||||
std::uint16_t nanosecond{}; // [0, 999]
|
||||
|
||||
local_time(int h, int m, int s,
|
||||
int ms = 0, int us = 0, int ns = 0)
|
||||
@@ -297,8 +297,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
|
||||
|
||||
struct time_offset
|
||||
{
|
||||
std::int8_t hour; // [-12, 12]
|
||||
std::int8_t minute; // [-59, 59]
|
||||
std::int8_t hour{}; // [-12, 12]
|
||||
std::int8_t minute{}; // [-59, 59]
|
||||
|
||||
time_offset(int h, int m)
|
||||
: hour (static_cast<std::int8_t>(h)),
|
||||
@@ -364,8 +364,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
|
||||
|
||||
struct local_datetime
|
||||
{
|
||||
local_date date;
|
||||
local_time time;
|
||||
local_date date{};
|
||||
local_time time{};
|
||||
|
||||
local_datetime(local_date d, local_time t): date(d), time(t) {}
|
||||
|
||||
@@ -478,9 +478,9 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
|
||||
|
||||
struct offset_datetime
|
||||
{
|
||||
local_date date;
|
||||
local_time time;
|
||||
time_offset offset;
|
||||
local_date date{};
|
||||
local_time time{};
|
||||
time_offset offset{};
|
||||
|
||||
offset_datetime(local_date d, local_time t, time_offset o)
|
||||
: date(d), time(t), offset(o)
|
||||
+19
-1
@@ -2,14 +2,32 @@
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_EXCEPTION_HPP
|
||||
#define TOML11_EXCEPTION_HPP
|
||||
#include <stdexcept>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "source_location.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
struct file_io_error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
file_io_error(int errnum, const std::string& msg, const std::string& fname)
|
||||
: std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)),
|
||||
errno_(errnum)
|
||||
{}
|
||||
|
||||
int get_errno() const noexcept {return errno_;}
|
||||
|
||||
private:
|
||||
int errno_;
|
||||
};
|
||||
|
||||
struct exception : public std::exception
|
||||
{
|
||||
public:
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_FROM_HPP
|
||||
#define TOML11_FROM_HPP
|
||||
#include "traits.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
+101
-13
@@ -140,7 +140,7 @@ get(basic_value<C, M, V>&& v)
|
||||
// ============================================================================
|
||||
// std::string_view
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
|
||||
@@ -215,6 +215,7 @@ template<typename T, typename C,
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::is_container<T>, // T is a container
|
||||
detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...)
|
||||
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
|
||||
detail::negation< // not toml::array
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
|
||||
>::value, T>
|
||||
@@ -255,19 +256,37 @@ get(const basic_value<C, M, V>&);
|
||||
|
||||
// toml::from<T>::from_toml(v)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
std::size_t S = sizeof(::toml::from<T>)>
|
||||
T get(const basic_value<C, M, V>&);
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
|
||||
get(const basic_value<C, M, V>&);
|
||||
|
||||
// T(const toml::value&) and T is not toml::basic_value
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
|
||||
get(basic_value<C, M, V>&);
|
||||
|
||||
// T(const toml::value&) and T is not toml::basic_value,
|
||||
// and it does not have `from<T>` nor `from_toml`.
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::negation<detail::is_basic_value<T>>,
|
||||
std::is_constructible<T, const basic_value<C, M, V>&>
|
||||
std::is_constructible<T, const basic_value<C, M, V>&>,
|
||||
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
|
||||
detail::negation<detail::has_specialized_from<T>>
|
||||
>::value, T>
|
||||
get(const basic_value<C, M, V>&);
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::negation<detail::is_basic_value<T>>,
|
||||
std::is_constructible<T, basic_value<C, M, V>&>,
|
||||
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
|
||||
detail::negation<detail::has_specialized_from<T>>
|
||||
>::value, T>
|
||||
get(basic_value<C, M, V>&);
|
||||
|
||||
// ============================================================================
|
||||
// array-like types; most likely STL container, like std::vector, etc.
|
||||
|
||||
@@ -321,6 +340,7 @@ template<typename T, typename C,
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::is_container<T>, // T is a container
|
||||
detail::negation<detail::has_push_back_method<T>>, // w/o push_back
|
||||
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
|
||||
detail::negation< // T is not toml::array
|
||||
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
|
||||
>::value, T>
|
||||
@@ -338,8 +358,10 @@ get(const basic_value<C, M, V>& v)
|
||||
{v.location(), "here"}
|
||||
}));
|
||||
}
|
||||
std::transform(ar.cbegin(), ar.cend(), container.begin(),
|
||||
[](const value& x){return ::toml::get<value_type>(x);});
|
||||
for(std::size_t i=0; i<ar.size(); ++i)
|
||||
{
|
||||
container[i] = ::toml::get<value_type>(ar[i]);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -438,9 +460,16 @@ get(const basic_value<C, M, V>& v)
|
||||
return ud;
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V,
|
||||
std::size_t>
|
||||
T get(const basic_value<C, M, V>& v)
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
|
||||
get(const basic_value<C, M, V>& v)
|
||||
{
|
||||
return ::toml::from<T>::from_toml(v);
|
||||
}
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
|
||||
get(basic_value<C, M, V>& v)
|
||||
{
|
||||
return ::toml::from<T>::from_toml(v);
|
||||
}
|
||||
@@ -448,14 +477,29 @@ T get(const basic_value<C, M, V>& v)
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::negation<detail::is_basic_value<T>>,
|
||||
std::is_constructible<T, const basic_value<C, M, V>&>
|
||||
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
|
||||
std::is_constructible<T, const basic_value<C, M, V>&>, // T is constructible from toml::value
|
||||
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
|
||||
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
|
||||
>::value, T>
|
||||
get(const basic_value<C, M, V>& v)
|
||||
{
|
||||
return T(v);
|
||||
}
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
detail::enable_if_t<detail::conjunction<
|
||||
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
|
||||
std::is_constructible<T, basic_value<C, M, V>&>, // T is constructible from toml::value
|
||||
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
|
||||
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
|
||||
>::value, T>
|
||||
get(basic_value<C, M, V>& v)
|
||||
{
|
||||
return T(v);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// find
|
||||
|
||||
@@ -1031,6 +1075,50 @@ find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
|
||||
return get_or(tab.at(ky), std::forward<T>(opt));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// recursive find-or with type deduction (find_or(value, keys, opt))
|
||||
|
||||
template<typename Value, typename ... Ks,
|
||||
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
|
||||
// here we need to add SFINAE in the template parameter to avoid
|
||||
// infinite recursion in type deduction on gcc
|
||||
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
|
||||
-> decltype(find_or(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
|
||||
{
|
||||
if(!v.is_table())
|
||||
{
|
||||
return detail::last_one(std::forward<Ks>(keys)...);
|
||||
}
|
||||
auto&& tab = std::forward<Value>(v).as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
return detail::last_one(std::forward<Ks>(keys)...);
|
||||
}
|
||||
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// recursive find_or with explicit type specialization, find_or<int>(value, keys...)
|
||||
|
||||
template<typename T, typename Value, typename ... Ks,
|
||||
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
|
||||
// here we need to add SFINAE in the template parameter to avoid
|
||||
// infinite recursion in type deduction on gcc
|
||||
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
|
||||
-> decltype(find_or<T>(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
|
||||
{
|
||||
if(!v.is_table())
|
||||
{
|
||||
return detail::last_one(std::forward<Ks>(keys)...);
|
||||
}
|
||||
auto&& tab = std::forward<Value>(v).as_table();
|
||||
if(tab.count(ky) == 0)
|
||||
{
|
||||
return detail::last_one(std::forward<Ks>(keys)...);
|
||||
}
|
||||
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// expect
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_INTO_HPP
|
||||
#define TOML11_INTO_HPP
|
||||
#include "traits.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
+39
-15
@@ -5,7 +5,6 @@
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
|
||||
#include "combinator.hpp"
|
||||
|
||||
@@ -119,8 +118,8 @@ using lex_local_time = lex_partial_time;
|
||||
// ===========================================================================
|
||||
|
||||
using lex_quotation_mark = character<'"'>;
|
||||
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
|
||||
in_range<0x0a, 0x1F>, // is allowed
|
||||
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
|
||||
in_range<0x0A, 0x1F>,
|
||||
character<0x22>, character<0x5C>,
|
||||
character<0x7F>>>;
|
||||
|
||||
@@ -133,6 +132,9 @@ using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
|
||||
character<'b'>, character<'f'>,
|
||||
character<'n'>, character<'r'>,
|
||||
character<'t'>,
|
||||
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
|
||||
character<'e'>, // ESC (0x1B)
|
||||
#endif
|
||||
lex_escape_unicode_short,
|
||||
lex_escape_unicode_long
|
||||
>;
|
||||
@@ -166,7 +168,7 @@ using lex_basic_string = sequence<lex_quotation_mark,
|
||||
// | ^- expected newline, but got '"'.
|
||||
// ```
|
||||
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
|
||||
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
|
||||
// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
|
||||
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
|
||||
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
|
||||
// the string body.
|
||||
@@ -178,8 +180,8 @@ using lex_ml_basic_string_close = sequence<
|
||||
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
|
||||
>;
|
||||
|
||||
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
|
||||
in_range<0x0a, 0x1F>, // is tab
|
||||
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
|
||||
in_range<0x0A, 0x1F>,
|
||||
character<0x5C>, // backslash
|
||||
character<0x7F>, // DEL
|
||||
lex_ml_basic_string_delim>>;
|
||||
@@ -196,8 +198,8 @@ using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
|
||||
lex_ml_basic_body,
|
||||
lex_ml_basic_string_close>;
|
||||
|
||||
using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
|
||||
in_range<0x10, 0x19>, character<0x27>>>;
|
||||
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
|
||||
character<0x7F>, character<0x27>>>;
|
||||
using lex_apostrophe = character<'\''>;
|
||||
using lex_literal_string = sequence<lex_apostrophe,
|
||||
repeat<lex_literal_char, unlimited>,
|
||||
@@ -212,7 +214,7 @@ using lex_ml_literal_string_close = sequence<
|
||||
>;
|
||||
|
||||
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
|
||||
in_range<0x10, 0x1F>,
|
||||
in_range<0x0A, 0x1F>,
|
||||
character<0x7F>,
|
||||
lex_ml_literal_string_delim>>;
|
||||
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
|
||||
@@ -225,12 +227,6 @@ using lex_string = either<lex_ml_basic_string, lex_basic_string,
|
||||
lex_ml_literal_string, lex_literal_string>;
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
using lex_comment_start_symbol = character<'#'>;
|
||||
using lex_non_eol = either<character<'\t'>, exclude<in_range<0x00, 0x19>>>;
|
||||
using lex_comment = sequence<lex_comment_start_symbol,
|
||||
repeat<lex_non_eol, unlimited>>;
|
||||
|
||||
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
|
||||
|
||||
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
|
||||
@@ -265,6 +261,34 @@ using lex_array_table = sequence<lex_array_table_open,
|
||||
maybe<lex_ws>,
|
||||
lex_array_table_close>;
|
||||
|
||||
using lex_utf8_1byte = in_range<0x00, 0x7F>;
|
||||
using lex_utf8_2byte = sequence<
|
||||
in_range<'\xC2', '\xDF'>,
|
||||
in_range<'\x80', '\xBF'>
|
||||
>;
|
||||
using lex_utf8_3byte = sequence<either<
|
||||
sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>,
|
||||
sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>,
|
||||
sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>,
|
||||
sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>>
|
||||
>, in_range<'\x80', '\xBF'>>;
|
||||
using lex_utf8_4byte = sequence<either<
|
||||
sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>,
|
||||
sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>,
|
||||
sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>>
|
||||
>, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>;
|
||||
using lex_utf8_code = either<
|
||||
lex_utf8_1byte,
|
||||
lex_utf8_2byte,
|
||||
lex_utf8_3byte,
|
||||
lex_utf8_4byte
|
||||
>;
|
||||
|
||||
using lex_comment_start_symbol = character<'#'>;
|
||||
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>;
|
||||
using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
|
||||
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif // TOML_LEXER_HPP
|
||||
+11
-5
@@ -12,8 +12,11 @@ inline namespace toml_literals
|
||||
{
|
||||
|
||||
// implementation
|
||||
inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
||||
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
|
||||
literal_internal_impl(::toml::detail::location loc)
|
||||
{
|
||||
using value_type = ::toml::basic_value<
|
||||
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
|
||||
// if there are some comments or empty lines, skip them.
|
||||
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
|
||||
::toml::detail::maybe<::toml::detail::lex_ws>,
|
||||
@@ -50,7 +53,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
||||
// If it is neither a table-key or a array-of-table-key, it may be a value.
|
||||
if(!is_table_key && !is_aots_key)
|
||||
{
|
||||
if(auto data = ::toml::detail::parse_value<::toml::value>(loc))
|
||||
if(auto data = ::toml::detail::parse_value<value_type>(loc, 0))
|
||||
{
|
||||
return data.unwrap();
|
||||
}
|
||||
@@ -67,7 +70,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
||||
// It is a valid toml file.
|
||||
// It should be parsed as if we parse a file with this content.
|
||||
|
||||
if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc))
|
||||
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
|
||||
{
|
||||
return data.unwrap();
|
||||
}
|
||||
@@ -78,11 +81,13 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
||||
|
||||
}
|
||||
|
||||
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
|
||||
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
|
||||
operator"" _toml(const char* str, std::size_t len)
|
||||
{
|
||||
::toml::detail::location loc(
|
||||
std::string("TOML literal encoded in a C++ code"),
|
||||
std::vector<char>(str, str + len));
|
||||
// literal length does not include the null character at the end.
|
||||
return literal_internal_impl(std::move(loc));
|
||||
}
|
||||
|
||||
@@ -91,7 +96,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
// value of u8"" literal has been changed from char to char8_t and char8_t is
|
||||
// NOT compatible to char
|
||||
inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len)
|
||||
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
|
||||
operator"" _toml(const char8_t* str, std::size_t len)
|
||||
{
|
||||
::toml::detail::location loc(
|
||||
std::string("TOML literal encoded in a C++ code"),
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
#ifndef TOML11_MACROS_HPP
|
||||
#define TOML11_MACROS_HPP
|
||||
|
||||
#define TOML11_STRINGIZE_AUX(x) #x
|
||||
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
|
||||
|
||||
#define TOML11_CONCATENATE_AUX(x, y) x##y
|
||||
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
|
||||
|
||||
// ============================================================================
|
||||
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
|
||||
|
||||
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TOML11_ARGS_SIZE
|
||||
|
||||
#define TOML11_INDEX_RSEQ() \
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
|
||||
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||
#define TOML11_ARGS_SIZE_IMPL(\
|
||||
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \
|
||||
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \
|
||||
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \
|
||||
ARG31, ARG32, N, ...) N
|
||||
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
|
||||
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TOML11_FOR_EACH_VA_ARGS
|
||||
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
|
||||
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
|
||||
|
||||
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
|
||||
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
|
||||
|
||||
// use it in the following way.
|
||||
// ```cpp
|
||||
// namespace foo
|
||||
// {
|
||||
// struct Foo
|
||||
// {
|
||||
// std::string s;
|
||||
// double d;
|
||||
// int i;
|
||||
// };
|
||||
// } // foo
|
||||
//
|
||||
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
|
||||
// ```
|
||||
// And then you can use `toml::find<foo::Foo>(file, "foo");`
|
||||
//
|
||||
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
|
||||
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME));
|
||||
|
||||
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
|
||||
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME;
|
||||
|
||||
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
|
||||
namespace toml { \
|
||||
template<> \
|
||||
struct from<NAME> \
|
||||
{ \
|
||||
template<typename C, template<typename ...> class T, \
|
||||
template<typename ...> class A> \
|
||||
static NAME from_toml(const basic_value<C, T, A>& v) \
|
||||
{ \
|
||||
NAME obj; \
|
||||
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
|
||||
return obj; \
|
||||
} \
|
||||
}; \
|
||||
template<> \
|
||||
struct into<NAME> \
|
||||
{ \
|
||||
static value into_toml(const NAME& obj) \
|
||||
{ \
|
||||
::toml::value v = ::toml::table{}; \
|
||||
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
|
||||
return v; \
|
||||
} \
|
||||
}; \
|
||||
} /* toml */
|
||||
|
||||
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
|
||||
|
||||
#endif// TOML11_MACROS_HPP
|
||||
+624
-117
File diff suppressed because it is too large
Load Diff
+13
-14
@@ -70,16 +70,16 @@ struct region_base
|
||||
struct location final : public region_base
|
||||
{
|
||||
using const_iterator = typename std::vector<char>::const_iterator;
|
||||
using difference_type = typename const_iterator::difference_type;
|
||||
using difference_type = typename std::iterator_traits<const_iterator>::difference_type;
|
||||
using source_ptr = std::shared_ptr<const std::vector<char>>;
|
||||
|
||||
location(std::string name, std::vector<char> cont)
|
||||
location(std::string source_name, std::vector<char> cont)
|
||||
: source_(std::make_shared<std::vector<char>>(std::move(cont))),
|
||||
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
|
||||
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
|
||||
{}
|
||||
location(std::string name, const std::string& cont)
|
||||
location(std::string source_name, const std::string& cont)
|
||||
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
|
||||
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
|
||||
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
|
||||
{}
|
||||
|
||||
location(const location&) = default;
|
||||
@@ -92,7 +92,7 @@ struct location final : public region_base
|
||||
char front() const noexcept override {return *iter_;}
|
||||
|
||||
// this const prohibits codes like `++(loc.iter())`.
|
||||
const const_iterator iter() const noexcept {return iter_;}
|
||||
std::add_const<const_iterator>::type iter() const noexcept {return iter_;}
|
||||
|
||||
const_iterator begin() const noexcept {return source_->cbegin();}
|
||||
const_iterator end() const noexcept {return source_->cend();}
|
||||
@@ -227,8 +227,7 @@ struct region final : public region_base
|
||||
region& operator+=(const region& other)
|
||||
{
|
||||
// different regions cannot be concatenated
|
||||
assert(this->begin() == other.begin() && this->end() == other.end() &&
|
||||
this->last_ == other.first_);
|
||||
assert(this->source_ == other.source_ && this->last_ == other.first_);
|
||||
|
||||
this->last_ = other.last_;
|
||||
return *this;
|
||||
@@ -343,9 +342,9 @@ struct region final : public region_base
|
||||
}))
|
||||
{
|
||||
// unwrap the first '#' by std::next.
|
||||
auto str = make_string(std::next(comment_found), iter);
|
||||
if(str.back() == '\r') {str.pop_back();}
|
||||
com.push_back(std::move(str));
|
||||
auto s = make_string(std::next(comment_found), iter);
|
||||
if(!s.empty() && s.back() == '\r') {s.pop_back();}
|
||||
com.push_back(std::move(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -396,9 +395,9 @@ struct region final : public region_base
|
||||
}))
|
||||
{
|
||||
// unwrap the first '#' by std::next.
|
||||
auto str = make_string(std::next(comment_found), this->line_end());
|
||||
if(str.back() == '\r') {str.pop_back();}
|
||||
com.push_back(std::move(str));
|
||||
auto s = make_string(std::next(comment_found), this->line_end());
|
||||
if(!s.empty() && s.back() == '\r') {s.pop_back();}
|
||||
com.push_back(std::move(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user