Compare commits

..

1 Commits

Author SHA1 Message Date
Duncan Ogilvie c76956450a Initial draft of [package] support 2022-03-20 21:19:07 +01:00
124 changed files with 26963 additions and 5779 deletions
+2 -3
View File
@@ -10,10 +10,9 @@ AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false AllowShortFunctionsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
AllowShortLambdasOnASingleLine: None
AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
@@ -73,7 +72,7 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60 PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right PointerAlignment: Right
ReflowComments: true ReflowComments: true
SortIncludes: false SortIncludes: true
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true
-24
View File
@@ -1,24 +0,0 @@
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
+9 -16
View File
@@ -1,25 +1,21 @@
name: build name: CMake
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
cmake: build:
# Skip building pull requests from the same repository # 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) }} if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [windows-2022, macos-latest, ubuntu-22.04] os: [windows-2019, macos-10.15, ubuntu-20.04]
env: env:
BUILD_TYPE: 'Release' BUILD_TYPE: Release
CMAKE_GENERATOR: 'Ninja'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Visual Studio Development Environment
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.1
- name: Tag cmkr.cmake - name: Tag cmkr.cmake
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
@@ -42,31 +38,28 @@ jobs:
ctest -C ${{ env.BUILD_TYPE }} --verbose ctest -C ${{ env.BUILD_TYPE }} --verbose
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: ${{ github.event.repository.name }}-${{ matrix.os }} name: ${{ github.event.repository.name }}-${{ matrix.os }}
path: install/bin/* path: install/bin/*
- name: Get lowercase OS name - name: Get lowercase OS name
id: osname id: osname
uses: ASzc/change-string-case-action@ccb130a4e483d3e86287289183704dc9bf53e77e # master 2024-04-05 uses: ASzc/change-string-case-action@v1
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
string: ${{ runner.os }} string: ${{ runner.os }}
- name: Compress artifacts - name: Compress artifacts
uses: vimtor/action-zip@1379ea20d4c5705669ba81fd626dd01b1c738f26 # v1.2 uses: vimtor/action-zip@v1
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
files: install/bin/ files: install/bin/
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
- name: Release - name: Release
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 uses: softprops/action-gh-release@v1
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }} prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
files: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip files: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
generate_release_notes: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-41
View File
@@ -1,41 +0,0 @@
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
View File
@@ -9,5 +9,3 @@ build*/
.idea/ .idea/
cmake-build*/ cmake-build*/
CMakeLists.txt.user CMakeLists.txt.user
.vscode/
.DS_Store
+55 -25
View File
@@ -1,20 +1,23 @@
# This file is automatically generated from cmake.toml - DO NOT EDIT # This file is automatically generated from cmake.toml - DO NOT EDIT
# See https://github.com/build-cpp/cmkr for more information # See https://github.com/build-cpp/cmkr for more information
cmake_minimum_required(VERSION 2.8...3.31) cmake_minimum_required(VERSION 2.8...3.8)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 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") message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
endif() endif()
# Regenerate CMakeLists.txt automatically in the root project
set(CMKR_ROOT_PROJECT OFF) set(CMKR_ROOT_PROJECT OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(CMKR_ROOT_PROJECT ON) set(CMKR_ROOT_PROJECT ON)
# Enable folder support # Enable folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
# Create a configure-time dependency on cmake.toml to improve IDE support # Create a configure-time dependency on cmake.toml to improve IDE support
if(CMKR_ROOT_PROJECT)
configure_file(cmake.toml cmake.toml COPYONLY) configure_file(cmake.toml cmake.toml COPYONLY)
endif() endif()
@@ -22,7 +25,7 @@ project(cmkr
LANGUAGES LANGUAGES
CXX CXX
VERSION VERSION
0.2.46 0.2.12
DESCRIPTION DESCRIPTION
"CMakeLists generator from TOML" "CMakeLists generator from TOML"
) )
@@ -30,7 +33,7 @@ project(cmkr
include("cmake/generate_documentation.cmake") include("cmake/generate_documentation.cmake")
include("cmake/generate_resources.cmake") include("cmake/generate_resources.cmake")
# Subdirectory: third_party # third_party
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER}) set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER) if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party") set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party")
@@ -40,7 +43,7 @@ endif()
add_subdirectory(third_party) add_subdirectory(third_party)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER}) set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Subdirectory: tests # tests
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER}) set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER) if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests") set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests")
@@ -50,35 +53,64 @@ endif()
add_subdirectory(tests) add_subdirectory(tests)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER}) set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Target: cmkr_generate_documentation
# Target cmkr_generate_documentation
set(CMKR_TARGET cmkr_generate_documentation)
set(cmkr_generate_documentation_SOURCES "")
set(CMKR_SOURCES ${cmkr_generate_documentation_SOURCES})
add_library(cmkr_generate_documentation INTERFACE) add_library(cmkr_generate_documentation INTERFACE)
set(CMKR_TARGET cmkr_generate_documentation) if(cmkr_generate_documentation_SOURCES)
target_sources(cmkr_generate_documentation INTERFACE ${cmkr_generate_documentation_SOURCES})
endif()
generate_documentation() generate_documentation()
# Target: cmkr unset(CMKR_TARGET)
set(cmkr_SOURCES unset(CMKR_SOURCES)
cmake.toml
"cmake/cmkr.cmake" # Target cmkr
"cmake/version.hpp.in" set(CMKR_TARGET cmkr)
set(cmkr_SOURCES "")
list(APPEND 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"
"include/arguments.hpp" "include/arguments.hpp"
"include/build.hpp" "include/build.hpp"
"include/cmake_generator.hpp" "include/cmake_generator.hpp"
"include/error.hpp"
"include/fs.hpp" "include/fs.hpp"
"include/help.hpp" "include/help.hpp"
"include/literals.hpp" "include/literals.hpp"
"include/project_parser.hpp" "include/project_parser.hpp"
"src/arguments.cpp" "cmake/cmkr.cmake"
"src/build.cpp" "cmake/version.hpp.in"
"src/cmake_generator.cpp"
"src/help.cpp"
"src/main.cpp"
"src/project_parser.cpp"
) )
list(APPEND cmkr_SOURCES
cmake.toml
)
set(CMKR_SOURCES ${cmkr_SOURCES})
add_executable(cmkr) add_executable(cmkr)
target_sources(cmkr PRIVATE ${cmkr_SOURCES}) if(cmkr_SOURCES)
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
endif()
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
if(NOT CMKR_VS_STARTUP_PROJECT)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmkr)
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES})
target_compile_features(cmkr PRIVATE target_compile_features(cmkr PRIVATE
@@ -94,15 +126,13 @@ target_link_libraries(cmkr PRIVATE
ghc_filesystem ghc_filesystem
mpark_variant mpark_variant
ordered_map ordered_map
nlohmann_json
) )
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) generate_resources(${CMKR_TARGET})
if(NOT CMKR_VS_STARTUP_PROJECT)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmkr)
endif()
set(CMKR_TARGET cmkr) unset(CMKR_TARGET)
include("cmake/custom_targets.cmake") unset(CMKR_SOURCES)
install( install(
TARGETS TARGETS
+13 -18
View File
@@ -1,7 +1,5 @@
# cmkr # cmkr
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](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`, 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: `cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
@@ -15,29 +13,27 @@ type = "executable"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
``` ```
`cmkr` can bootstrap itself and you only need CMake and a C++ compiler to use it. `cmkr` can bootstrap itself from CMake and you only need CMake to use it.
## Getting started ## Getting started
To get started, run the following commands from your project directory: To get started run the following commands from your project directory:
```sh ```sh
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
cmake -P cmkr.cmake cmake -P cmkr.cmake
``` ```
After the bootstrapping process finishes, customize [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) for your project and run CMake: After the bootstrapping process finishes, modify [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) and open the project in your favorite IDE or build with CMake:
```sh ```sh
cmake -B build cmake -B build
cmake --build build cmake --build build
``` ```
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt` when necessary. Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt`.
<sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub> In CI settings the `cmkr` bootstrapping process is skipped so there is no extra configure-time overhead in your pipelines.
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional overhead in your pipelines.
## Template repositories ## Template repositories
@@ -57,10 +53,10 @@ Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/rele
``` ```
Usage: cmkr [arguments] Usage: cmkr [arguments]
arguments: arguments:
init [executable|library|shared|static|interface] Create a project. init [executable|library|shared|static|interface] Starts a new project in the same directory.
gen Generates CMakeLists.txt file. gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build. build <extra cmake args> Run cmake and build.
install Run cmake --install. install Run cmake --install. Needs admin privileges.
clean Clean the build directory. clean Clean the build directory.
help Show help. help Show help.
version Current cmkr version. version Current cmkr version.
@@ -68,10 +64,9 @@ arguments:
## Credits ## Credits
- [gulrak/filesystem](https://github.com/gulrak/filesystem) - https://github.com/gulrak/filesystem
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map) - https://github.com/Tessil/ordered-map
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11) - https://github.com/ToruNiina/toml11
- [mpark/variant](https://github.com/mpark/variant) - https://github.com/mpark/variant
- [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer) - https://www.svgrepo.com/svg/192268/hammer
- [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️ - https://github.com/can1357 for buying `cmkr.build` ❤️
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
+7 -4
View File
@@ -1,17 +1,17 @@
[cmake] [cmake]
version = "2.8...3.31" version = "2.8...3.8"
cmkr-include = false cmkr-include = false
[project] [project]
name = "cmkr" name = "cmkr"
version = "0.2.46" version = "0.2.12"
description = "CMakeLists generator from TOML" description = "CMakeLists generator from TOML"
languages = ["CXX"] languages = ["CXX"]
subdirs = ["third_party", "tests"]
include-after = [ include-after = [
"cmake/generate_documentation.cmake", "cmake/generate_documentation.cmake",
"cmake/generate_resources.cmake" "cmake/generate_resources.cmake"
] ]
subdirs = ["third_party", "tests"]
[target.cmkr_generate_documentation] [target.cmkr_generate_documentation]
type = "interface" type = "interface"
@@ -36,8 +36,11 @@ link-libraries = [
"ghc_filesystem", "ghc_filesystem",
"mpark_variant", "mpark_variant",
"ordered_map", "ordered_map",
"nlohmann_json",
] ]
include-after = ["cmake/custom_targets.cmake"] cmake-after = """
generate_resources(${CMKR_TARGET})
"""
[[install]] [[install]]
targets = ["cmkr"] targets = ["cmkr"]
+5 -29
View File
@@ -2,7 +2,7 @@ include_guard()
# Change these defaults to point to your infrastructure if desired # 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_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
set(CMKR_TAG "v0.2.46" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE) set(CMKR_TAG "v0.2.12" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE) set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake # To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
@@ -58,16 +58,9 @@ else()
endif() endif()
# Use cached cmkr if found # Use cached cmkr if found
if(DEFINED ENV{CMKR_CACHE}) if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}") set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}") 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 "\\/$") if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/") set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
endif() endif()
@@ -79,27 +72,10 @@ endif()
set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}") set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}") set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
# Helper function to check if a string starts with a prefix
# Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61
function(cmkr_startswith str prefix result)
string(LENGTH "${prefix}" prefix_length)
string(LENGTH "${str}" str_length)
if(prefix_length LESS_EQUAL str_length)
string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix)
if(prefix STREQUAL str_prefix)
set("${result}" ON PARENT_SCOPE)
return()
endif()
endif()
set("${result}" OFF PARENT_SCOPE)
endfunction()
# Handle upgrading logic # Handle upgrading logic
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE) if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD) if(CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE) if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
if(CMKR_STARTSWITH_BUILD)
if(DEFINED ENV{CMKR_CACHE})
message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'") message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
if(EXISTS "${CMKR_CACHED_EXECUTABLE}") if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
@@ -110,7 +86,7 @@ if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE) unset(CMKR_EXECUTABLE CACHE)
endif() endif()
elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE) elseif(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}" AND CMKR_EXECUTABLE MATCHES "^${CMKR_DIRECTORY_PREFIX}")
message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE) unset(CMKR_EXECUTABLE CACHE)
endif() endif()
-18
View File
@@ -1,18 +0,0 @@
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()
+12 -12
View File
@@ -1,21 +1,21 @@
option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT}) option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT})
set(CMKR_TESTS "" CACHE INTERNAL "List of test directories in the order declared in tests/cmake.toml")
if(CMKR_GENERATE_DOCUMENTATION)
# Hook the add_test function to capture the tests in the order declared in tests/cmake.toml
function(add_test)
cmake_parse_arguments(TEST "" "WORKING_DIRECTORY" "" ${ARGN})
get_filename_component(TEST_WORKING_DIRECTORY "${TEST_WORKING_DIRECTORY}" NAME)
list(APPEND CMKR_TESTS "${TEST_WORKING_DIRECTORY}")
set(CMKR_TESTS "${CMKR_TESTS}" CACHE INTERNAL "")
_add_test(${test} ${ARGN})
endfunction()
endif()
function(generate_documentation) function(generate_documentation)
if(CMKR_GENERATE_DOCUMENTATION) if(CMKR_GENERATE_DOCUMENTATION)
message(STATUS "[cmkr] Generating documentation...") message(STATUS "[cmkr] Generating documentation...")
# Extract the order of the tests
set(CMKR_TESTS "")
file(READ "${PROJECT_SOURCE_DIR}/tests/cmake.toml" tests_toml NO_HEX_CONVERSION)
string(REGEX MATCHALL "working-directory = \"([^\"]+)\"" tests_match "${tests_toml}")
foreach(match ${tests_match})
if(match MATCHES "working-directory = \"([^\"]+)\"")
list(APPEND CMKR_TESTS "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "This should not happen (wrong regex?)")
endif()
endforeach()
# Delete previously generated examples # Delete previously generated examples
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples") set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")
file(GLOB example_files "${example_folder}/*.md") file(GLOB example_files "${example_folder}/*.md")
-18
View File
@@ -21,22 +21,4 @@ gh_edit_branch: "main"
gh_edit_view_mode: "edit" gh_edit_view_mode: "edit"
gh_edit_source: docs gh_edit_source: docs
ga_tracking: G-DY172KY7Z9
production_url : https://cmkr.build production_url : https://cmkr.build
footer_content: |
<script src="https://giscus.app/client.js"
data-repo="build-cpp/cmkr"
data-repo-id="MDEwOlJlcG9zaXRvcnkyOTQwMDc3MzY="
data-category="Documentation"
data-category-id="DIC_kwDOEYYzuM4CTXeA"
data-mapping="pathname"
data-strict="1"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="en"
crossorigin="anonymous"
async>
</script>
-143
View File
@@ -1,143 +0,0 @@
---
layout: page
title: Basics
nav_order: 1
---
# Basics
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
A CMake **project** is a collection of targets. In the context of libraries the project usually corresponds to the **package** that other projects can depend on.
<sub>Visual Studio: a CMake **project** corresponds to a _solution_.</sub>
## Targets
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or 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 the compiler builds them.
The most commonly-used properties:
- [_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, CMake also (transitively) propagates properties of the target you link to based on their _visibility_.
You should think of **linking** in CMake as _depending on_.
{:.info}
The propagation of properties depends on their **visibility**:
- **Private**: properties that are only used when building the target itself.
- **Interface**: properties that are used when depending on this target.
- **Public**: combination of private and interface.
In practice you default to **private**, unless consumers of your library _require_ the property to build their target. In that case you use **public**.
## Example
The most intuitive example is with _include directories_. Imagine there are two targets:
1. `StringUtils`: A library with string utilities.
- _Sources_: `StringUtils/src/stringutils.cpp`
- _Include directories_: `StringUtils/include`
2. `DataProcessor`: An executable that uses functionality from `StringUtils` to process some data.
- _Sources_: `DataProcessor/src/main.cpp`
- _Link libraries_: `StringUtils`
The _include directories_ property of `StringUtils` has to be **public**. If it was **private**, the `DataProcessor` target would fail to `#include <stringutils.hpp>` since the _include directories_ property is not propagated.
The `cmake.toml` for this example would look something like this:
```toml
[project]
name = "DataProcessor"
[target.StringUtils]
type = "static"
sources = ["StringUtils/src/stringutils.cpp"]
headers = ["StringUtils/include/stringutils.hpp"]
include-directories = ["StringUtils/include"] # public (default for libraries)
[target.DataProcessor]
type = "executable"
sources = ["DataProcessor/src/main.cpp"]
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.
+27 -243
View File
@@ -2,15 +2,14 @@
layout: page layout: page
title: Reference title: Reference
permalink: /cmake-toml/ permalink: /cmake-toml/
nav_order: 3 nav_order: 1
--- ---
# Reference # Reference
This page is a reference for the options in the `cmake.toml` file. If you think anything is missing or unclear, please [edit this page](https://github.com/build-cpp/cmkr/edit/main/docs/cmake-toml.md) or open an [issue](https://github.com/build-cpp/cmkr/issues). This page is a reference for the options in the `cmake.toml` file. If you think anything is missing or unclear, please [edit this page](https://github.com/build-cpp/cmkr/edit/main/docs/cmake-toml.md) or open an [issue](https://github.com/build-cpp/cmkr/issues).
This is a reference page. Check out the [examples](/examples) and the [cmkr topic](https://github.com/topics/cmkr) as well. See the [examples](/examples) section for concrete examples.
{:.info}
## CMake configuration ## CMake configuration
@@ -28,7 +27,6 @@ name = "myproject"
version = "1.0.0" version = "1.0.0"
description = "Description of the project" description = "Description of the project"
languages = ["C", "CXX"] languages = ["C", "CXX"]
msvc-runtime = "" # dynamic (implicit default), static
cmake-before = """ cmake-before = """
message(STATUS "CMake injected before the project() call") message(STATUS "CMake injected before the project() call")
""" """
@@ -39,45 +37,17 @@ include-before = ["cmake/before-project.cmake"]
include-after = ["cmake/after-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 ## Conditions
You can specify your own named conditions and use them in any `condition` field: You can specify your own conditions and use them in any `condition` field:
```toml ```toml
[conditions] [conditions]
ptr64 = "CMAKE_SIZEOF_VOID_P EQUAL 8" arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8"
ptr32 = "CMAKE_SIZEOF_VOID_P EQUAL 4" arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4"
``` ```
This will make the `ptr64` and `ptr32` conditions available with their respective CMake expressions. This will make the `arch64` and `arch32` 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: You can also prefix most keys with `condition.` to represent a conditional:
@@ -85,13 +55,9 @@ You can also prefix most keys with `condition.` to represent a conditional:
[target] [target]
type = "executable" type = "executable"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
ptr64.sources = ["src/ptr64_only.cpp"] windows.sources = ["src/windows_specific.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): The following conditions are predefined (you can override them if you desire):
```toml ```toml
@@ -102,20 +68,11 @@ unix = "UNIX"
bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\"" bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\""
linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\"" linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\""
gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\"" gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\""
clang = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
msvc = "MSVC" 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" root = "CMKR_ROOT_PROJECT"
x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8" x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4" x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
android = "ANDROID"
apple = "APPLE"
bsd = "BSD"
cygwin = "CYGWIN"
ios = "IOS"
xcode = "XCODE"
wince = "WINCE"
``` ```
## Subdirectories ## Subdirectories
@@ -133,47 +90,18 @@ include-before = ["cmake/before-subdir.cmake"]
include-after = ["cmake/after-subdir.cmake"] include-after = ["cmake/after-subdir.cmake"]
``` ```
## Options
```toml
[options]
MYPROJECT_BUILD_TESTS = false
MYPROJECT_SPECIAL_OPTION = { value = true, help = "Docstring for this option." }
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). 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]`).
## Variables
```toml
[variables]
MYBOOL = true
MYSTRING = "hello"
```
Variables emit a [`set`](https://cmake.org/cmake/help/latest/command/set.html) and can be used to configure subprojects and packages.
## Vcpkg ## Vcpkg
```toml ```toml
[vcpkg] [vcpkg]
version = "2024.11.16" version = "2021.05.12"
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2024.11.16.tar.gz" url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2021.05.12.tar.gz"
packages = ["fmt", "zlib"] 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) or [vcpkg.link](https://vcpkg.link). The vcpkg `version` will automatically generate the `url` from the [official repository](https://github.com/microsoft/vcpkg/releases). For a custom registry you can specify your own `url` (and omit the `version`). You can browse available packages on [vcpkg.io](https://vcpkg.io/en/packages.html).
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`. To disable the [default features](https://learn.microsoft.com/en-us/vcpkg/concepts/default-features) you can do: `cpp-httplib[core,openssl]` To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
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 ## Packages
@@ -191,90 +119,50 @@ components = ["mycomponent"]
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release. **Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
```toml ```toml
# Include CMake project from git
[fetch-content.gitcontent] [fetch-content.gitcontent]
condition = "mycondition" condition = "mycondition"
git = "https://github.com/myuser/gitcontent" git = "https://github.com/myuser/gitcontent"
tag = "v0.1" tag = "v0.1"
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] [fetch-content.svncontent]
condition = "mycondition" condition = "mycondition"
svn = "https://svn-host.com/url" svn = "https://svn-host.com/url"
rev = "svn_rev" rev = "svn_rev"
# For many options, you can also use a separate table: [fetch-content.urlcontent]
# [fetch-content.bigproject.options] condition = "mycondition"
# BUILD_TESTS = false url = "https://content-host.com/urlcontent.zip"
# BUILD_EXAMPLES = false hash = "123123123123"
# 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 ## Targets
```toml ```toml
[target.mytarget] [target.mytarget]
condition = "mycondition" condition = "mycondition"
alias = "mytarget::mytarget" alias = "mytarget::mytarget"
type = "static" # executable, library, shared (DLL), static, interface, object, custom type = "static" # executable, shared (DLL), static, interface, object, library, custom
headers = ["src/mytarget.h"] headers = ["src/mytarget.h"]
sources = ["src/mytarget.cpp"] sources = ["src/mytarget.cpp"]
msvc-runtime = "" # dynamic (implicit default), static
# The keys below match the target_xxx CMake commands # The keys below match the target_xxx CMake commands
# Keys prefixed with private- will get PRIVATE visibility # Keys prefixed with private- will get PRIVATE visibility
compile-definitions = [""] # preprocessor define (-DFOO) compile-definitions = [""]
private-compile-definitions = [""] private-compile-definitions = [""]
compile-features = [""] # C++ standard version (cxx_std_20) compile-features = [""]
private-compile-features = [""] private-compile-features = [""]
compile-options = [""] # compiler flags compile-options = [""]
private-compile-options = [""] private-compile-options = [""]
include-directories = [""] # include paths/directories include-directories = [""]
private-include-directories = [""] private-include-directories = [""]
link-directories = [""] # library directories link-directories = [""]
private-link-directories = [""] private-link-directories = [""]
link-libraries = [""] # dependencies link-libraries = [""]
private-link-libraries = [""] private-link-libraries = [""]
link-options = [""] # linker flags link-options = [""]
private-link-options = [""] private-link-options = [""]
precompile-headers = [""] # precompiled headers precompile-headers = [""]
private-precompile-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 = """ cmake-before = """
message(STATUS "CMake injected before the target") message(STATUS "CMake injected before the target")
@@ -292,108 +180,6 @@ CXX_STANDARD_REQUIRED = true
FOLDER = "MyFolder" 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) ## Tests and installation (unfinished)
**Note**: The `[[test]]` and `[[install]]` are unfinished features and will likely change in a future release. **Note**: The `[[test]]` and `[[install]]` are unfinished features and will likely change in a future release.
@@ -414,9 +200,7 @@ working-directory = "mytest-dir"
condition = "mycondition" condition = "mycondition"
targets = ["mytarget", "mytest"] targets = ["mytarget", "mytest"]
destination = ["bin"] destination = ["bin"]
component = "mycomponent"
files = ["content/my.png"] files = ["content/my.png"]
dirs = ["include"] dirs = [""]
configs = ["Release", "Debug"] configs = [""]
optional = false
``` ```
-34
View File
@@ -1,34 +0,0 @@
---
# 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>
-141
View File
@@ -1,141 +0,0 @@
---
# 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>
+2 -3
View File
@@ -16,9 +16,8 @@ Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) from [GitHub](https://github.com)
name = "fetch-content" name = "fetch-content"
description = "Fetching from git" description = "Fetching from git"
[fetch-content.fmt] [fetch-content]
git = "https://github.com/fmtlib/fmt" fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
tag = "7.1.3"
[target.example] [target.example]
type = "executable" type = "executable"
-74
View File
@@ -1,74 +0,0 @@
---
# 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>
-34
View File
@@ -1,34 +0,0 @@
---
# Automatically generated from tests/msvc-runtime/cmake.toml - DO NOT EDIT
layout: default
title: Static MSVC runtime
permalink: /examples/msvc-runtime
parent: Examples
nav_order: 8
---
# Static MSVC runtime
```toml
[project]
name = "msvc-runtime"
description = "Static MSVC runtime"
msvc-runtime = "static"
# This target will compile with a static runtime
[target.static-runtime]
type = "executable"
sources = ["src/main.cpp"]
# This target overrides the [project].msvc-runtime
[target.dynamic-runtime]
type = "executable"
sources = ["src/main.cpp"]
msvc-runtime = "dynamic"
```
<sup><sub>This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml).</sub></sup>
-30
View File
@@ -1,30 +0,0 @@
---
# 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>
-99
View File
@@ -1,99 +0,0 @@
---
# 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>
+4 -4
View File
@@ -20,11 +20,11 @@ description = "Target templates"
type = "executable" type = "executable"
sources = ["src/templates.cpp"] sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"] compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties # Unlike interface targets you can also inherit properties
properties = { [template.app.properties]
CXX_STANDARD = "11", CXX_STANDARD = "11"
CXX_STANDARD_REQUIRED = true, CXX_STANDARD_REQUIRED = true
}
[target.app-a] [target.app-a]
type = "app" type = "app"
+5 -4
View File
@@ -9,7 +9,7 @@ nav_order: 4
# Dependencies from vcpkg # Dependencies from vcpkg
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: Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
```toml ```toml
[project] [project]
@@ -17,12 +17,13 @@ name = "vcpkg"
description = "Dependencies from vcpkg" description = "Dependencies from vcpkg"
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions # See https://github.com/microsoft/vcpkg/releases for vcpkg versions
# See https://vcpkg.io/en/packages or https://vcpkg.link for available packages # See https://vcpkg.io/en/packages.html for available packages
[vcpkg] [vcpkg]
version = "2024.11.16" version = "2021.05.12"
packages = ["fmt"] packages = ["fmt"]
[find-package.fmt] [find-package]
fmt = {}
[target.example] [target.example]
type = "executable" type = "executable"
+15 -19
View File
@@ -1,12 +1,12 @@
--- ---
layout: home layout: home
title: Documentation title: Index
nav_order: 0 nav_order: 0
--- ---
# Documentation # Index
[`cmkr`](https://github.com/build-cpp/cmkr), pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io). `cmkr`, 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: `cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
@@ -19,29 +19,27 @@ type = "executable"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
``` ```
`cmkr` can bootstrap itself, and you only need CMake and a C++ compiler to use it. `cmkr` can bootstrap itself from CMake and you only need CMake to use it.
## Getting started ## Getting started
To get started, run the following commands from your project directory: To get started run the following commands from your project directory:
```sh ```sh
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
cmake -P cmkr.cmake cmake -P cmkr.cmake
``` ```
After the bootstrapping process finishes, customize [`cmake.toml`](./cmake-toml) for your project and run CMake: After the bootstrapping process finishes, modify [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) and open the project in your favorite IDE or build with CMake:
```sh ```sh
cmake -B build cmake -B build
cmake --build build cmake --build build
``` ```
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt` when necessary. Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt`.
<sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub> In CI settings the `cmkr` bootstrapping process is skipped so there is no extra configure-time overhead in your pipelines.
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional overhead in your pipelines.
## Template repositories ## Template repositories
@@ -61,10 +59,10 @@ Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/rele
``` ```
Usage: cmkr [arguments] Usage: cmkr [arguments]
arguments: arguments:
init [executable|library|shared|static|interface] Create a project. init [executable|library|shared|static|interface] Starts a new project in the same directory.
gen Generates CMakeLists.txt file. gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build. build <extra cmake args> Run cmake and build.
install Run cmake --install. install Run cmake --install. Needs admin privileges.
clean Clean the build directory. clean Clean the build directory.
help Show help. help Show help.
version Current cmkr version. version Current cmkr version.
@@ -72,10 +70,8 @@ arguments:
## Credits ## Credits
- [gulrak/filesystem](https://github.com/gulrak/filesystem) - https://github.com/gulrak/filesystem
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map) - https://github.com/Tessil/ordered-map
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11) - https://github.com/ToruNiina/toml11
- [mpark/variant](https://github.com/mpark/variant) - https://github.com/mpark/variant
- [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer) - https://www.svgrepo.com/svg/192268/hammer
- [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
-83
View File
@@ -1,83 +0,0 @@
---
layout: page
title: Philosophy
nav_order: 2
published: false
---
# Philosophy
## Problem statement
Similar to writing good C++, writing good CMake is difficult. The main difference is that nobody actually wants to learn CMake. The build system is something that should "just work".
There have been many attempts at creating new build systems (with varying levels of success). Naturally this causes [competing standards](https://xkcd.com/927/), which is undesirable. CMake is pretty much the de facto standard, and it has seen extensive use in complex software projects.
One of the main issues of CMake is the turing-complete scripting language you use to describe your projects. As your project gets more complex this can be very helpful, but it also unnecessarily complicates simple projects. There have been discussions about a declarative language on the [CMake issue tracker](https://gitlab.kitware.com/cmake/cmake/-/issues/19891) to solve this problem, but it looks like a "LISP-like" language is seriously being considered...
## The solution
The way cmkr (pronounced "cmaker") solves this problem is by using [TOML](https://toml.io/). Below is a minimal `cmake.toml` project:
```toml
[project]
name = "cmkr_for_beginners"
[target.hello_world]
type = "executable"
sources = ["src/main.cpp"]
```
The key difference between `cmkr` and other build systems is that it _generates_ CMake. This means that your projects are fully compatible with the CMake ecosystem, and you can try it out without having to rewrite your whole build system.
TODO: link/include more examples? Talk about conditions, packaging (missing)
### Another layer?!
A common issue people have with cmkr is that it introduces an additional layer of indirection to your build system. CMake is already a meta-buildsystem, which means you could call cmkr a "meta-meta-buildsystem".
Presumably the reason for this friction is that additional layers of abstraction introduce additional complexity. Because of this cmkr has been designed to be completely seamless:
- The user doesn't have to install any additional software to use cmkr. All you need is a semi-recent version of CMake and a C++ compiler.
- Modifying `cmake.toml` automatically triggers a regeneration of `CMakeLists.txt`. There is no change to your build process.
- The `CMakeLists.txt` is generated to be human-readable. This means you can easily "eject" from cmkr and go back to CMake.
An additional argument for cmkr is that anecdotally people say it "just works". Because of its simplicity it's also easy to teach, even to people without programming experience.
There is also precedent in the JavaScript community. Bundlers and translators are the norm there and their developer experience is miles ahead of C++.
<sub>Not to say the JavaScript ecosystem is without its flaws, but generators does not appear to be one of them</sub>
### Unsupported features
Because cmkr is still in early in development there are many missing/unfinished features. It was decided that users can bridge the gap by including CMake at arbitrary locations.
This has the advantage that it forces complexity in the build system to be self-contained and modular, a problem all too common in projects as they age.
## Enterprise
Words like "bootstrapping" and "generating code" might worry engineers working in an enterprise environment, and rightly so. From the beginning it has been a priority to make cmkr suitable for use in big corporations with strict protocols for security and reproducibility.
### No additional dependencies
As mentioned above, the only thing you need is a working C++ compiler and a semi-recent version of CMake. It is assumed that you are already building (and executing) C++ projects on your servers, so cmkr does not introduce additional requirements.
All the logic for downloading and compiling the `cmkr` executable is self-contained in a ~250 line `cmkr.cmake` script. You can easily audit it and see if it's up to your standards.
### Reproducibility
Per default the `cmkr.cmake` bootstrapping script contains the exact version of cmkr used to generate your project. As long as the cmkr repository is available you will build the exact same version of cmkr, which will generate the exact same `CMakeLists.txt` file.
This also means that cmkr can decide to break backwards compatibility without affecting legacy projects. An effort will always be made to maintain backwards compatibility though.
### Integrity
As an additional safeguard you can modify `cmkr.cmake` to pin the version tag to a commit hash. This hash is checked to ensure the integrity of the upstream repository.
### Availability
You can easily point `cmkr.cmake` to a mirror of the cmkr repository to ensure availability should something catastrophic happen.
### Not executed in CI
The final (and key) feature is that the bootstrapping process is never executed in CI environments. This means `cmkr` is only ever executed on your developer's machines and not on your infrastructure.
View File
+2
View File
@@ -0,0 +1,2 @@
bundle exec just-the-docs rake search:init
bundle exec jekyll serve --livereload
View File
+2
View File
@@ -7,3 +7,5 @@ const char *handle_args(int argc, char **argv);
} // namespace args } // namespace args
} // namespace cmkr } // namespace cmkr
const char *cmkr_args_handle_args(int, char **);
+6
View File
@@ -11,3 +11,9 @@ int install();
} // namespace build } // namespace build
} // namespace cmkr } // namespace cmkr
int cmkr_build_run(int argc, char **argv);
int cmkr_build_clean();
int cmkr_build_install();
+27
View File
@@ -0,0 +1,27 @@
#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);
+3 -1
View File
@@ -1,6 +1,8 @@
#pragma once #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) #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS #define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
+4
View File
@@ -9,3 +9,7 @@ const char *message() noexcept;
} // namespace help } // namespace help
} // namespace cmkr } // namespace cmkr
const char *cmkr_help_version(void);
const char *cmkr_help_message(void);
+19 -147
View File
@@ -1,11 +1,9 @@
#pragma once #pragma once
#include <vector>
#include <string>
#include <mpark/variant.hpp> #include <mpark/variant.hpp>
#include <string>
#include <tsl/ordered_map.h> #include <tsl/ordered_map.h>
#include <tsl/ordered_set.h> #include <vector>
namespace cmkr { namespace cmkr {
namespace parser { namespace parser {
@@ -15,21 +13,21 @@ using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>; using ConditionVector = Condition<std::vector<std::string>>;
struct Variable { struct Setting {
std::string name; std::string name;
std::string help; std::string comment;
mpark::variant<bool, std::string> value; mpark::variant<bool, std::string> val;
bool cache = false; bool cache = false;
bool force = false; bool force = false;
}; };
struct Option { struct Option {
std::string name; std::string name;
std::string help; std::string comment;
mpark::variant<bool, std::string> value; bool val = false;
}; };
struct Package { struct FindPackage {
std::string name; std::string name;
std::string condition; std::string condition;
std::string version; std::string version;
@@ -45,18 +43,9 @@ struct Vcpkg {
struct Package { struct Package {
std::string name; std::string name;
std::vector<std::string> features; std::vector<std::string> features;
bool default_features = true;
}; };
std::vector<Package> packages; 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 { enum TargetType {
@@ -74,108 +63,11 @@ enum TargetType {
extern const char *targetTypeNames[target_last]; extern const char *targetTypeNames[target_last];
struct Target { 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; std::string name;
TargetType type = target_last; TargetType type = target_last;
std::string type_name; std::string type_name;
ConditionVector headers;
ConditionVector sources; ConditionVector sources;
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands // https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
@@ -203,11 +95,6 @@ struct Target {
ConditionVector precompile_headers; ConditionVector precompile_headers;
ConditionVector private_precompile_headers; ConditionVector private_precompile_headers;
ConditionVector dependencies;
CustomTarget custom_target;
std::vector<CustomCommand> custom_commands;
std::string condition; std::string condition;
std::string alias; std::string alias;
Condition<tsl::ordered_map<std::string, std::string>> properties; Condition<tsl::ordered_map<std::string, std::string>> properties;
@@ -221,7 +108,6 @@ struct Target {
struct Template { struct Template {
Target outline; Target outline;
std::string add_function; std::string add_function;
std::vector<std::string> add_arguments;
bool pass_sources_to_add_function = false; bool pass_sources_to_add_function = false;
}; };
@@ -242,7 +128,6 @@ struct Install {
std::vector<std::string> configs; std::vector<std::string> configs;
std::string destination; std::string destination;
std::string component; std::string component;
bool optional = false;
}; };
struct Subdir { struct Subdir {
@@ -255,35 +140,26 @@ struct Subdir {
ConditionVector include_after; ConditionVector include_after;
}; };
struct ContentOption {
mpark::variant<bool, std::string> value;
};
struct Content { struct Content {
std::string name; std::string name;
std::string condition; std::string condition;
tsl::ordered_map<std::string, std::string> arguments; tsl::ordered_map<std::string, std::string> arguments;
tsl::ordered_map<std::string, ContentOption> options;
Condition<std::string> cmake_before; Condition<std::string> cmake_before;
Condition<std::string> cmake_after; Condition<std::string> cmake_after;
ConditionVector include_before; ConditionVector include_before;
ConditionVector include_after; ConditionVector include_after;
bool system = false;
std::string subdir;
}; };
enum MsvcRuntimeType { struct Package {
msvc_dynamic, std::string name;
msvc_static, std::vector<std::string> targets;
msvc_last, std::vector<std::string> headers;
std::vector<std::string> dependencies;
std::string namespace_name;
}; };
extern const char *msvcRuntimeTypeNames[msvc_last];
struct Project { struct Project {
const Project *parent;
// This is the CMake version required to use all cmkr versions. // This is the CMake version required to use all cmkr versions.
std::string cmake_version = "3.15"; std::string cmake_version = "3.15";
std::string cmkr_include = "cmkr.cmake"; std::string cmkr_include = "cmkr.cmake";
@@ -300,17 +176,16 @@ struct Project {
std::string project_name; std::string project_name;
std::string project_version; std::string project_version;
std::string project_description; std::string project_description;
ConditionVector project_languages; std::vector<std::string> project_languages;
bool project_allow_unknown_languages = false;
MsvcRuntimeType project_msvc_runtime = msvc_last;
Condition<std::string> cmake_before; Condition<std::string> cmake_before;
Condition<std::string> cmake_after; Condition<std::string> cmake_after;
ConditionVector include_before; ConditionVector include_before;
ConditionVector include_after; ConditionVector include_after;
std::vector<Variable> variables; std::vector<Setting> settings;
std::vector<Option> options; std::vector<Option> options;
std::vector<Package> packages; std::vector<FindPackage> packages;
Vcpkg vcpkg; Vcpkg vcpkg;
Package package;
std::vector<Content> contents; std::vector<Content> contents;
std::vector<Template> templates; std::vector<Template> templates;
std::vector<Target> targets; std::vector<Target> targets;
@@ -320,9 +195,6 @@ struct Project {
std::vector<Subdir> subdirs; std::vector<Subdir> subdirs;
Project(const Project *parent, const std::string &path, bool build); 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); bool is_root_path(const std::string &path);
+16 -4
View File
@@ -2,8 +2,10 @@
#include "build.hpp" #include "build.hpp"
#include "cmake_generator.hpp" #include "cmake_generator.hpp"
#include "help.hpp" #include "help.hpp"
#include "fs.hpp"
#include "fs.hpp"
#include <exception>
#include <iostream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -35,17 +37,17 @@ const char *handle_args(int argc, char **argv) {
} else if (main_arg == "build") { } else if (main_arg == "build") {
auto ret = build::run(argc, argv); auto ret = build::run(argc, argv);
if (ret) if (ret)
throw std::runtime_error("CMake build failed!"); return "CMake build error!";
return "CMake build completed!"; return "CMake build completed!";
} else if (main_arg == "install") { } else if (main_arg == "install") {
auto ret = build::install(); auto ret = build::install();
if (ret) if (ret)
throw std::runtime_error("CMake install failed!"); return "CMake install error!";
return "CMake install completed!"; return "CMake install completed!";
} else if (main_arg == "clean") { } else if (main_arg == "clean") {
auto ret = build::clean(); auto ret = build::clean();
if (ret) if (ret)
throw std::runtime_error("CMake clean failed!"); return "CMake clean error!";
return "Cleaned build directory!"; return "Cleaned build directory!";
} else { } else {
throw std::runtime_error(cmkr::help::message()); throw std::runtime_error(cmkr::help::message());
@@ -53,3 +55,13 @@ const char *handle_args(int argc, char **argv) {
} }
} // namespace args } // namespace args
} // namespace cmkr } // namespace cmkr
const char *cmkr_args_handle_args(int argc, char **argv) {
try {
return cmkr::args::handle_args(argc, argv);
} catch (const std::exception &e) {
return e.what();
} catch (...) {
return "Unknown error!";
}
}
+38 -4
View File
@@ -1,10 +1,14 @@
#include "build.hpp" #include "build.hpp"
#include "cmake_generator.hpp" #include "cmake_generator.hpp"
#include "error.hpp"
#include "project_parser.hpp" #include "project_parser.hpp"
#include "fs.hpp" #include "fs.hpp"
#include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <system_error>
namespace cmkr { namespace cmkr {
namespace build { namespace build {
@@ -13,7 +17,7 @@ int run(int argc, char **argv) {
parser::Project project(nullptr, ".", true); parser::Project project(nullptr, ".", true);
if (argc > 2) { if (argc > 2) {
for (int i = 2; i < argc; ++i) { for (int i = 2; i < argc; ++i) {
project.build_args.emplace_back(argv[i]); project.build_args.push_back(argv[i]);
} }
} }
std::stringstream ss; std::stringstream ss;
@@ -44,13 +48,13 @@ int run(int argc, char **argv) {
} }
int clean() { int clean() {
bool success = false; bool ret = false;
parser::Project project(nullptr, ".", true); parser::Project project(nullptr, ".", true);
if (fs::exists(project.build_dir)) { if (fs::exists(project.build_dir)) {
success = fs::remove_all(project.build_dir); ret = fs::remove_all(project.build_dir);
fs::create_directory(project.build_dir); fs::create_directory(project.build_dir);
} }
return success ? EXIT_SUCCESS : EXIT_FAILURE; return !ret;
} }
int install() { int install() {
@@ -60,3 +64,33 @@ int install() {
} }
} // namespace build } // namespace build
} // namespace cmkr } // 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);
}
}
+349 -1118
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
#include "error.hpp"
#include <cassert>
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 && i < (sizeof(err_string) / sizeof(*(err_string))));
return err_string[i];
}
+5 -3
View File
@@ -4,9 +4,7 @@
namespace cmkr { namespace cmkr {
namespace help { namespace help {
const char *version() noexcept { const char *version() noexcept { return "cmkr version " CMKR_VERSION; }
return "cmkr version " CMKR_VERSION;
}
const char *message() noexcept { const char *message() noexcept {
return R"lit( return R"lit(
@@ -23,3 +21,7 @@ arguments:
} }
} // namespace help } // namespace help
} // namespace cmkr } // namespace cmkr
const char *cmkr_help_version(void) { return cmkr::help::version(); }
const char *cmkr_help_message(void) { return cmkr::help::message(); }
+73 -700
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1,4 +1,3 @@
# These will be generated by cmkr, so no point in tracking them # These will be generated by cmkr, so no point in tracking them
**/CMakeLists.txt **/CMakeLists.txt
**/cmkr.cmake **/cmkr.cmake
**/vcpkg.json
+8 -81
View File
@@ -14,7 +14,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/basic" "${CMAKE_CURRENT_LIST_DIR}/basic"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -24,7 +24,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/interface" "${CMAKE_CURRENT_LIST_DIR}/interface"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -34,7 +34,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/fetch-content" "${CMAKE_CURRENT_LIST_DIR}/fetch-content"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -44,7 +44,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/conditions" "${CMAKE_CURRENT_LIST_DIR}/conditions"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -54,7 +54,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/vcpkg" "${CMAKE_CURRENT_LIST_DIR}/vcpkg"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -64,7 +64,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/cxx-standard" "${CMAKE_CURRENT_LIST_DIR}/cxx-standard"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -74,7 +74,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/globbing" "${CMAKE_CURRENT_LIST_DIR}/globbing"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
@@ -84,80 +84,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/templates" "${CMAKE_CURRENT_LIST_DIR}/templates"
COMMAND COMMAND
"$<TARGET_FILE:cmkr>" $<TARGET_FILE:cmkr>
build build
) )
if(MSVC) # msvc
add_test(
NAME
msvc-runtime
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/msvc-runtime"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
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
)
+2 -40
View File
@@ -47,45 +47,7 @@ command = "$<TARGET_FILE:cmkr>"
arguments = ["build"] arguments = ["build"]
[[test]] [[test]]
condition = "msvc" name = "package"
name = "msvc-runtime" working-directory = "package"
working-directory = "msvc-runtime"
command = "$<TARGET_FILE:cmkr>"
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>" command = "$<TARGET_FILE:cmkr>"
arguments = ["build"] arguments = ["build"]
-18
View File
@@ -1,18 +0,0 @@
# 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.
-5
View File
@@ -1,5 +0,0 @@
#include <cstdio>
int main() {
puts("Hello from cmkr!");
}
-5
View File
@@ -2,9 +2,6 @@
name = "conditions" name = "conditions"
cmake-after = "set(CUSTOM ON)" cmake-after = "set(CUSTOM ON)"
[options]
CONDITIONS_BUILD_TESTS = "root"
[conditions] [conditions]
custom = "CUSTOM" custom = "CUSTOM"
@@ -18,8 +15,6 @@ macos.cmake-after = "message(STATUS macos-after)"
linux.cmake-after = "message(STATUS linux-after)" linux.cmake-after = "message(STATUS linux-after)"
unix.cmake-after = "message(STATUS unix-after)" unix.cmake-after = "message(STATUS unix-after)"
custom.cmake-after = "message(STATUS custom-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] [target.example.properties]
AUTOMOC = false AUTOMOC = false
+1 -2
View File
@@ -1,2 +1 @@
int main() { int main() { }
}
@@ -1,4 +1,3 @@
#include <Windows.h> #include <Windows.h>
void foo() { void foo() { }
}
-123
View File
@@ -1,123 +0,0 @@
# 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"
@@ -1,13 +0,0 @@
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")
@@ -1,9 +0,0 @@
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}")
-6
View File
@@ -1,6 +0,0 @@
#include <iostream>
int main() {
std::cout << "Hello, world!" << std::endl;
return 0;
}
-7
View File
@@ -1,7 +0,0 @@
#include "generated.hpp"
int generated_value();
int main() {
return generated_value() == GENERATED_VALUE ? 0 : 1;
}
+2 -1
View File
@@ -1,7 +1,8 @@
#include <cstdio> #include <cstdio>
#include <tuple> #include <tuple>
int main() { int main()
{
auto tpl = std::make_tuple(1, 2); auto tpl = std::make_tuple(1, 2);
printf("Hello from C++11 %d\n", std::get<0>(tpl)); printf("Hello from C++11 %d\n", std::get<0>(tpl));
} }
+2 -3
View File
@@ -4,9 +4,8 @@
name = "fetch-content" name = "fetch-content"
description = "Fetching from git" description = "Fetching from git"
[fetch-content.fmt] [fetch-content]
git = "https://github.com/fmtlib/fmt" fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
tag = "7.1.3"
[target.example] [target.example]
type = "executable" type = "executable"
+2 -1
View File
@@ -1,5 +1,6 @@
#include <fmt/core.h> #include <fmt/core.h>
int main() { int main()
{
fmt::print("Hello, world!\n"); fmt::print("Hello, world!\n");
} }
-54
View File
@@ -1,54 +0,0 @@
# 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"
@@ -1,44 +0,0 @@
// 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;
}
-26
View File
@@ -1,26 +0,0 @@
// 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;
}
}
+3 -2
View File
@@ -2,6 +2,7 @@
#include <string> #include <string>
namespace mylib { namespace mylib
{
std::string message(); std::string message();
} // namespace mylib }
+2 -1
View File
@@ -1,5 +1,6 @@
#include <mylib/mylib.hpp> #include <mylib/mylib.hpp>
std::string mylib::message() { std::string mylib::message()
{
return "cmkr is awesome!"; return "cmkr is awesome!";
} }
+3 -4
View File
@@ -1,5 +1,4 @@
namespace mylib { namespace mylib
static const char *version() { {
return "v1.0"; static const char* version() { return "v1.0"; }
}
} // namespace mylib } // namespace mylib
+3 -2
View File
@@ -2,6 +2,7 @@
#include "mylib/mylib.hpp" #include "mylib/mylib.hpp"
int main() { int main()
printf("mylib version: %s\n", mylib::version()); {
printf("mylib version: %s\n", mylib::version())
} }
-15
View File
@@ -1,15 +0,0 @@
[project]
name = "msvc-runtime"
description = "Static MSVC runtime"
msvc-runtime = "static"
# This target will compile with a static runtime
[target.static-runtime]
type = "executable"
sources = ["src/main.cpp"]
# This target overrides the [project].msvc-runtime
[target.dynamic-runtime]
type = "executable"
sources = ["src/main.cpp"]
msvc-runtime = "dynamic"
-5
View File
@@ -1,5 +0,0 @@
#include <cstdio>
int main() {
puts("Hello from cmkr(msvc-static)!");
}
-13
View File
@@ -1,13 +0,0 @@
# 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>"]
-7
View File
@@ -1,7 +0,0 @@
#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];
}
-14
View File
@@ -1,14 +0,0 @@
#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;
}
+20
View File
@@ -0,0 +1,20 @@
[project]
name = "Utility"
version = "1.2"
[target.Utility]
type = "static"
alias = "Utility::Utility" # autogenerate this alias as ${PROJECT_NAME}::TargetName
sources = [
"src/Utility/*.cpp",
"include/Utility/*.hpp",
]
include-directories = ["include"]
compile-features = ["cxx_std_11"]
[package]
name = "Utility" # defaults to ${PROJECT_NAME}
targets = ["Utility"] # required
headers = ["include/**.hpp", "include/**.h"] # required
dependencies = [] # defaults to all the required packages
namespace = "Utility::" # defaults to ${PROJECT_NAME}::
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <string>
namespace String
{
bool startsWith(const std::string& str, const std::string& prefix);
std::string reverse(const std::string& str);
}
+19
View File
@@ -0,0 +1,19 @@
#include <cctype>
#include <algorithm>
#include <Utility/String.hpp>
namespace String
{
bool startsWith(const std::string& str, const std::string& prefix)
{
return str.find(prefix) == 0;
}
std::string reverse(const std::string& str)
{
auto result = str;
std::reverse(result.begin(), result.end());
return result;
}
}
-2
View File
@@ -1,2 +0,0 @@
build/
*.bin
-81
View File
@@ -1,81 +0,0 @@
# 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"
-31
View File
@@ -1,31 +0,0 @@
// 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;
}
-146
View File
@@ -1,146 +0,0 @@
// 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;
}
-11
View File
@@ -1,11 +0,0 @@
[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"]
@@ -1,6 +0,0 @@
// Created by Anthony Printup on 9/18/2023.
#include <cstdio>
extern "C" void library_function() {
std::puts("Hello from library_function!");
}
-12
View File
@@ -1,12 +0,0 @@
// 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
}
+4 -4
View File
@@ -8,11 +8,11 @@ description = "Target templates"
type = "executable" type = "executable"
sources = ["src/templates.cpp"] sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"] compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties # Unlike interface targets you can also inherit properties
properties = { [template.app.properties]
CXX_STANDARD = "11", CXX_STANDARD = "11"
CXX_STANDARD_REQUIRED = true, CXX_STANDARD_REQUIRED = true
}
[target.app-a] [target.app-a]
type = "app" type = "app"
+5 -4
View File
@@ -1,16 +1,17 @@
# 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: # Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
[project] [project]
name = "vcpkg" name = "vcpkg"
description = "Dependencies from vcpkg" description = "Dependencies from vcpkg"
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions # See https://github.com/microsoft/vcpkg/releases for vcpkg versions
# See https://vcpkg.io/en/packages or https://vcpkg.link for available packages # See https://vcpkg.io/en/packages.html for available packages
[vcpkg] [vcpkg]
version = "2024.11.16" version = "2021.05.12"
packages = ["fmt"] packages = ["fmt"]
[find-package.fmt] [find-package]
fmt = {}
[target.example] [target.example]
type = "executable" type = "executable"
+2 -1
View File
@@ -1,5 +1,6 @@
#include <fmt/core.h> #include <fmt/core.h>
int main() { int main()
{
fmt::print("Hello, world!\n"); fmt::print("Hello, world!\n");
} }
+11
View File
@@ -0,0 +1,11 @@
{
"$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": ""
}
-3
View File
@@ -1,3 +0,0 @@
# Disable clang-format in this folder
DisableFormat: true
SortIncludes: Never
Generated Vendored
+5 -2
View File
@@ -11,9 +11,12 @@ target_compile_definitions(ordered_map INTERFACE NOMINMAX)
# https://github.com/ToruNiina/toml11 (MIT) # https://github.com/ToruNiina/toml11 (MIT)
add_library(toml11 INTERFACE) add_library(toml11 INTERFACE)
target_include_directories(toml11 INTERFACE toml11-3.8.1) target_include_directories(toml11 INTERFACE toml11-3.6.0)
target_compile_definitions(toml11 INTERFACE TOML11_USE_UNRELEASED_TOML_FEATURES)
# https://github.com/mpark/variant (BSL-1.0) # https://github.com/mpark/variant (BSL-1.0)
add_library(mpark_variant INTERFACE) add_library(mpark_variant INTERFACE)
target_include_directories(mpark_variant INTERFACE variant-1.4.0/include) 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)
+21
View File
@@ -0,0 +1,21 @@
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.
File diff suppressed because it is too large Load Diff
@@ -25,14 +25,21 @@
#ifndef TOML_FOR_MODERN_CPP #ifndef TOML_FOR_MODERN_CPP
#define 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_MAJOR 3
#define TOML11_VERSION_MINOR 8 #define TOML11_VERSION_MINOR 5
#define TOML11_VERSION_PATCH 1 #define TOML11_VERSION_PATCH 0
#include "toml/parser.hpp" #include "toml/parser.hpp"
#include "toml/literal.hpp" #include "toml/literal.hpp"
#include "toml/serializer.hpp" #include "toml/serializer.hpp"
#include "toml/get.hpp" #include "toml/get.hpp"
#include "toml/macros.hpp"
#endif// TOML_FOR_MODERN_CPP #endif// TOML_FOR_MODERN_CPP
@@ -17,41 +17,11 @@ namespace color_ansi
{ {
namespace detail namespace detail
{ {
inline int colorize_index() inline int colorize_index()
{ {
static const int index = std::ios_base::xalloc(); static const int index = std::ios_base::xalloc();
return index; 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 } // detail
inline std::ostream& colorize(std::ostream& os) inline std::ostream& colorize(std::ostream& os)
@@ -85,21 +55,6 @@ inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} {if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os) inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return 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 } // color_ansi
// ANSI escape sequence is the only and default colorization method currently // ANSI escape sequence is the only and default colorization method currently
@@ -29,7 +29,7 @@ namespace detail
// to output character as an error message. // to output character as an error message.
inline std::string show_char(const char c) inline std::string show_char(const char c)
{ {
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. // It supress 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 // 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 // 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 // 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) invoke(location& loc)
{ {
const auto first = loc.iter(); const auto first = loc.iter();
auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.reset(first); loc.reset(first);
@@ -4,18 +4,11 @@
#define TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
#include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #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`. // 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>` // 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 // but bahaves in the opposite way. `preserve_comments` is just the same as
@@ -346,7 +339,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) // 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 // does not contain any comments in a value. To minimize the impact on the
// efficiency, this is chosen as a default. // efficiency, this is choosed as a default.
// //
// To reduce the memory footprint, later we can try empty base optimization (EBO). // To reduce the memory footprint, later we can try empty base optimization (EBO).
struct discard_comments struct discard_comments
@@ -425,14 +418,14 @@ struct discard_comments
// empty, so accessing through operator[], front/back, data causes address // empty, so accessing through operator[], front/back, data causes address
// error. // error.
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} reference operator[](const size_type) noexcept {return *data();}
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} const_reference operator[](const size_type) const noexcept {return *data();}
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} 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.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
reference front() noexcept {never_call("toml::discard_comment::front");} reference front() noexcept {return *data();}
const_reference front() const noexcept {never_call("toml::discard_comment::front");} const_reference front() const noexcept {return *data();}
reference back() noexcept {never_call("toml::discard_comment::back");} reference back() noexcept {return *data();}
const_reference back() const noexcept {never_call("toml::discard_comment::back");} const_reference back() const noexcept {return *data();}
pointer data() noexcept {return nullptr;} pointer data() noexcept {return nullptr;}
const_pointer data() const noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;}
@@ -450,18 +443,6 @@ struct discard_comments
const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};}
const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
const_reverse_iterator crend() 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;} inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
@@ -21,22 +21,7 @@ namespace toml
namespace detail namespace detail
{ {
// TODO: find more sophisticated way to handle this // TODO: find more sophisticated way to handle this
#if defined(_MSC_VER) #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_s(&dst, src);
if (result) { throw std::runtime_error("localtime_s failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_s(&dst, 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) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -51,6 +36,21 @@ inline std::tm gmtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("gmtime_r failed."); } if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst; return dst;
} }
#elif defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_s(&dst, src);
if (result) { throw std::runtime_error("localtime_s failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_s(&dst, src);
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#else // fallback. not threadsafe #else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
@@ -85,9 +85,9 @@ enum class month_t : std::uint8_t
struct local_date struct local_date
{ {
std::int16_t year{}; // A.D. (like, 2018) std::int16_t year; // A.D. (like, 2018)
std::uint8_t month{}; // [0, 11] std::uint8_t month; // [0, 11]
std::uint8_t day{}; // [1, 31] std::uint8_t day; // [1, 31]
local_date(int y, month_t m, int d) local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)), : 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 struct local_time
{ {
std::uint8_t hour{}; // [0, 23] std::uint8_t hour; // [0, 23]
std::uint8_t minute{}; // [0, 59] std::uint8_t minute; // [0, 59]
std::uint8_t second{}; // [0, 60] std::uint8_t second; // [0, 60]
std::uint16_t millisecond{}; // [0, 999] std::uint16_t millisecond; // [0, 999]
std::uint16_t microsecond{}; // [0, 999] std::uint16_t microsecond; // [0, 999]
std::uint16_t nanosecond{}; // [0, 999] std::uint16_t nanosecond; // [0, 999]
local_time(int h, int m, int s, local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0) 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 struct time_offset
{ {
std::int8_t hour{}; // [-12, 12] std::int8_t hour; // [-12, 12]
std::int8_t minute{}; // [-59, 59] std::int8_t minute; // [-59, 59]
time_offset(int h, int m) time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)), : 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 struct local_datetime
{ {
local_date date{}; local_date date;
local_time time{}; local_time time;
local_datetime(local_date d, local_time t): date(d), time(t) {} 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 struct offset_datetime
{ {
local_date date{}; local_date date;
local_time time{}; local_time time;
time_offset offset{}; time_offset offset;
offset_datetime(local_date d, local_time t, time_offset o) offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o) : date(d), time(t), offset(o)
@@ -2,32 +2,14 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP #ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP
#include <array>
#include <string>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <cstring>
#include "source_location.hpp" #include "source_location.hpp"
namespace toml 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 struct exception : public std::exception
{ {
public: public:
@@ -2,6 +2,7 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_FROM_HPP #ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP #define TOML11_FROM_HPP
#include "traits.hpp"
namespace toml namespace toml
{ {
@@ -140,7 +140,7 @@ get(basic_value<C, M, V>&& v)
// ============================================================================ // ============================================================================
// std::string_view // std::string_view
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 #if __cplusplus >= 201703L
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view> inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
@@ -215,7 +215,6 @@ template<typename T, typename C,
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...) 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::negation< // not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
@@ -256,37 +255,19 @@ get(const basic_value<C, M, V>&);
// toml::from<T>::from_toml(v) // toml::from<T>::from_toml(v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V,
detail::enable_if_t<detail::has_specialized_from<T>::value, T> std::size_t S = sizeof(::toml::from<T>)>
get(const basic_value<C, M, V>&); T get(const basic_value<C, M, V>&);
template<typename T, typename C, // T(const toml::value&) and T is not toml::basic_value
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 T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, 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> >::value, T>
get(const basic_value<C, M, V>&); 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. // array-like types; most likely STL container, like std::vector, etc.
@@ -340,7 +321,6 @@ template<typename T, typename C,
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back 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::negation< // T is not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
@@ -358,10 +338,8 @@ get(const basic_value<C, M, V>& v)
{v.location(), "here"} {v.location(), "here"}
})); }));
} }
for(std::size_t i=0; i<ar.size(); ++i) std::transform(ar.cbegin(), ar.cend(), container.begin(),
{ [](const value& x){return ::toml::get<value_type>(x);});
container[i] = ::toml::get<value_type>(ar[i]);
}
return container; return container;
} }
@@ -460,16 +438,9 @@ get(const basic_value<C, M, V>& v)
return ud; return ud;
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V,
detail::enable_if_t<detail::has_specialized_from<T>::value, T> std::size_t>
get(const basic_value<C, M, V>& v) 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); return ::toml::from<T>::from_toml(v);
} }
@@ -477,29 +448,14 @@ get(basic_value<C, M, V>& v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>, // T is constructible from toml::value std::is_constructible<T, const basic_value<C, M, V>&>
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> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return T(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 // find
@@ -1075,50 +1031,6 @@ 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)); 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 // expect
@@ -2,6 +2,7 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_INTO_HPP #ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP #define TOML11_INTO_HPP
#include "traits.hpp"
namespace toml namespace toml
{ {

Some files were not shown because too many files have changed in this diff Show More