mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f4b376e42 | |||
| fcbf937c49 | |||
| e609c8a48f | |||
| 735898dbac | |||
| f7126b0158 | |||
| b6702714eb | |||
| efa4f716c2 | |||
| 76b7b8e778 | |||
| 501b26ced5 | |||
| 5bc36275ad | |||
| 35c837a181 | |||
| 710dfa4963 | |||
| c49ee6bafe | |||
| 368273bab2 | |||
| b1249af365 | |||
| ea2ff31b5a | |||
| a32eb66df4 | |||
| 9b85833240 | |||
| ccb3d0b9ef | |||
| 49cdab39bd | |||
| e318139314 | |||
| 0f2a9a55fb | |||
| 38cec2d5d1 | |||
| 3caf593438 | |||
| c94dbd6416 | |||
| a1eaf601c6 | |||
| 10320c8e55 | |||
| 45e12c8d98 | |||
| e12ebc060d | |||
| c6e3fc159f | |||
| dc6f306d9c | |||
| e08bd5a311 | |||
| 068977e2f5 | |||
| 73147747a4 | |||
| a64fa9cc34 | |||
| eb6ccb39ee | |||
| 91dd8ce51f | |||
| efbfca27bd | |||
| 78bcf2acf7 | |||
| 83559cc5d4 | |||
| 5d85cc83e3 | |||
| dea41d80b5 | |||
| b52cf5b241 | |||
| e9480a625f | |||
| 13a7a8788a | |||
| 45ca74099c | |||
| ae3d73896e | |||
| 57042565d1 | |||
| d02ee3ed24 | |||
| 4a15d2ba55 | |||
| 87c0295f93 | |||
| a608e6b778 | |||
| 523f5f4a8a | |||
| 510cf71af5 | |||
| 87d90138b4 | |||
| 50615e0c27 | |||
| 42ed3048b3 | |||
| b0a1d79a42 | |||
| 5882bc082d | |||
| a90b3c6095 | |||
| 0a887c08f8 | |||
| 9f2cfe4083 | |||
| 15ad1dc6a7 | |||
| dabffeaed9 | |||
| 59fe5c38b8 | |||
| dcdc7ac410 | |||
| 88377a8bfb | |||
| 3a9685be82 | |||
| 5576789c18 | |||
| a362978f99 | |||
| a2f33297e4 | |||
| 674c976647 | |||
| 714a666e88 | |||
| d613e433d5 | |||
| 7c828d8740 | |||
| 559f750c89 | |||
| 4b6b72874e | |||
| 6bffa7cc71 | |||
| f415b432ed | |||
| 6c46664336 | |||
| f32aac63aa | |||
| 33d4cc4156 | |||
| 9288c8a87d | |||
| e36eee4420 | |||
| 53820c9f65 | |||
| ff7e4b8f23 | |||
| 1072cb44e2 | |||
| 926fc15fa2 | |||
| e2c929adb0 | |||
| 0dec7ca4d5 | |||
| 45e346b4a3 | |||
| 7165986190 | |||
| be0ef6d615 | |||
| d607b9028c | |||
| 099b14552c | |||
| 771c80a41a | |||
| 58c7de1d60 | |||
| a61e4bb999 | |||
| 47aaeaf8ce | |||
| 62609a0088 | |||
| e0d8a085db | |||
| 56da4144ae | |||
| 9388b3f29d | |||
| 079644cfeb | |||
| a7ca9f04c0 | |||
| 339c2aac94 | |||
| c573c9a76d | |||
| b24a0a2fdc | |||
| 854e8817c4 | |||
| 1c2947b2fc | |||
| 19e61aa1aa | |||
| bfa1e1fe0d | |||
| 56bd78f7ad | |||
| 4362b3547f | |||
| 08204d9847 | |||
| 2202aa8173 | |||
| d3829fb6cd | |||
| 3fb095210a | |||
| 9f99365a25 | |||
| be387141b5 | |||
| 09ded38f70 | |||
| ad90b1ad3e | |||
| 5bfa03d311 | |||
| bdd9d393d6 | |||
| 1deaec2b41 | |||
| cfee3bbc14 | |||
| bcaa60cabe | |||
| 7b1d33395e | |||
| 240fafc102 | |||
| 7a7ddf2699 | |||
| 605a04e72d | |||
| fd7f078127 | |||
| ee0e0d71d9 | |||
| 9d6897b572 | |||
| f8c34e5545 | |||
| e6d783ea20 | |||
| ee7fe38a7b | |||
| 66b2aad843 | |||
| d2e376080a | |||
| 634908c651 | |||
| 33911f3f1b |
+3
-2
@@ -13,13 +13,14 @@ AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
@@ -72,7 +73,7 @@ PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
|
||||
[*.{c,h,cpp,hpp,toml}]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
|
||||
[{CMakeLists.txt, *.cmake}]
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
insert_final_newline = true
|
||||
|
||||
# Exclude the third_party folder
|
||||
[/third_party/**]
|
||||
charset = unset
|
||||
end_of_line = unset
|
||||
insert_final_newline = unset
|
||||
trim_trailing_whitespace = unset
|
||||
indent_style = unset
|
||||
indent_size = unset
|
||||
@@ -1,21 +1,25 @@
|
||||
name: CMake
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
cmake:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-2019, macos-10.15, ubuntu-20.04]
|
||||
os: [windows-2022, macos-latest, ubuntu-22.04]
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
BUILD_TYPE: 'Release'
|
||||
CMAKE_GENERATOR: 'Ninja'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Visual Studio Development Environment
|
||||
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.1
|
||||
|
||||
- name: Tag cmkr.cmake
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
@@ -38,27 +42,27 @@ jobs:
|
||||
ctest -C ${{ env.BUILD_TYPE }} --verbose
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}-${{ matrix.os }}
|
||||
path: install/bin/*
|
||||
|
||||
- name: Get lowercase OS name
|
||||
id: osname
|
||||
uses: ASzc/change-string-case-action@07c1e24a97f0951e13f88870b99c058fcf0b14cf # v5
|
||||
uses: ASzc/change-string-case-action@ccb130a4e483d3e86287289183704dc9bf53e77e # master 2024-04-05
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
string: ${{ runner.os }}
|
||||
|
||||
- name: Compress artifacts
|
||||
uses: vimtor/action-zip@26a249fb00d43ca98dad77a4b3838025fc226aa1 # v1.1
|
||||
uses: vimtor/action-zip@1379ea20d4c5705669ba81fd626dd01b1c738f26 # v1.2
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
files: install/bin/
|
||||
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: lint
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: clang-format
|
||||
id: clang-format
|
||||
uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2
|
||||
with:
|
||||
exclude: './third_party'
|
||||
extensions: 'c,h,cpp,hpp'
|
||||
clangFormatVersion: 15
|
||||
style: file
|
||||
|
||||
- name: clang-format instructions
|
||||
if: ${{ failure() && steps.clang-format.outcome == 'failure' }}
|
||||
run: |
|
||||
# Instructions for fixing the formatting errors
|
||||
echo -e "\n\033[0;31mTo fix the formatting, run:\nclang-format -style=file -i \$(git ls-files \"*.c\" \"*.h\" \"*.cpp\" \"*.hpp\")\033[0m\n"
|
||||
exit 1
|
||||
|
||||
editorconfig:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run editorconfig-checker
|
||||
uses: editorconfig-checker/action-editorconfig-checker@27dd658c2b1282c7f1f4dc242cdf0f6d8af728ab # master 2025-05-30
|
||||
+2
-1
@@ -9,4 +9,5 @@ build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
CMakeLists.txt.user
|
||||
.vscode/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
Generated
+12
-15
@@ -1,7 +1,7 @@
|
||||
# This file is automatically generated from cmake.toml - DO NOT EDIT
|
||||
# See https://github.com/build-cpp/cmkr for more information
|
||||
|
||||
cmake_minimum_required(VERSION 2.8...3.8)
|
||||
cmake_minimum_required(VERSION 2.8...3.31)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
|
||||
@@ -22,7 +22,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.21
|
||||
0.2.45
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
@@ -58,24 +58,22 @@ generate_documentation()
|
||||
|
||||
# Target: cmkr
|
||||
set(cmkr_SOURCES
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
"src/error.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"src/project_parser.cpp"
|
||||
cmake.toml
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
"include/arguments.hpp"
|
||||
"include/build.hpp"
|
||||
"include/cmake_generator.hpp"
|
||||
"include/error.hpp"
|
||||
"include/fs.hpp"
|
||||
"include/help.hpp"
|
||||
"include/literals.hpp"
|
||||
"include/project_parser.hpp"
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
cmake.toml
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"src/project_parser.cpp"
|
||||
)
|
||||
|
||||
add_executable(cmkr)
|
||||
@@ -96,7 +94,6 @@ target_link_libraries(cmkr PRIVATE
|
||||
ghc_filesystem
|
||||
mpark_variant
|
||||
ordered_map
|
||||
nlohmann_json
|
||||
)
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
@@ -105,7 +102,7 @@ if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
endif()
|
||||
|
||||
set(CMKR_TARGET cmkr)
|
||||
generate_resources(${CMKR_TARGET})
|
||||
include("cmake/custom_targets.cmake")
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# cmkr
|
||||
|
||||
[](https://deepwiki.com/build-cpp/cmkr)
|
||||
|
||||
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io).
|
||||
|
||||
`cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
|
||||
|
||||
+3
-6
@@ -1,10 +1,10 @@
|
||||
[cmake]
|
||||
version = "2.8...3.8"
|
||||
version = "2.8...3.31"
|
||||
cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.21"
|
||||
version = "0.2.45"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
include-after = [
|
||||
@@ -36,11 +36,8 @@ link-libraries = [
|
||||
"ghc_filesystem",
|
||||
"mpark_variant",
|
||||
"ordered_map",
|
||||
"nlohmann_json",
|
||||
]
|
||||
cmake-after = """
|
||||
generate_resources(${CMKR_TARGET})
|
||||
"""
|
||||
include-after = ["cmake/custom_targets.cmake"]
|
||||
|
||||
[[install]]
|
||||
targets = ["cmkr"]
|
||||
|
||||
+77
-77
@@ -1,78 +1,78 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
if(NOT CMAKE_SCRIPT_MODE_FILE)
|
||||
message(FATAL_ERROR "Usage: cmake -P bump_version.cmake [1.2.3]")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
|
||||
message(FATAL_ERROR "Cannot find cmake.toml")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake")
|
||||
message(FATAL_ERROR "Cannot find cmkr.cmake")
|
||||
endif()
|
||||
|
||||
# Validate branch
|
||||
find_package(Git REQUIRED)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" branch --show-current OUTPUT_VARIABLE GIT_BRANCH)
|
||||
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
|
||||
if(NOT GIT_BRANCH STREQUAL "main")
|
||||
message(FATAL_ERROR "You need to be on the main branch, you are on: ${GIT_BRANCH}")
|
||||
endif()
|
||||
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake.toml" CMAKE_TOML)
|
||||
string(FIND "${CMAKE_TOML}" "[project]" PROJECT_INDEX)
|
||||
string(SUBSTRING "${CMAKE_TOML}" ${PROJECT_INDEX} -1 CMAKE_TOML_PROJECT)
|
||||
set(SEMVER_REGEX "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
|
||||
set(VERSION_REGEX "version = \"${SEMVER_REGEX}\"")
|
||||
if(CMAKE_TOML_PROJECT MATCHES "${VERSION_REGEX}")
|
||||
set(MAJOR "${CMAKE_MATCH_1}")
|
||||
set(MINOR "${CMAKE_MATCH_2}")
|
||||
set(PATCH "${CMAKE_MATCH_3}")
|
||||
set(OLDVERSION "${MAJOR}.${MINOR}.${PATCH}")
|
||||
else()
|
||||
message(FATAL_ERROR "Failed to match semantic version in cmake.toml")
|
||||
endif()
|
||||
|
||||
if(CMAKE_ARGV3)
|
||||
if(NOT CMAKE_ARGV3 MATCHES "${SEMVER_REGEX}")
|
||||
message(FATAL_ERROR "Invalid semantic version number '${CMAKE_ARGV3}'")
|
||||
endif()
|
||||
set(NEWVERSION "${CMAKE_ARGV3}")
|
||||
else()
|
||||
math(EXPR NEWPATCH "${PATCH} + 1")
|
||||
set(NEWVERSION "${MAJOR}.${MINOR}.${NEWPATCH}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Version ${OLDVERSION} -> ${NEWVERSION}")
|
||||
|
||||
find_program(CMKR_EXECUTABLE "cmkr" PATHS "${CMAKE_SOURCE_DIR}/build" PATH_SUFFIXES Debug Release RelWithDebInfo MinSizeRel NO_CACHE REQUIRED)
|
||||
message(STATUS "Found cmkr: ${CMKR_EXECUTABLE}")
|
||||
|
||||
# Replace version in cmake.toml
|
||||
string(REPLACE "version = \"${OLDVERSION}\"" "version = \"${NEWVERSION}\"" CMAKE_TOML "${CMAKE_TOML}")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/cmake.toml"
|
||||
CONTENT "${CMAKE_TOML}"
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
|
||||
# Run cmkr gen
|
||||
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen RESULT_VARIABLE CMKR_EXEC_RESULT)
|
||||
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "cmkr gen failed (exit code ${CMKR_EXEC_RESULT})")
|
||||
endif()
|
||||
|
||||
# Replace version in cmkr.cmake
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake" CMKR_CMAKE)
|
||||
string(REGEX REPLACE "CMKR_TAG \"[^\"]+\"" "CMKR_TAG \"v${NEWVERSION}\"" CMKR_CMAKE "${CMKR_CMAKE}")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake"
|
||||
CONTENT "${CMKR_CMAKE}"
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
|
||||
# Print git commands
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
if(NOT CMAKE_SCRIPT_MODE_FILE)
|
||||
message(FATAL_ERROR "Usage: cmake -P bump_version.cmake [1.2.3]")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
|
||||
message(FATAL_ERROR "Cannot find cmake.toml")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake")
|
||||
message(FATAL_ERROR "Cannot find cmkr.cmake")
|
||||
endif()
|
||||
|
||||
# Validate branch
|
||||
find_package(Git REQUIRED)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" branch --show-current OUTPUT_VARIABLE GIT_BRANCH)
|
||||
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
|
||||
if(NOT GIT_BRANCH STREQUAL "main")
|
||||
message(FATAL_ERROR "You need to be on the main branch, you are on: ${GIT_BRANCH}")
|
||||
endif()
|
||||
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake.toml" CMAKE_TOML)
|
||||
string(FIND "${CMAKE_TOML}" "[project]" PROJECT_INDEX)
|
||||
string(SUBSTRING "${CMAKE_TOML}" ${PROJECT_INDEX} -1 CMAKE_TOML_PROJECT)
|
||||
set(SEMVER_REGEX "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
|
||||
set(VERSION_REGEX "version = \"${SEMVER_REGEX}\"")
|
||||
if(CMAKE_TOML_PROJECT MATCHES "${VERSION_REGEX}")
|
||||
set(MAJOR "${CMAKE_MATCH_1}")
|
||||
set(MINOR "${CMAKE_MATCH_2}")
|
||||
set(PATCH "${CMAKE_MATCH_3}")
|
||||
set(OLDVERSION "${MAJOR}.${MINOR}.${PATCH}")
|
||||
else()
|
||||
message(FATAL_ERROR "Failed to match semantic version in cmake.toml")
|
||||
endif()
|
||||
|
||||
if(CMAKE_ARGV3)
|
||||
if(NOT CMAKE_ARGV3 MATCHES "${SEMVER_REGEX}")
|
||||
message(FATAL_ERROR "Invalid semantic version number '${CMAKE_ARGV3}'")
|
||||
endif()
|
||||
set(NEWVERSION "${CMAKE_ARGV3}")
|
||||
else()
|
||||
math(EXPR NEWPATCH "${PATCH} + 1")
|
||||
set(NEWVERSION "${MAJOR}.${MINOR}.${NEWPATCH}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Version ${OLDVERSION} -> ${NEWVERSION}")
|
||||
|
||||
find_program(CMKR_EXECUTABLE "cmkr" PATHS "${CMAKE_SOURCE_DIR}/build" PATH_SUFFIXES Debug Release RelWithDebInfo MinSizeRel NO_CACHE REQUIRED)
|
||||
message(STATUS "Found cmkr: ${CMKR_EXECUTABLE}")
|
||||
|
||||
# Replace version in cmake.toml
|
||||
string(REPLACE "version = \"${OLDVERSION}\"" "version = \"${NEWVERSION}\"" CMAKE_TOML "${CMAKE_TOML}")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/cmake.toml"
|
||||
CONTENT "${CMAKE_TOML}"
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
|
||||
# Run cmkr gen
|
||||
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen RESULT_VARIABLE CMKR_EXEC_RESULT)
|
||||
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "cmkr gen failed (exit code ${CMKR_EXEC_RESULT})")
|
||||
endif()
|
||||
|
||||
# Replace version in cmkr.cmake
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake" CMKR_CMAKE)
|
||||
string(REGEX REPLACE "CMKR_TAG \"[^\"]+\"" "CMKR_TAG \"v${NEWVERSION}\"" CMKR_CMAKE "${CMKR_CMAKE}")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake"
|
||||
CONTENT "${CMKR_CMAKE}"
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
|
||||
# Print git commands
|
||||
message(STATUS "Git commands to create new version:\ngit commit -a -m \"Bump to ${NEWVERSION}\"\ngit tag v${NEWVERSION}\ngit push origin main v${NEWVERSION}")
|
||||
+1
-1
@@ -2,7 +2,7 @@ include_guard()
|
||||
|
||||
# Change these defaults to point to your infrastructure if desired
|
||||
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
|
||||
set(CMKR_TAG "v0.2.21" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.45" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
|
||||
|
||||
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
generate_resources(cmkr)
|
||||
|
||||
add_custom_target(regenerate-cmake
|
||||
COMMAND "$<TARGET_FILE:cmkr>" gen
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}" -C $<CONFIG>
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
else()
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}"
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
endif()
|
||||
@@ -15,7 +15,7 @@ function(generate_documentation)
|
||||
message(FATAL_ERROR "This should not happen (wrong regex?)")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
# Delete previously generated examples
|
||||
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")
|
||||
file(GLOB example_files "${example_folder}/*.md")
|
||||
@@ -41,21 +41,21 @@ function(generate_documentation)
|
||||
# Read cmake.toml file
|
||||
file(READ "${test_toml}" test_contents NO_HEX_CONVERSION)
|
||||
string(LENGTH "${test_contents}" toml_length)
|
||||
|
||||
|
||||
# Extract header text
|
||||
string(REGEX MATCH "^(\n*(#[^\n]+\n)+\n*)" EXAMPLE_HEADER "${test_contents}")
|
||||
string(LENGTH "${EXAMPLE_HEADER}" header_length)
|
||||
string(STRIP "${EXAMPLE_HEADER}" EXAMPLE_HEADER)
|
||||
string(REGEX REPLACE "\n# ?" "\n\n" EXAMPLE_HEADER "\n${EXAMPLE_HEADER}")
|
||||
string(STRIP "${EXAMPLE_HEADER}" EXAMPLE_HEADER)
|
||||
|
||||
|
||||
# Extract footer text
|
||||
string(REGEX MATCH "(((#[^\n]+)(\n+|$))+)$" EXAMPLE_FOOTER "${test_contents}")
|
||||
string(LENGTH "${EXAMPLE_FOOTER}" footer_length)
|
||||
string(STRIP "${EXAMPLE_FOOTER}" EXAMPLE_FOOTER)
|
||||
string(REGEX REPLACE "\n# ?" "\n\n" EXAMPLE_FOOTER "\n${EXAMPLE_FOOTER}")
|
||||
string(STRIP "${EXAMPLE_FOOTER}" EXAMPLE_FOOTER)
|
||||
|
||||
|
||||
# Extract toml body
|
||||
math(EXPR toml_length "${toml_length}-${header_length}-${footer_length}")
|
||||
string(SUBSTRING "${test_contents}" ${header_length} ${toml_length} EXAMPLE_TOML)
|
||||
@@ -64,7 +64,7 @@ function(generate_documentation)
|
||||
# Extract title from description
|
||||
if("${EXAMPLE_TOML}" MATCHES "description *= *\"([^\"]+)\"")
|
||||
set(EXAMPLE_TITLE "${CMAKE_MATCH_1}")
|
||||
|
||||
|
||||
# Generate documentation markdown page
|
||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/example.md.in" "${example_folder}/${EXAMPLE_PERMALINK}.md" @ONLY NEWLINE_STYLE LF)
|
||||
else()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace cmkr {
|
||||
namespace resources {
|
||||
|
||||
static const char* @RESOURCE_NAME@ = R"RESOURCE(@RESOURCE_CONTENTS@)RESOURCE";
|
||||
|
||||
}
|
||||
namespace cmkr {
|
||||
namespace resources {
|
||||
|
||||
static const char* @RESOURCE_NAME@ = R"RESOURCE(@RESOURCE_CONTENTS@)RESOURCE";
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
cmkr.build
|
||||
cmkr.build
|
||||
|
||||
+80
-13
@@ -6,7 +6,7 @@ nav_order: 1
|
||||
|
||||
# Basics
|
||||
|
||||
To effectively use cmkr it helps to understand the basic concepts of CMake.
|
||||
To effectively use cmkr it helps to understand the basic concepts of CMake. This page serves as an introduction to CMake for beginners. Links to the CMake documentation are included for reference.
|
||||
|
||||
## Projects
|
||||
|
||||
@@ -16,25 +16,41 @@ A CMake **project** is a collection of targets. In the context of libraries the
|
||||
|
||||
## Targets
|
||||
|
||||
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or library you can build. There are also [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but we ignore them for now.
|
||||
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or static/dynamic library you can build.
|
||||
|
||||
<sub>Visual Studio: a **target** corresponds to a _project_.</sub>
|
||||
|
||||
Most important target types:
|
||||
|
||||
|Type|Purpose|
|
||||
|-|-|
|
||||
|Executable|Program you run on your computer (`.exe`)|
|
||||
|Static library|Library code _compiled into_ the final executable (`.lib`, `.a`)|
|
||||
|Dynamic library|Library code loaded at runtime (`.dll`, `.so`, `.dylib`)|
|
||||
|Interface|Used for organizational purposes (common flags/includes/libraries)|
|
||||
|
||||
There are also other [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but they are not important for now.
|
||||
|
||||
## Target Properties
|
||||
|
||||
Targets have a collection of **properties** that describe how to build them.
|
||||
Targets have a collection of **properties** that describe how the compiler builds them.
|
||||
|
||||
Examples of properties:
|
||||
The most commonly-used properties:
|
||||
|
||||
- _Sources_: the `*.cpp` files used to build the target.
|
||||
- _Compile options_: Command line flags for the compiler.
|
||||
- _Link libraries_: The **dependencies** required to build this target.
|
||||
- [_Sources_](https://cmake.org/cmake/help/latest/command/target_sources.html): the `*.cpp` files used to build the target.
|
||||
- [_Include directories_](https://cmake.org/cmake/help/latest/command/target_include_directories.html): Paths to directories the compiler will search when doing `#include`.
|
||||
- [_Compile definitions_](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html): Preprocessor macro definitions used for compilation (`-DMYOPTION`).
|
||||
- [_Compile options_](https://cmake.org/cmake/help/latest/command/target_compile_options.html): Command line flags for the compiler (platform/compiler specific).
|
||||
- [_Compile features_](https://cmake.org/cmake/help/latest/command/target_compile_features.html): C/C++ standard version to build with.
|
||||
- [_Link libraries_](https://cmake.org/cmake/help/latest/command/target_link_libraries.html): The **dependencies** required to build this target.
|
||||
- [_Link options_](https://cmake.org/cmake/help/latest/command/target_link_options.html): Command line flags for the linker (platform/linker specific).
|
||||
|
||||
<sub>See the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for an exhaustive list of target properties.</sub>
|
||||
|
||||
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line of the linker, CMake also propagates properties of the target you link to.
|
||||
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line, CMake also (transitively) propagates properties of the target you link to based on their _visibility_.
|
||||
|
||||
<sub>You can think of **linking** as _depending on_.</sub>
|
||||
You should think of **linking** in CMake as _depending on_.
|
||||
{:.info}
|
||||
|
||||
The propagation of properties depends on their **visibility**:
|
||||
|
||||
@@ -44,7 +60,7 @@ The propagation of properties depends on their **visibility**:
|
||||
|
||||
In practice you default to **private**, unless consumers of your library _require_ the property to build their target. In that case you use **public**.
|
||||
|
||||
### Example
|
||||
## Example
|
||||
|
||||
The most intuitive example is with _include directories_. Imagine there are two targets:
|
||||
|
||||
@@ -67,10 +83,61 @@ name = "DataProcessor"
|
||||
type = "static"
|
||||
sources = ["StringUtils/src/stringutils.cpp"]
|
||||
headers = ["StringUtils/include/stringutils.hpp"]
|
||||
include-directories = ["StringUtils/include"]
|
||||
include-directories = ["StringUtils/include"] # public (default for libraries)
|
||||
|
||||
[target.DataProcessor]
|
||||
type = "executable"
|
||||
sources = ["DataProcessor/src/main.cpp"]
|
||||
link-libraries = ["StringUtils"]
|
||||
```
|
||||
link-libraries = ["StringUtils"] # private (default for executables)
|
||||
```
|
||||
|
||||
The generated `CMakeLists.txt` (simplified) looks like this:
|
||||
|
||||
```cmake
|
||||
project(DataProcessor)
|
||||
|
||||
# Target: StringUtils
|
||||
add_library(StringUtils STATIC
|
||||
"StringUtils/include/stringutils.hpp"
|
||||
"StringUtils/src/stringutils.cpp"
|
||||
)
|
||||
target_include_directories(StringUtils PUBLIC
|
||||
"StringUtils/include"
|
||||
)
|
||||
|
||||
# Target: DataProcessor
|
||||
add_executable(DataProcessor
|
||||
"DataProcessor/src/main.cpp"
|
||||
)
|
||||
target_link_libraries(DataProcessor PRIVATE
|
||||
StringUtils
|
||||
)
|
||||
```
|
||||
|
||||
## CMake Phases
|
||||
|
||||
CMake works in three phases:
|
||||
1. **Configure**: Execute `CMakeLists.txt` (find dependencies, create targets, OS/compiler-specific conditions, etc).
|
||||
2. **Generate**: Generate build files (propagates target properties and handles [_generator expressions_](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html)).
|
||||
3. **Build**: Execute the compiler to produce executables/libraries.
|
||||
|
||||
CMake keeps generated files separate from your source in a **build directory**. You specify a [**generator**](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) (Visual Studio/Ninja) and [**build type**](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations) (Debug/Release) during configuration.
|
||||
|
||||
```bash
|
||||
# Configure project and generate build files in build directory
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build the project
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
We specify both `CMAKE_BUILD_TYPE` and `--config` to be compatible with generators that support multiple configurations at once ((like Visual Studio/Xcode/Ninja Multi-Config).
|
||||
{:.info}
|
||||
|
||||
**Build Types**:
|
||||
- `Debug`: No optimization, debug symbols
|
||||
- `Release`: Full optimization, no debug symbols
|
||||
- `RelWithDebInfo`: Medium optimizations, debug symbols
|
||||
- `MinSizeRel`: Optimize for size, no debug symbols
|
||||
|
||||
**Generators**: CMake auto-detects (Makefiles on Linux/macOS, Visual Studio on Windows), but you can specify explicitly with `-G` during the _configure_ step.
|
||||
|
||||
+111
-29
@@ -39,17 +39,45 @@ include-before = ["cmake/before-project.cmake"]
|
||||
include-after = ["cmake/after-project.cmake"]
|
||||
```
|
||||
|
||||
### Languages
|
||||
|
||||
Supported languages are (see [`enable_language`](https://cmake.org/cmake/help/latest/command/enable_language.html) for more information):
|
||||
|
||||
- `C`
|
||||
- `CXX` → C++
|
||||
- `CSharp` → C#
|
||||
- `CUDA`
|
||||
- `OBJC` → Objective-C
|
||||
- `OBJCXX` → Objective-C++
|
||||
- `Fortran`
|
||||
- `HIP`
|
||||
- `ISPC`
|
||||
- `Swift`
|
||||
- `ASM`
|
||||
- `ASM_MASM` → [Microsoft Macro Assembler (MASM)](https://learn.microsoft.com/en-US/cpp/assembler/masm/masm-for-x64-ml64-exe)
|
||||
- `ASM_NASM` → [Netwide Assembler (NASM)](https://www.nasm.us)
|
||||
- `ASM_MARMASM` [Microsoft ARM Assembler](https://learn.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-command-line-reference)
|
||||
- `ASM-ATT`
|
||||
- `Java` (undocumented)
|
||||
- `RC` (undocumented)
|
||||
|
||||
After a language is enabled, adding sources files with the corresponding extension to your target will automatically use the appropriate compiler/assembler for it.
|
||||
|
||||
_Note_: It is generally discouraged to disable the `C` language, unless you are absolutely sure it is not used. Sometimes projects added with `fetch-content` implicitly require it and the error messages can be extremely confusing.
|
||||
|
||||
## Conditions
|
||||
|
||||
You can specify your own conditions and use them in any `condition` field:
|
||||
You can specify your own named conditions and use them in any `condition` field:
|
||||
|
||||
```toml
|
||||
[conditions]
|
||||
arch64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
arch32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
ptr64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
ptr32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
```
|
||||
|
||||
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
|
||||
This will make the `ptr64` and `ptr32` conditions available with their respective CMake expressions.
|
||||
|
||||
**Note**: condition names can only contain lower-case alphanumeric characters (`[0-9a-z]`) and dashes (`-`).
|
||||
|
||||
You can also prefix most keys with `condition.` to represent a conditional:
|
||||
|
||||
@@ -57,9 +85,11 @@ You can also prefix most keys with `condition.` to represent a conditional:
|
||||
[target]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
ptr64.sources = ["src/ptr64_only.cpp"]
|
||||
```
|
||||
|
||||
Instead of a named condition you can also specify a [CMake expression](https://cmake.org/cmake/help/latest/command/if.html#condition-syntax) in quotes. Instances of `$<name>` are replaced with the corresponding condition. For example: `"CONDITIONS_BUILD_TESTS AND $<linux>"` becomes `CONDITIONS_BUILD_TESTS AND (CMAKE_SYSTEM_NAME MATCHES "Linux")` in the final `CMakeLists.txt` file.
|
||||
|
||||
### Predefined conditions
|
||||
|
||||
The following conditions are predefined (you can override them if you desire):
|
||||
@@ -72,11 +102,20 @@ unix = "UNIX"
|
||||
bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\""
|
||||
linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\""
|
||||
gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\""
|
||||
clang = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
|
||||
msvc = "MSVC"
|
||||
clang = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
|
||||
clang-cl = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
|
||||
clang-any = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
|
||||
root = "CMKR_ROOT_PROJECT"
|
||||
x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
android = "ANDROID"
|
||||
apple = "APPLE"
|
||||
bsd = "BSD"
|
||||
cygwin = "CYGWIN"
|
||||
ios = "IOS"
|
||||
xcode = "XCODE"
|
||||
wince = "WINCE"
|
||||
```
|
||||
|
||||
## Subdirectories
|
||||
@@ -103,7 +142,7 @@ MYPROJECT_SPECIAL_OPTION = { value = true, help = "Docstring for this option." }
|
||||
MYPROJECT_BUILD_EXAMPLES = "root"
|
||||
```
|
||||
|
||||
Options correspond to [CMake cache variables](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html) that can be used to customize your project at configure-time. You can configure with `cmake -DMYPROJECT_BUILD_TESTS=ON` to enable the option. Every option automatically gets a corresponding [condition](#conditions).
|
||||
Options correspond to [CMake cache variables](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html) that can be used to customize your project at configure-time. You can configure with `cmake -DMYPROJECT_BUILD_TESTS=ON` to enable the option. Every option automatically gets a corresponding [condition](#conditions). Additionally, a normalized condition is created based on the `[project].name` (i.e. `MYPROJECT_BUILD_TESTS` becomes `build-tests`).
|
||||
|
||||
The special value `root` can be used to set the option to `true` if the project is compiled as the root project (it will be `false` if someone is including your project via `[fetch-content]` or `[subdir]`).
|
||||
|
||||
@@ -121,14 +160,20 @@ Variables emit a [`set`](https://cmake.org/cmake/help/latest/command/set.html) a
|
||||
|
||||
```toml
|
||||
[vcpkg]
|
||||
version = "2021.05.12"
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2021.05.12.tar.gz"
|
||||
version = "2024.11.16"
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2024.11.16.tar.gz"
|
||||
packages = ["fmt", "zlib"]
|
||||
overlay-ports = ["my-ports"]
|
||||
overlay-triplets = ["my-triplets"]
|
||||
```
|
||||
|
||||
The vcpkg `version` will automatically generate the `url` from the [official repository](https://github.com/microsoft/vcpkg/releases). For a custom registry you can specify your own `url` (and omit the `version`). You can browse available packages on [vcpkg.io](https://vcpkg.io/en/packages.html).
|
||||
The vcpkg `version` will automatically generate the `url` from the [official repository](https://github.com/microsoft/vcpkg/releases). For a custom registry you can specify your own `url` (and omit the `version`). You can browse available packages on [vcpkg.io](https://vcpkg.io/en/packages.html) or [vcpkg.link](https://vcpkg.link).
|
||||
|
||||
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
|
||||
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`. To disable the [default features](https://learn.microsoft.com/en-us/vcpkg/concepts/default-features) you can do: `cpp-httplib[core,openssl]`
|
||||
|
||||
The `overlay-ports` feature allows you to embed vcpkg ports inside your project, without having to fork the main vcpkg registry or creating a custom registry. You can find more information in the relevant [documentation](https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports). The `overlay-triplets` feature allows you to customize triplets and change the default behavior (for example always preferring static libraries on Windows). To specify both in one go you can do `[vcpkg].overlay = "vcpkg-overlay"`.
|
||||
|
||||
For a concrete example of all the features, take a look at the [vcpkg_template](https://github.com/build-cpp/vcpkg_template) repository.
|
||||
|
||||
## Packages
|
||||
|
||||
@@ -146,54 +191,60 @@ components = ["mycomponent"]
|
||||
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
|
||||
|
||||
```toml
|
||||
# Include CMake project from git
|
||||
[fetch-content.gitcontent]
|
||||
condition = "mycondition"
|
||||
git = "https://github.com/myuser/gitcontent"
|
||||
tag = "v0.1"
|
||||
shallow = false
|
||||
shallow = false # shallow clone (--depth 1)
|
||||
system = false
|
||||
subdir = "" # folder containing CMakeLists.txt
|
||||
|
||||
# Include a CMake project from a URL
|
||||
[fetch-content.urlcontent]
|
||||
condition = "mycondition"
|
||||
url = "https://content-host.com/urlcontent.zip"
|
||||
# Other supported algorithms:
|
||||
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
|
||||
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
|
||||
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
|
||||
|
||||
[fetch-content.svncontent]
|
||||
condition = "mycondition"
|
||||
svn = "https://svn-host.com/url"
|
||||
rev = "svn_rev"
|
||||
|
||||
[fetch-content.urlcontent]
|
||||
condition = "mycondition"
|
||||
url = "https://content-host.com/urlcontent.zip"
|
||||
# These are equivalent, supported algorithms:
|
||||
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
|
||||
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
|
||||
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Targets
|
||||
|
||||
```toml
|
||||
[target.mytarget]
|
||||
condition = "mycondition"
|
||||
alias = "mytarget::mytarget"
|
||||
type = "static" # executable, shared (DLL), static, interface, object, library, custom
|
||||
type = "static" # executable, library, shared (DLL), static, interface, object, custom
|
||||
headers = ["src/mytarget.h"]
|
||||
sources = ["src/mytarget.cpp"]
|
||||
msvc-runtime = "" # dynamic (implicit default), static
|
||||
|
||||
# The keys below match the target_xxx CMake commands
|
||||
# Keys prefixed with private- will get PRIVATE visibility
|
||||
compile-definitions = [""]
|
||||
compile-definitions = [""] # preprocessor define (-DFOO)
|
||||
private-compile-definitions = [""]
|
||||
compile-features = [""]
|
||||
compile-features = [""] # C++ standard version (cxx_std_20)
|
||||
private-compile-features = [""]
|
||||
compile-options = [""]
|
||||
compile-options = [""] # compiler flags
|
||||
private-compile-options = [""]
|
||||
include-directories = [""]
|
||||
include-directories = [""] # include paths/directories
|
||||
private-include-directories = [""]
|
||||
link-directories = [""]
|
||||
link-directories = [""] # library directories
|
||||
private-link-directories = [""]
|
||||
link-libraries = [""]
|
||||
link-libraries = [""] # dependencies
|
||||
private-link-libraries = [""]
|
||||
link-options = [""]
|
||||
link-options = [""] # linker flags
|
||||
private-link-options = [""]
|
||||
precompile-headers = [""]
|
||||
precompile-headers = [""] # precompiled headers
|
||||
private-precompile-headers = [""]
|
||||
|
||||
cmake-before = """
|
||||
@@ -212,6 +263,35 @@ CXX_STANDARD_REQUIRED = true
|
||||
FOLDER = "MyFolder"
|
||||
```
|
||||
|
||||
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. |
|
||||
| `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:
|
||||
@@ -222,6 +302,7 @@ 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
|
||||
@@ -233,6 +314,7 @@ 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)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
# Automatically generated from tests/compile-options/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Compiler flags
|
||||
permalink: /examples/compile-options
|
||||
parent: Examples
|
||||
nav_order: 9
|
||||
---
|
||||
|
||||
# Compiler flags
|
||||
|
||||
Example project that sets compiler flags and preprocessor defines for various platforms.
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "compile-options"
|
||||
description = "Compiler flags"
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc.compile-options = ["/W2"]
|
||||
msvc.compile-definitions = ["PLATFORM=\"msvc\""]
|
||||
gcc.compile-options = ["-Wall"]
|
||||
gcc.compile-definitions = ["PLATFORM=\"gcc\""]
|
||||
clang.compile-options = ["-Wall"]
|
||||
clang.compile-definitions = ["PLATFORM=\"clang\""]
|
||||
```
|
||||
|
||||
The `hello` target uses [conditions](/cmake-toml#conditions) to set different compiler flags and definitions depending on the platform. See the [targets](/cmake-toml/#targets) documentation for other things you can set.
|
||||
|
||||
_Note_: In general you only want to specify flags _required_ to compile your code without errors.
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/compile-options/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/compile-options/cmake.toml).</sub></sup>
|
||||
@@ -16,8 +16,9 @@ Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) from [GitHub](https://github.com)
|
||||
name = "fetch-content"
|
||||
description = "Fetching from git"
|
||||
|
||||
[fetch-content]
|
||||
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
|
||||
[fetch-content.fmt]
|
||||
git = "https://github.com/fmtlib/fmt"
|
||||
tag = "7.1.3"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
|
||||
@@ -9,7 +9,7 @@ nav_order: 4
|
||||
|
||||
# Dependencies from vcpkg
|
||||
|
||||
Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
|
||||
Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://github.com/microsoft/vcpkg) and links an `example` target to it:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
@@ -17,13 +17,12 @@ name = "vcpkg"
|
||||
description = "Dependencies from vcpkg"
|
||||
|
||||
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
|
||||
# See https://vcpkg.io/en/packages.html for available packages
|
||||
# See https://vcpkg.io/en/packages or https://vcpkg.link for available packages
|
||||
[vcpkg]
|
||||
version = "2022.11.14"
|
||||
version = "2024.11.16"
|
||||
packages = ["fmt"]
|
||||
|
||||
[find-package]
|
||||
fmt = {}
|
||||
[find-package.fmt]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
layout: null
|
||||
permalink: /getting-started/
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
|
||||
</head>
|
||||
---
|
||||
layout: null
|
||||
permalink: /getting-started/
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
|
||||
</head>
|
||||
</html>
|
||||
@@ -11,9 +11,3 @@ int install();
|
||||
|
||||
} // namespace build
|
||||
} // namespace cmkr
|
||||
|
||||
int cmkr_build_run(int argc, char **argv);
|
||||
|
||||
int cmkr_build_clean();
|
||||
|
||||
int cmkr_build_install();
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
struct Status {
|
||||
enum class Code {
|
||||
Success = 0,
|
||||
RuntimeError,
|
||||
InitError,
|
||||
GenerationError,
|
||||
BuildError,
|
||||
CleanError,
|
||||
InstallError,
|
||||
};
|
||||
Status(Code ec) noexcept;
|
||||
operator int() const noexcept;
|
||||
Code code() const noexcept;
|
||||
|
||||
private:
|
||||
Code ec_ = Code::Success;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_error_status_string(int);
|
||||
+2
-4
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || \
|
||||
(defined(__cplusplus) && __cplusplus >= 201703L)) && \
|
||||
defined(__has_include)
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
|
||||
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
|
||||
#define GHC_USE_STD_FS
|
||||
#include <filesystem>
|
||||
@@ -12,4 +10,4 @@ namespace fs = std::filesystem;
|
||||
#ifndef GHC_USE_STD_FS
|
||||
#include <ghc/filesystem.hpp>
|
||||
namespace fs = ghc::filesystem;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,3 @@ const char *message() noexcept;
|
||||
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_help_version(void);
|
||||
|
||||
const char *cmkr_help_message(void);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <mpark/variant.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <mpark/variant.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
#include <tsl/ordered_set.h>
|
||||
#include <vector>
|
||||
|
||||
namespace cmkr {
|
||||
namespace parser {
|
||||
@@ -44,9 +45,18 @@ struct Vcpkg {
|
||||
struct Package {
|
||||
std::string name;
|
||||
std::vector<std::string> features;
|
||||
bool default_features = true;
|
||||
};
|
||||
|
||||
std::vector<Package> packages;
|
||||
|
||||
// https://github.com/Microsoft/vcpkg-docs/blob/main/vcpkg/users/buildsystems/cmake-integration.md
|
||||
std::vector<std::string> overlay_ports;
|
||||
std::vector<std::string> overlay_triplets;
|
||||
|
||||
bool enabled() const {
|
||||
return !packages.empty();
|
||||
}
|
||||
};
|
||||
|
||||
enum TargetType {
|
||||
@@ -108,6 +118,7 @@ struct Target {
|
||||
struct Template {
|
||||
Target outline;
|
||||
std::string add_function;
|
||||
std::vector<std::string> add_arguments;
|
||||
bool pass_sources_to_add_function = false;
|
||||
};
|
||||
|
||||
@@ -150,6 +161,8 @@ struct Content {
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
ConditionVector include_after;
|
||||
bool system = false;
|
||||
std::string subdir;
|
||||
};
|
||||
|
||||
enum MsvcRuntimeType {
|
||||
@@ -201,9 +214,10 @@ struct Project {
|
||||
Project(const Project *parent, const std::string &path, bool build);
|
||||
const Project *root() const;
|
||||
bool cmake_minimum_version(int major, int minor) const;
|
||||
static bool is_condition_name(const std::string &name);
|
||||
};
|
||||
|
||||
bool is_root_path(const std::string &path);
|
||||
|
||||
} // namespace parser
|
||||
} // namespace cmkr
|
||||
} // namespace cmkr
|
||||
|
||||
+1
-3
@@ -2,10 +2,8 @@
|
||||
#include "build.hpp"
|
||||
#include "cmake_generator.hpp"
|
||||
#include "help.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
+4
-38
@@ -1,14 +1,10 @@
|
||||
#include "build.hpp"
|
||||
#include "cmake_generator.hpp"
|
||||
#include "error.hpp"
|
||||
#include "project_parser.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
|
||||
namespace cmkr {
|
||||
namespace build {
|
||||
@@ -17,7 +13,7 @@ int run(int argc, char **argv) {
|
||||
parser::Project project(nullptr, ".", true);
|
||||
if (argc > 2) {
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
project.build_args.push_back(argv[i]);
|
||||
project.build_args.emplace_back(argv[i]);
|
||||
}
|
||||
}
|
||||
std::stringstream ss;
|
||||
@@ -48,13 +44,13 @@ int run(int argc, char **argv) {
|
||||
}
|
||||
|
||||
int clean() {
|
||||
bool ret = false;
|
||||
bool success = false;
|
||||
parser::Project project(nullptr, ".", true);
|
||||
if (fs::exists(project.build_dir)) {
|
||||
ret = fs::remove_all(project.build_dir);
|
||||
success = fs::remove_all(project.build_dir);
|
||||
fs::create_directory(project.build_dir);
|
||||
}
|
||||
return !ret;
|
||||
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int install() {
|
||||
@@ -64,33 +60,3 @@ int install() {
|
||||
}
|
||||
} // namespace build
|
||||
} // namespace cmkr
|
||||
|
||||
int cmkr_build_run(int argc, char **argv) {
|
||||
try {
|
||||
return cmkr::build::run(argc, argv);
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::BuildError);
|
||||
}
|
||||
}
|
||||
|
||||
int cmkr_build_clean(void) {
|
||||
try {
|
||||
return cmkr::build::clean();
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::CleanError);
|
||||
}
|
||||
}
|
||||
|
||||
int cmkr_build_install(void) {
|
||||
try {
|
||||
return cmkr::build::install();
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::InstallError);
|
||||
}
|
||||
}
|
||||
|
||||
+367
-156
@@ -1,15 +1,14 @@
|
||||
#include "cmake_generator.hpp"
|
||||
#include "error.hpp"
|
||||
#include "literals.hpp"
|
||||
#include <resources/cmkr.hpp>
|
||||
|
||||
#include "fs.hpp"
|
||||
#include "project_parser.hpp"
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
|
||||
namespace cmkr {
|
||||
namespace gen {
|
||||
@@ -43,7 +42,7 @@ static tsl::ordered_map<std::string, std::vector<std::string>> known_languages =
|
||||
{"Swift", {".swift"}},
|
||||
};
|
||||
|
||||
static std::string format(const char *format, tsl::ordered_map<std::string, std::string> variables) {
|
||||
static std::string format(const char *format, const tsl::ordered_map<std::string, std::string> &variables) {
|
||||
std::string s = format;
|
||||
for (const auto &itr : variables) {
|
||||
size_t start_pos = 0;
|
||||
@@ -55,57 +54,85 @@ static std::string format(const char *format, tsl::ordered_map<std::string, std:
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool is_root_project) {
|
||||
std::vector<std::string> temp;
|
||||
|
||||
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
|
||||
auto fullpath = full.string();
|
||||
auto base_len = base.string().length();
|
||||
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
|
||||
return delet;
|
||||
static std::vector<fs::path> expand_cmake_path(const fs::path &source_path, const fs::path &toml_dir, bool is_root_project) {
|
||||
auto is_subdir = [](fs::path p, const fs::path &root) {
|
||||
while (true) {
|
||||
if (p == root) {
|
||||
return true;
|
||||
}
|
||||
auto parent = p.parent_path();
|
||||
if (parent == p) {
|
||||
break;
|
||||
}
|
||||
p = parent;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto stem = name.filename().stem().string();
|
||||
auto ext = name.extension();
|
||||
|
||||
if (is_root_project && stem == "**" && name == name.filename()) {
|
||||
throw std::runtime_error("Recursive globbing not allowed in project root: " + name.string());
|
||||
if (!is_subdir(fs::absolute(toml_dir / source_path), toml_dir)) {
|
||||
throw std::runtime_error("Path traversal is not allowed: " + source_path.string());
|
||||
}
|
||||
|
||||
// Split the path at the first period (since fs::path::stem() and fs::path::extension() split at the last period)
|
||||
std::string stem, extension;
|
||||
auto filename = source_path.filename().string();
|
||||
auto dot_position = filename.find('.');
|
||||
if (dot_position != std::string::npos) {
|
||||
stem = filename.substr(0, dot_position);
|
||||
extension = filename.substr(dot_position);
|
||||
} else {
|
||||
stem = filename;
|
||||
}
|
||||
|
||||
if (is_root_project && stem == "**" && !source_path.has_parent_path()) {
|
||||
throw std::runtime_error("Recursive globbing not allowed in project root: " + source_path.string());
|
||||
}
|
||||
|
||||
auto has_extension = [](const fs::path &file_path, const std::string &extension) {
|
||||
auto path = file_path.string();
|
||||
return path.rfind(extension) == path.length() - extension.length();
|
||||
};
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
if (stem == "*") {
|
||||
for (const auto &f : fs::directory_iterator(toml_dir / name.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && f.path().extension() == ext) {
|
||||
temp.push_back(extract_suffix(toml_dir, f));
|
||||
for (const auto &f : fs::directory_iterator(toml_dir / source_path.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && has_extension(f.path(), extension)) {
|
||||
paths.push_back(fs::relative(f, toml_dir));
|
||||
}
|
||||
}
|
||||
} else if (stem == "**") {
|
||||
for (const auto &f : fs::recursive_directory_iterator(toml_dir / name.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && f.path().extension() == ext) {
|
||||
temp.push_back(extract_suffix(toml_dir, f.path()));
|
||||
for (const auto &f :
|
||||
fs::recursive_directory_iterator(toml_dir / source_path.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && has_extension(f.path(), extension)) {
|
||||
paths.push_back(fs::relative(f, toml_dir));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temp.push_back(name.string());
|
||||
paths.push_back(source_path);
|
||||
}
|
||||
// Normalize all paths to work with CMake (it needs a / on Windows as well)
|
||||
for (auto &path : temp) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(temp.begin(), temp.end());
|
||||
return temp;
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool is_root_project) {
|
||||
// TODO: add duplicate checking
|
||||
std::vector<std::string> result;
|
||||
std::vector<std::string> paths;
|
||||
for (const auto &src : sources) {
|
||||
auto expanded = expand_cmake_path(src, toml_dir, is_root_project);
|
||||
for (const auto &f : expanded) {
|
||||
result.push_back(f);
|
||||
paths.push_back(f.string());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
// Normalize all paths to work with CMake (it needs a / on Windows as well)
|
||||
for (auto &path : paths) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(paths.begin(), paths.end());
|
||||
|
||||
// TODO: remove duplicates
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static void create_file(const fs::path &path, const std::string &contents) {
|
||||
@@ -209,18 +236,21 @@ void generate_project(const std::string &type) {
|
||||
throw std::runtime_error("Cannot initialize a project when cmake.toml already exists!");
|
||||
}
|
||||
|
||||
// Check if the folder is empty before creating any files
|
||||
auto is_empty = fs::is_empty(fs::current_path());
|
||||
|
||||
// Automatically generate .gitattributes to not skew the statistics of the repo
|
||||
generate_gitfile(".gitattributes", {"/**/CMakeLists.txt linguist-generated", "/**/cmkr.cmake linguist-vendored"});
|
||||
|
||||
// Generate .gitignore with reasonable defaults for CMake
|
||||
generate_gitfile(".gitignore", {"build*/", "cmake-build*/", ".idea/", ".vscode/"});
|
||||
generate_gitfile(".gitignore", {"build*/", "cmake-build*/", "CMakerLists.txt", "CMakeLists.txt.user"});
|
||||
|
||||
tsl::ordered_map<std::string, std::string> variables = {
|
||||
{"@name", name},
|
||||
{"@type", type},
|
||||
};
|
||||
|
||||
if (!fs::is_empty(fs::current_path())) {
|
||||
if (!is_empty) {
|
||||
// Make a backup of an existing CMakeLists.txt if it exists
|
||||
std::error_code ec;
|
||||
fs::rename("CMakeLists.txt", "CMakeLists.txt.bak", ec);
|
||||
@@ -246,7 +276,7 @@ void generate_project(const std::string &type) {
|
||||
|
||||
struct CommandEndl {
|
||||
std::stringstream &ss;
|
||||
CommandEndl(std::stringstream &ss) : ss(ss) {
|
||||
explicit CommandEndl(std::stringstream &ss) : ss(ss) {
|
||||
}
|
||||
void endl() {
|
||||
ss << '\n';
|
||||
@@ -255,10 +285,14 @@ struct CommandEndl {
|
||||
|
||||
struct RawArg {
|
||||
RawArg() = default;
|
||||
RawArg(std::string arg) : arg(std::move(arg)) {
|
||||
explicit RawArg(std::string arg) : arg(std::move(arg)) {
|
||||
}
|
||||
|
||||
std::string arg;
|
||||
|
||||
bool empty() const {
|
||||
return arg.empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Credit: JustMagic
|
||||
@@ -290,7 +324,7 @@ struct Command {
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#unquoted-argument
|
||||
// NOTE: Normally '/' does not require quoting according to the documentation but this has been the case here
|
||||
// previously, so for backwards compatibility its still here.
|
||||
if (str.find_first_of("()#\"\\'> |/;") == str.npos)
|
||||
if (str.find_first_of("()#\"\\'> |/;") == std::string::npos)
|
||||
return str;
|
||||
std::string result;
|
||||
result += "\"";
|
||||
@@ -376,7 +410,7 @@ struct Command {
|
||||
}
|
||||
|
||||
bool print_arg(const RawArg &arg) {
|
||||
if (arg.arg.empty()) {
|
||||
if (arg.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -439,14 +473,15 @@ static std::string tolf(const std::string &str) {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
struct Generator {
|
||||
Generator(const parser::Project &project) : project(project) {
|
||||
Generator(const parser::Project &project, fs::path path) : project(project), path(std::move(path)) {
|
||||
}
|
||||
Generator(const Generator &) = delete;
|
||||
|
||||
const parser::Project &project;
|
||||
fs::path path;
|
||||
std::stringstream ss;
|
||||
int indent = 0;
|
||||
|
||||
@@ -476,8 +511,8 @@ struct Generator {
|
||||
void inject_includes(const std::vector<std::string> &includes) {
|
||||
if (!includes.empty()) {
|
||||
for (const auto &file : includes) {
|
||||
if (!fs::is_regular_file(file)) {
|
||||
throw std::runtime_error("Include '" + file + "' does not exist");
|
||||
if (!fs::exists(path / file)) {
|
||||
throw std::runtime_error("Include not found: " + file);
|
||||
}
|
||||
cmd("include")(file);
|
||||
}
|
||||
@@ -490,7 +525,7 @@ struct Generator {
|
||||
throw std::runtime_error("Detected additional \" at the end of cmake block");
|
||||
}
|
||||
auto cmake_lf = tolf(cmake);
|
||||
while (cmake_lf.back() == '\n')
|
||||
while (!cmake_lf.empty() && cmake_lf.back() == '\n')
|
||||
cmake_lf.pop_back();
|
||||
bool did_indent = false;
|
||||
for (char ch : cmake_lf) {
|
||||
@@ -511,15 +546,13 @@ struct Generator {
|
||||
if (!value.empty()) {
|
||||
for (const auto &itr : value) {
|
||||
const auto &condition = itr.first;
|
||||
if (!condition.empty()) {
|
||||
cmd("if", condition)(RawArg(project.conditions.at(condition)));
|
||||
}
|
||||
auto endif = if_condition(condition);
|
||||
|
||||
if (!itr.second.empty()) {
|
||||
fn(condition, itr.second);
|
||||
}
|
||||
|
||||
if (!condition.empty()) {
|
||||
if (endif) {
|
||||
cmd("endif")().endl();
|
||||
} else if (!itr.second.empty()) {
|
||||
endl();
|
||||
@@ -529,11 +562,77 @@ struct Generator {
|
||||
}
|
||||
|
||||
void conditional_includes(const parser::ConditionVector &include) {
|
||||
handle_condition(include, [this](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
handle_condition(include, [this](const std::string &, const std::vector<std::string> &includes) {
|
||||
inject_includes(includes);
|
||||
});
|
||||
}
|
||||
|
||||
void conditional_cmake(const parser::Condition<std::string> &cmake) {
|
||||
handle_condition(cmake, [this](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
handle_condition(cmake, [this](const std::string &, const std::string &cmake) {
|
||||
inject_cmake(cmake);
|
||||
});
|
||||
}
|
||||
|
||||
bool if_condition(const std::string &condition) {
|
||||
if (condition.empty()) {
|
||||
return false;
|
||||
}
|
||||
auto found = project.conditions.find(condition);
|
||||
if (found == project.conditions.end()) {
|
||||
if (cmkr::parser::Project::is_condition_name(condition)) {
|
||||
// NOTE: this should have been caught by the parser already
|
||||
throw std::runtime_error("Condition '" + condition + "' is not defined");
|
||||
}
|
||||
cmd("if", "NOTE: unnamed condition")(RawArg(cmake_condition(condition)));
|
||||
} else {
|
||||
cmd("if", condition)(RawArg(cmake_condition(found->second)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string cmake_condition(const std::string &condition) {
|
||||
// HACK: this replaces '$<name>' with the value of the 'name' condition. We can safely
|
||||
// reuse the generator expression syntax, because it is not valid in CMake conditions.
|
||||
// TODO: properly handle quoted arguments (using a simple state machine):
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#quoted-argument
|
||||
std::string result = "";
|
||||
bool in_replacement = false;
|
||||
std::string temp;
|
||||
for (size_t i = 0; i < condition.length(); i++) {
|
||||
if (in_replacement) {
|
||||
if (condition[i] == '>') {
|
||||
in_replacement = false;
|
||||
if (temp.empty()) {
|
||||
throw std::runtime_error("Empty replacement in condition '" + condition + "'");
|
||||
}
|
||||
auto found = project.conditions.find(temp);
|
||||
if (found == project.conditions.end()) {
|
||||
throw std::runtime_error("Unknown condition '" + temp + "' in replacement");
|
||||
}
|
||||
auto has_space = found->second.find(' ') != std::string::npos;
|
||||
if (has_space) {
|
||||
result += '(';
|
||||
}
|
||||
result += found->second;
|
||||
if (has_space) {
|
||||
result += ')';
|
||||
}
|
||||
temp.clear();
|
||||
} else {
|
||||
temp += condition[i];
|
||||
}
|
||||
} else if (condition[i] == '$' && i + 1 < condition.length() && condition[i + 1] == '<') {
|
||||
i++;
|
||||
in_replacement = true;
|
||||
} else {
|
||||
result += condition[i];
|
||||
}
|
||||
}
|
||||
if (!temp.empty()) {
|
||||
throw std::runtime_error("Unterminated replacement in condition '" + condition + "'");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -542,10 +641,7 @@ struct ConditionScope {
|
||||
bool endif = false;
|
||||
|
||||
ConditionScope(Generator &gen, const std::string &condition) : gen(gen) {
|
||||
if (!condition.empty()) {
|
||||
gen.cmd("if", condition)(RawArg(gen.project.conditions.at(condition)));
|
||||
endif = true;
|
||||
}
|
||||
endif = gen.if_condition(condition);
|
||||
}
|
||||
|
||||
ConditionScope(const ConditionScope &) = delete;
|
||||
@@ -559,28 +655,26 @@ struct ConditionScope {
|
||||
};
|
||||
|
||||
static bool vcpkg_valid_identifier(const std::string &name) {
|
||||
// prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default
|
||||
auto is_reserved = [](const std::string &s) {
|
||||
if (s == "prn" || s == "aux" || s == "nul" || s == "con" || s == "core" || s == "default") {
|
||||
return true;
|
||||
}
|
||||
if (s.length() == 4 && (s.compare(0, 3, "lpt") == 0 || s.compare(0, 3, "com") == 0) && (s[3] >= '1' && s[3] <= '9')) {
|
||||
return true;
|
||||
// [a-z0-9]+(-[a-z0-9]+)*
|
||||
for (size_t i = 0; i < name.length(); i++) {
|
||||
auto c = name[i];
|
||||
if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (i > 0 && c == '-')) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// [a-z0-9]+(-[a-z0-9]+)*
|
||||
auto is_identifier = [](const std::string &s) {
|
||||
for (size_t i = 0; i < s.length(); i++) {
|
||||
auto c = s[i];
|
||||
if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (i > 0 && c == '-')) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vcpkg_identifier_reserved(const std::string &name) {
|
||||
// prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default
|
||||
if (name == "prn" || name == "aux" || name == "nul" || name == "con" || name == "core" || name == "default") {
|
||||
return true;
|
||||
};
|
||||
return is_identifier(name) && !is_reserved(name);
|
||||
}
|
||||
if (name.length() == 4 && (name.compare(0, 3, "lpt") == 0 || name.compare(0, 3, "com") == 0) && (name[3] >= '1' && name[3] <= '9')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string vcpkg_escape_identifier(const std::string &name) {
|
||||
@@ -595,12 +689,17 @@ static std::string vcpkg_escape_identifier(const std::string &name) {
|
||||
ch = '-';
|
||||
}
|
||||
|
||||
escaped += std::tolower(ch);
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
ch += ('a' - 'A');
|
||||
}
|
||||
escaped += ch;
|
||||
}
|
||||
|
||||
if (!vcpkg_valid_identifier(escaped)) {
|
||||
throw std::runtime_error("The escaped project name '" + escaped + "' is not usable with [vcpkg]");
|
||||
}
|
||||
if (vcpkg_identifier_reserved(escaped)) {
|
||||
throw std::runtime_error("The escaped project name '" + escaped + "' is a reserved name [vcpkg]");
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
@@ -614,7 +713,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
|
||||
parser::Project project(parent_project, path, false);
|
||||
|
||||
for (auto const &lang : project.project_languages) {
|
||||
for (const auto &lang : project.project_languages) {
|
||||
if (known_languages.find(lang) == known_languages.end()) {
|
||||
if (project.project_allow_unknown_languages) {
|
||||
printf("[warning] Unknown language '%s' specified\n", lang.c_str());
|
||||
@@ -624,13 +723,19 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
}
|
||||
|
||||
Generator gen(project);
|
||||
Generator gen(project, path);
|
||||
|
||||
// Helper lambdas for more convenient CMake generation
|
||||
auto &ss = gen.ss;
|
||||
auto cmd = [&gen](const std::string &command) { return gen.cmd(command); };
|
||||
auto comment = [&gen](const std::string &comment) { return gen.comment(comment); };
|
||||
auto endl = [&gen]() { gen.endl(); };
|
||||
auto cmd = [&gen](const std::string &command) {
|
||||
return gen.cmd(command);
|
||||
};
|
||||
auto comment = [&gen](const std::string &comment) {
|
||||
return gen.comment(comment);
|
||||
};
|
||||
auto endl = [&gen]() {
|
||||
gen.endl();
|
||||
};
|
||||
|
||||
std::string cmkr_url = "https://github.com/build-cpp/cmkr";
|
||||
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
|
||||
@@ -640,24 +745,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
if (is_root_project) {
|
||||
cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl();
|
||||
|
||||
if (project.project_msvc_runtime != parser::msvc_last) {
|
||||
comment("Enable support for MSVC_RUNTIME_LIBRARY");
|
||||
cmd("cmake_policy")("SET", "CMP0091", "NEW");
|
||||
|
||||
switch (project.project_msvc_runtime) {
|
||||
case parser::msvc_dynamic:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL");
|
||||
break;
|
||||
case parser::msvc_static:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
endl();
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
if (!project.allow_in_tree) {
|
||||
// clang-format off
|
||||
cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR");
|
||||
@@ -666,6 +753,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
if (project.project_msvc_runtime != parser::msvc_last) {
|
||||
comment("Enable support for MSVC_RUNTIME_LIBRARY");
|
||||
cmd("cmake_policy")("SET", "CMP0091", "NEW").endl();
|
||||
}
|
||||
|
||||
cmd("set")("CMKR_ROOT_PROJECT", "OFF");
|
||||
// clang-format off
|
||||
cmd("if")("CMAKE_CURRENT_SOURCE_DIR", "STREQUAL", "CMAKE_SOURCE_DIR");
|
||||
@@ -683,7 +775,26 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON").endl();
|
||||
|
||||
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
|
||||
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
|
||||
if (project.cmake_minimum_version(3, 0)) {
|
||||
cmd("set_property")("DIRECTORY", "APPEND", "PROPERTY", "CMAKE_CONFIGURE_DEPENDS", "cmake.toml");
|
||||
} else {
|
||||
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
|
||||
}
|
||||
|
||||
if (project.project_msvc_runtime != parser::msvc_last) {
|
||||
cmd("if")("NOT", "DEFINED", "CMAKE_MSVC_RUNTIME_LIBRARY");
|
||||
switch (project.project_msvc_runtime) {
|
||||
case parser::msvc_dynamic:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL");
|
||||
break;
|
||||
case parser::msvc_static:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cmd("endif")().endl();
|
||||
}
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
|
||||
@@ -692,12 +803,14 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
create_file(cmkr_include, resources::cmkr);
|
||||
}
|
||||
} else {
|
||||
// clang-format off
|
||||
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
|
||||
cmd("if")("CMKR_ROOT_PROJECT");
|
||||
if (project.cmake_minimum_version(3, 0)) {
|
||||
cmd("set_property")("DIRECTORY", "APPEND", "PROPERTY", "CMAKE_CONFIGURE_DEPENDS", "cmake.toml");
|
||||
} else {
|
||||
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
|
||||
}
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// TODO: remove support and replace with global compile-features
|
||||
@@ -762,6 +875,27 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
endl();
|
||||
}
|
||||
|
||||
if (project.vcpkg.enabled()) {
|
||||
comment("vcpkg settings");
|
||||
auto emit_overlay = [&cmd](const std::string &name, const std::vector<std::string> &overlay) {
|
||||
if (!overlay.empty()) {
|
||||
std::vector<std::string> set_args;
|
||||
for (const auto &directory : overlay) {
|
||||
if (!fs::path(directory).is_relative()) {
|
||||
throw std::runtime_error("[vcpkg] overlay is not a relative path: " + directory);
|
||||
}
|
||||
if (!fs::is_directory(directory)) {
|
||||
throw std::runtime_error("[vcpkg] overlay is not a directory: " + directory);
|
||||
}
|
||||
set_args.emplace_back("${CMAKE_CURRENT_SOURCE_DIR}/" + directory);
|
||||
}
|
||||
cmd("set")(name, set_args);
|
||||
}
|
||||
};
|
||||
emit_overlay("VCPKG_OVERLAY_PORTS", project.vcpkg.overlay_ports);
|
||||
emit_overlay("VCPKG_OVERLAY_TRIPLETS", project.vcpkg.overlay_triplets);
|
||||
}
|
||||
|
||||
gen.conditional_includes(project.include_before);
|
||||
gen.conditional_cmake(project.cmake_before);
|
||||
|
||||
@@ -782,7 +916,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
gen.conditional_includes(project.include_after);
|
||||
gen.conditional_cmake(project.cmake_after);
|
||||
|
||||
if (!project.vcpkg.packages.empty()) {
|
||||
if (project.vcpkg.enabled()) {
|
||||
if (!is_root_project) {
|
||||
throw std::runtime_error("[vcpkg] is only supported in the root project");
|
||||
}
|
||||
|
||||
// Allow the user to specify a url or derive it from the version
|
||||
auto url = project.vcpkg.url;
|
||||
auto version_name = url;
|
||||
@@ -795,24 +933,35 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
// Show a nicer error than vcpkg when specifying an invalid package name
|
||||
for (const auto &package : project.vcpkg.packages) {
|
||||
const auto &packages = project.vcpkg.packages;
|
||||
for (const auto &package : packages) {
|
||||
if (!vcpkg_valid_identifier(package.name)) {
|
||||
throw std::runtime_error("Invalid [vcpkg].packages name '" + package.name + "' (needs to be lowercase alphanumeric)");
|
||||
}
|
||||
}
|
||||
|
||||
tsl::ordered_map<std::string, std::string> vcpkg_args = {
|
||||
{"URL", url},
|
||||
{"SUBBUILD_DIR", "CMakeFiles/vcpkg-subbuild"},
|
||||
{"SOURCE_DIR", "vcpkg"},
|
||||
{"BINARY_DIR", "CMakeFiles/vcpkg-build"},
|
||||
};
|
||||
|
||||
// CMake to bootstrap vcpkg and download the packages
|
||||
// clang-format off
|
||||
cmd("if")("CMKR_ROOT_PROJECT", "AND", "NOT", "CMKR_DISABLE_VCPKG");
|
||||
cmd("include")("FetchContent");
|
||||
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
|
||||
cmd("FetchContent_Declare")("vcpkg", "URL", url);
|
||||
// Not using FetchContent_MakeAvailable here in case vcpkg adds CMakeLists.txt
|
||||
cmd("FetchContent_GetProperties")("vcpkg");
|
||||
cmd("if")("NOT", "vcpkg_POPULATED");
|
||||
cmd("FetchContent_Populate")("vcpkg");
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
comment("Fix warnings about DOWNLOAD_EXTRACT_TIMESTAMP");
|
||||
cmd("if")("POLICY", "CMP0135");
|
||||
cmd("cmake_policy")("SET", "CMP0135", "NEW");
|
||||
cmd("endif")();
|
||||
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
|
||||
cmd("FetchContent_Declare")("vcpkg", vcpkg_args);
|
||||
cmd("FetchContent_MakeAvailable")("vcpkg").endl();
|
||||
cmd("if")("CMAKE_HOST_SYSTEM_NAME", "STREQUAL", "Darwin", "AND", "CMAKE_OSX_ARCHITECTURES", "STREQUAL", RawArg("\"\""));
|
||||
cmd("set")("CMAKE_OSX_ARCHITECTURES", "${CMAKE_HOST_SYSTEM_PROCESSOR}", "CACHE", "STRING", RawArg("\"\""), "FORCE");
|
||||
cmd("endif")();
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
cmd("endif")();
|
||||
endl();
|
||||
// clang-format on
|
||||
@@ -825,33 +974,41 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
ofs << R"({
|
||||
"$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",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
)";
|
||||
|
||||
const auto &packages = project.vcpkg.packages;
|
||||
for (size_t i = 0; i < packages.size(); i++) {
|
||||
const auto &package = packages[i];
|
||||
const auto &features = package.features;
|
||||
if (!vcpkg_valid_identifier(package.name)) {
|
||||
throw std::runtime_error("Invalid vcpkg package name '" + package.name + "'");
|
||||
throw std::runtime_error("Invalid vcpkg package name '" + package.name + "', name is not valid");
|
||||
}
|
||||
if (vcpkg_identifier_reserved(package.name)) {
|
||||
throw std::runtime_error("Invalid vcpkg package name '" + package.name + "', name is reserved");
|
||||
}
|
||||
for (const auto &feature : features) {
|
||||
if (!vcpkg_valid_identifier(feature)) {
|
||||
throw std::runtime_error("Invalid vcpkg package feature '" + feature + "'");
|
||||
throw std::runtime_error("Invalid vcpkg package feature '" + feature + "', name is not valid");
|
||||
}
|
||||
if (vcpkg_identifier_reserved(feature)) {
|
||||
throw std::runtime_error("Invalid vcpkg package feature '" + feature + "', name is reserved");
|
||||
}
|
||||
}
|
||||
if (features.empty()) {
|
||||
if (features.empty() && package.default_features) {
|
||||
ofs << " \"" << package.name << '\"';
|
||||
} else {
|
||||
ofs << " {\n";
|
||||
ofs << " \"name\": \"" << package.name << "\",\n";
|
||||
if (!package.default_features) {
|
||||
ofs << " \"default-features\": false,\n";
|
||||
}
|
||||
ofs << " \"features\": [";
|
||||
for (size_t j = 0; j < features.size(); j++) {
|
||||
const auto &feature = features[j];
|
||||
ofs << '\"' << feature << '\"';
|
||||
if (j + 1 < features.size()) {
|
||||
ofs << ',';
|
||||
ofs << ", ";
|
||||
}
|
||||
}
|
||||
ofs << "]\n";
|
||||
@@ -877,33 +1034,10 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
ofs << " ],\n";
|
||||
ofs << " \"description\": \"" << escape(project.project_description) << "\",\n";
|
||||
ofs << " \"name\": \"" << escape(vcpkg_escape_identifier(project.project_name)) << "\",\n";
|
||||
ofs << R"( "version-string": "")" << '\n';
|
||||
ofs << " \"version-string\": \"none\"\n";
|
||||
ofs << "}\n";
|
||||
}
|
||||
|
||||
if (!project.contents.empty()) {
|
||||
cmd("include")("FetchContent").endl();
|
||||
for (const auto &content : project.contents) {
|
||||
ConditionScope cs(gen, content.condition);
|
||||
|
||||
gen.conditional_includes(content.include_before);
|
||||
gen.conditional_cmake(content.cmake_before);
|
||||
|
||||
std::string version_info = "";
|
||||
if (content.arguments.contains("GIT_TAG")) {
|
||||
version_info = " (" + content.arguments.at("GIT_TAG") + ")";
|
||||
} else if (content.arguments.contains("SVN_REVISION")) {
|
||||
version_info = " (" + content.arguments.at("SVN_REVISION") + ")";
|
||||
}
|
||||
cmd("message")("STATUS", "Fetching " + content.name + version_info + "...");
|
||||
cmd("FetchContent_Declare")(content.name, content.arguments);
|
||||
cmd("FetchContent_MakeAvailable")(content.name).endl();
|
||||
|
||||
gen.conditional_includes(content.include_after);
|
||||
gen.conditional_cmake(content.cmake_after);
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.packages.empty()) {
|
||||
comment("Packages");
|
||||
for (const auto &dep : project.packages) {
|
||||
@@ -918,6 +1052,41 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.contents.empty()) {
|
||||
cmd("include")("FetchContent").endl();
|
||||
if (!project.root()->vcpkg.enabled()) {
|
||||
comment("Fix warnings about DOWNLOAD_EXTRACT_TIMESTAMP");
|
||||
// clang-format off
|
||||
cmd("if")("POLICY", "CMP0135");
|
||||
cmd("cmake_policy")("SET", "CMP0135", "NEW");
|
||||
cmd("endif")();
|
||||
// clang-format on
|
||||
}
|
||||
for (const auto &content : project.contents) {
|
||||
ConditionScope cs(gen, content.condition);
|
||||
|
||||
gen.conditional_includes(content.include_before);
|
||||
gen.conditional_cmake(content.cmake_before);
|
||||
|
||||
std::string version_info;
|
||||
if (content.arguments.contains("GIT_TAG")) {
|
||||
version_info = " (" + content.arguments.at("GIT_TAG") + ")";
|
||||
} else if (content.arguments.contains("SVN_REVISION")) {
|
||||
version_info = " (" + content.arguments.at("SVN_REVISION") + ")";
|
||||
}
|
||||
cmd("message")("STATUS", "Fetching " + content.name + version_info + "...");
|
||||
if (content.system) {
|
||||
cmd("FetchContent_Declare")(content.name, "SYSTEM", content.arguments);
|
||||
} else {
|
||||
cmd("FetchContent_Declare")(content.name, content.arguments);
|
||||
}
|
||||
cmd("FetchContent_MakeAvailable")(content.name).endl();
|
||||
|
||||
gen.conditional_includes(content.include_after);
|
||||
gen.conditional_cmake(content.cmake_after);
|
||||
}
|
||||
}
|
||||
|
||||
auto add_subdir = [&](const std::string &dir) {
|
||||
// clang-format off
|
||||
comment("Subdirectory: " + dir);
|
||||
@@ -963,6 +1132,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
if (project_languages.empty())
|
||||
project_languages = {"C", "CXX"};
|
||||
|
||||
// Reference: https://gitlab.kitware.com/cmake/cmake/-/issues/24340#note_1304703
|
||||
project_languages.push_back("RC");
|
||||
|
||||
// All acceptable extensions based off our given languages.
|
||||
tsl::ordered_set<std::string> project_extensions;
|
||||
for (const auto &language : project_languages) {
|
||||
@@ -987,7 +1159,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
for (size_t i = 0; i < project.targets.size(); i++) {
|
||||
const auto &target = project.targets[i];
|
||||
|
||||
auto throw_target_error = [&target](const std::string &message) { throw std::runtime_error("[target." + target.name + "] " + message); };
|
||||
auto throw_target_error = [&target](const std::string &message) {
|
||||
throw std::runtime_error("[target." + target.name + "] " + message);
|
||||
};
|
||||
|
||||
const parser::Template *tmplate = nullptr;
|
||||
std::unique_ptr<ConditionScope> tmplate_cs{};
|
||||
@@ -1120,7 +1294,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
continue;
|
||||
const auto &source_path = fs::path(path) / source;
|
||||
if (!fs::exists(source_path)) {
|
||||
throw_target_error("Source file does not exist " + source);
|
||||
throw_target_error("Source file not found: " + source);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1198,12 +1372,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
// Handle custom add commands from templates.
|
||||
if (tmplate != nullptr && !tmplate->add_function.empty()) {
|
||||
add_command = tmplate->add_function;
|
||||
target_type_string = ""; // TODO: let templates supply options to the add_command here?
|
||||
|
||||
if (tmplate->pass_sources_to_add_function) {
|
||||
cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}");
|
||||
cmd(add_command)(target.name, tmplate->add_arguments, "${" + sources_var + "}");
|
||||
} else {
|
||||
cmd(add_command)(target.name, target_type_string).endl();
|
||||
cmd(add_command)(target.name, tmplate->add_arguments).endl();
|
||||
if (has_sources) {
|
||||
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE",
|
||||
"${" + sources_var + "}");
|
||||
@@ -1226,8 +1399,29 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
auto target_cmd = [&](const char *command, const parser::ConditionVector &cargs, const std::string &scope) {
|
||||
gen.handle_condition(cargs,
|
||||
[&](const std::string &, const std::vector<std::string> &args) { cmd(command)(target.name, scope, args); });
|
||||
gen.handle_condition(cargs, [&](const std::string &, const std::vector<std::string> &args) {
|
||||
cmd(command)(target.name, scope, args);
|
||||
});
|
||||
};
|
||||
|
||||
auto link_libraries = [&](const parser::ConditionVector &cargs, const std::string &scope) {
|
||||
gen.handle_condition(cargs, [&](const std::string &, const std::vector<std::string> &args) {
|
||||
std::vector<std::string> targs;
|
||||
for (const std::string &arg : args) {
|
||||
if (arg.find("::") == 0) {
|
||||
auto library = arg.substr(2);
|
||||
// clang-format off
|
||||
cmd("if")("NOT", "TARGET", library);
|
||||
cmd("message")("FATAL_ERROR", "Target \"" + library + "\" referenced by \"" + target.name + "\" does not exist!");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
targs.push_back(std::move(library));
|
||||
} else {
|
||||
targs.push_back(arg);
|
||||
}
|
||||
}
|
||||
cmd("target_link_libraries")(target.name, scope, targs);
|
||||
});
|
||||
};
|
||||
|
||||
auto gen_target_cmds = [&](const parser::Target &t) {
|
||||
@@ -1246,8 +1440,8 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
target_cmd("target_link_directories", t.link_directories, target_scope);
|
||||
target_cmd("target_link_directories", t.private_link_directories, "PRIVATE");
|
||||
|
||||
target_cmd("target_link_libraries", t.link_libraries, target_scope);
|
||||
target_cmd("target_link_libraries", t.private_link_libraries, "PRIVATE");
|
||||
link_libraries(t.link_libraries, target_scope);
|
||||
link_libraries(t.private_link_libraries, "PRIVATE");
|
||||
|
||||
target_cmd("target_link_options", t.link_options, target_scope);
|
||||
target_cmd("target_link_options", t.private_link_options, "PRIVATE");
|
||||
@@ -1270,14 +1464,17 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &properties) {
|
||||
tsl::ordered_map<std::string, RawArg> raw_properties;
|
||||
for (const auto &propItr : properties) {
|
||||
if (propItr.first == "MSVC_RUNTIME_LIBRARY") {
|
||||
if (project_root->project_msvc_runtime == parser::msvc_last) {
|
||||
throw_target_error("You cannot set msvc-runtime without setting the root [project].msvc-runtime");
|
||||
}
|
||||
}
|
||||
// NOTE: We need to make sure that empty strings do not get omitted
|
||||
raw_properties.emplace(propItr.first, Command::quote(propItr.second));
|
||||
}
|
||||
cmd("set_target_properties")(target.name, "PROPERTIES", properties);
|
||||
cmd("set_target_properties")(target.name, "PROPERTIES", raw_properties);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1316,7 +1513,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
auto working_directory = std::make_pair("WORKING_DIRECTORY", dir);
|
||||
auto command = std::make_pair("COMMAND", test.command);
|
||||
auto arguments = std::make_pair("", test.arguments);
|
||||
|
||||
// Transform the provided arguments into raw arguments to prevent them from being quoted when the generator runs
|
||||
std::vector<RawArg> raw_arguments{};
|
||||
for (const auto &argument : test.arguments)
|
||||
raw_arguments.emplace_back(argument);
|
||||
auto arguments = std::make_pair("", raw_arguments);
|
||||
ConditionScope cs(gen, test.condition);
|
||||
cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
|
||||
}
|
||||
@@ -1347,10 +1549,19 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the generated CMakeLists.txt output from the stringstream buffer
|
||||
auto generated_cmake = ss.str();
|
||||
|
||||
// Make sure the file ends in a single newline
|
||||
while (!generated_cmake.empty() && std::isspace(generated_cmake.back())) {
|
||||
generated_cmake.pop_back();
|
||||
}
|
||||
generated_cmake += '\n';
|
||||
|
||||
// Generate CMakeLists.txt
|
||||
auto list_path = fs::path(path) / "CMakeLists.txt";
|
||||
|
||||
auto should_regenerate = [&list_path, &ss]() {
|
||||
auto should_regenerate = [&list_path, &generated_cmake]() {
|
||||
if (!fs::exists(list_path))
|
||||
return true;
|
||||
|
||||
@@ -1360,11 +1571,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
std::string data((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
return data != ss.str();
|
||||
return data != generated_cmake;
|
||||
}();
|
||||
|
||||
if (should_regenerate) {
|
||||
create_file(list_path, ss.str());
|
||||
create_file(list_path, generated_cmake);
|
||||
}
|
||||
|
||||
auto generate_subdir = [path, &project](const fs::path &sub) {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#include "error.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
Status::Status(Code ec) noexcept : ec_(ec) {
|
||||
}
|
||||
|
||||
Status::operator int() const noexcept {
|
||||
return static_cast<int>(ec_);
|
||||
}
|
||||
|
||||
Status::Code Status::code() const noexcept {
|
||||
return ec_;
|
||||
}
|
||||
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
// strings for cmkr::error::Status::Code
|
||||
static const char *err_string[] = {
|
||||
"Success", "Runtime error", "Initialization error", "CMake generation error", "Build error", "Clean error", "Install error",
|
||||
};
|
||||
|
||||
const char *cmkr_error_status(int i) {
|
||||
assert(i >= 0 && static_cast<size_t>(i) < (sizeof(err_string) / sizeof(*(err_string))));
|
||||
return err_string[i];
|
||||
}
|
||||
@@ -23,11 +23,3 @@ arguments:
|
||||
}
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_help_version(void) {
|
||||
return cmkr::help::version();
|
||||
}
|
||||
|
||||
const char *cmkr_help_message(void) {
|
||||
return cmkr::help::message();
|
||||
}
|
||||
+148
-18
@@ -36,7 +36,7 @@ static std::string format_key_message(const std::string &message, const toml::ke
|
||||
auto loc = value.location();
|
||||
auto line_number_str = std::to_string(loc.line());
|
||||
auto line_width = line_number_str.length();
|
||||
auto line_str = loc.line_str();
|
||||
const auto &line_str = loc.line_str();
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << message << "\n";
|
||||
@@ -73,7 +73,7 @@ class TomlChecker {
|
||||
public:
|
||||
TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {
|
||||
}
|
||||
TomlChecker(const TomlBasicValue &v) : m_v(v) {
|
||||
explicit TomlChecker(const TomlBasicValue &v) : m_v(v) {
|
||||
}
|
||||
TomlChecker(const TomlChecker &) = delete;
|
||||
TomlChecker(TomlChecker &&) = delete;
|
||||
@@ -140,7 +140,7 @@ class TomlChecker {
|
||||
for (const auto &itr : m_v.as_table()) {
|
||||
const auto &ky = itr.first;
|
||||
if (m_conditionVisited.contains(ky)) {
|
||||
if (!conditions.contains(ky)) {
|
||||
if (!conditions.contains(ky) && Project::is_condition_name(ky)) {
|
||||
throw_key_error("Unknown condition '" + ky + "'", ky, itr.second);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ class TomlChecker {
|
||||
throw_key_error("Unknown key '" + ky + "'", ky, itr.second);
|
||||
} else if (ky == "condition") {
|
||||
std::string condition = itr.second.as_string();
|
||||
if (!conditions.contains(condition)) {
|
||||
if (!conditions.contains(condition) && Project::is_condition_name(condition)) {
|
||||
throw_key_error("Unknown condition '" + condition + "'", condition, itr.second);
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,7 @@ class TomlCheckerRoot {
|
||||
bool m_checked = false;
|
||||
|
||||
public:
|
||||
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
|
||||
explicit TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
|
||||
}
|
||||
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
|
||||
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
|
||||
@@ -265,12 +265,22 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake";
|
||||
conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake";
|
||||
conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake";
|
||||
conditions["msvc"] = R"cmake(MSVC)cmake";
|
||||
conditions["clang"] =
|
||||
R"cmake((CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$") OR (CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$"))cmake";
|
||||
conditions["msvc"] = R"cmake(MSVC)cmake";
|
||||
conditions["clang-cl"] =
|
||||
R"cmake((CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$") OR (CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$"))cmake";
|
||||
conditions["clang-any"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake";
|
||||
conditions["root"] = R"cmake(CMKR_ROOT_PROJECT)cmake";
|
||||
conditions["x64"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 8)cmake";
|
||||
conditions["x32"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 4)cmake";
|
||||
conditions["android"] = R"cmake(ANDROID)cmake";
|
||||
conditions["apple"] = R"cmake(APPLE)cmake";
|
||||
conditions["bsd"] = R"cmake(BSD)cmake";
|
||||
conditions["cygwin"] = R"cmake(CYGWIN)cmake";
|
||||
conditions["ios"] = R"cmake(IOS)cmake";
|
||||
conditions["xcode"] = R"cmake(XCODE)cmake";
|
||||
conditions["wince"] = R"cmake(WINCE)cmake";
|
||||
} else {
|
||||
conditions = parent->conditions;
|
||||
templates = parent->templates;
|
||||
@@ -279,6 +289,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
if (checker.contains("conditions")) {
|
||||
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
|
||||
for (const auto &cond : conds) {
|
||||
if (!is_condition_name(cond.first)) {
|
||||
throw_key_error("Invalid condition name '" + cond.first + "'", cond.first, toml::find(toml::find(toml, "conditions"), cond.first));
|
||||
}
|
||||
conditions[cond.first] = cond.second;
|
||||
}
|
||||
}
|
||||
@@ -369,6 +382,25 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
}
|
||||
|
||||
if (checker.contains("options")) {
|
||||
auto normalize = [](const std::string &name) {
|
||||
std::string normalized;
|
||||
for (char ch : name) {
|
||||
if (ch == '_') {
|
||||
normalized += '-';
|
||||
} else if (ch >= 'A' && ch <= 'Z') {
|
||||
ch += ('a' - 'A');
|
||||
normalized += ch;
|
||||
} else if (ch == '-' || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')) {
|
||||
normalized += ch;
|
||||
} else {
|
||||
// Ignore all other characters
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
};
|
||||
auto nproject_prefix = normalize(project_name);
|
||||
nproject_prefix += '-';
|
||||
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto &itr : opts) {
|
||||
@@ -406,7 +438,21 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
throw_key_error(toml::concat_to_string("Unsupported value type: ", itr.second.type()), itr.first, itr.second);
|
||||
}
|
||||
options.push_back(o);
|
||||
|
||||
// Add a condition matching the option name
|
||||
conditions.emplace(o.name, o.name);
|
||||
|
||||
// Add an implicit condition for the option
|
||||
auto ncondition = normalize(o.name);
|
||||
if (ncondition.find(nproject_prefix) == 0) {
|
||||
ncondition = ncondition.substr(nproject_prefix.size());
|
||||
}
|
||||
if (!ncondition.empty()) {
|
||||
if (conditions.contains(ncondition)) {
|
||||
print_key_warning("Option '" + o.name + "' would create a condition '" + ncondition + "' that already exists", o.name, value);
|
||||
}
|
||||
conditions.emplace(ncondition, o.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,6 +489,15 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
c.optional("cmake-after", content.cmake_after);
|
||||
c.optional("include-before", content.include_before);
|
||||
c.optional("include-after", content.include_after);
|
||||
c.optional("system", content.system);
|
||||
|
||||
// Check if the minimum version requirement is satisfied (CMake 3.25)
|
||||
if (c.contains("system") && !this->cmake_minimum_version(3, 25)) {
|
||||
throw_key_error("The system argument is only supported on CMake version 3.25 and above.\nSet the CMake version in cmake.toml:\n"
|
||||
"[cmake]\n"
|
||||
"version = \"3.25\"\n",
|
||||
"system", "");
|
||||
}
|
||||
|
||||
for (const auto &argItr : itr.second.as_table()) {
|
||||
std::string value;
|
||||
@@ -488,13 +543,26 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
key = "URL";
|
||||
} else if (hash_algorithms.contains(key)) {
|
||||
std::string algo;
|
||||
for (auto c : key) {
|
||||
algo.push_back(std::toupper(c));
|
||||
for (auto ch : key) {
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
ch -= ('a' - 'A');
|
||||
}
|
||||
algo.push_back(ch);
|
||||
}
|
||||
key = "URL_HASH";
|
||||
if (value.empty()) {
|
||||
throw_key_error("Empty hash value", argItr.first, argItr.second);
|
||||
}
|
||||
for (char c : value) {
|
||||
if (!std::isxdigit(c)) {
|
||||
throw_key_error("Hash value must be a hex string", argItr.first, argItr.second);
|
||||
}
|
||||
}
|
||||
value = algo + "=" + value;
|
||||
} else if (key == "hash") {
|
||||
key = "URL_HASH";
|
||||
} else if (key == "subdir") {
|
||||
key = "SOURCE_SUBDIR";
|
||||
} else if (is_cmake_arg(key)) {
|
||||
// allow passthrough of ExternalProject options
|
||||
} else if (!c.visisted(key)) {
|
||||
@@ -585,6 +653,37 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
t.optional("link-libraries", target.link_libraries);
|
||||
t.optional("private-link-libraries", target.private_link_libraries);
|
||||
|
||||
// Add support for relative paths for (private-)link-libraries
|
||||
const auto fix_relative_paths = [&name, &path](ConditionVector &libraries, const char *key) {
|
||||
for (const auto &library_entries : libraries) {
|
||||
for (auto &library : libraries[library_entries.first]) {
|
||||
// Skip processing paths with potential CMake macros in them (this check isn't perfect)
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references
|
||||
if ((library.find("${") != std::string::npos || library.find("$ENV{") != std::string::npos ||
|
||||
library.find("$CACHE{") != std::string::npos) &&
|
||||
library.find('}') != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto library_path = fs::path(path) / library;
|
||||
if (fs::exists(library_path)) {
|
||||
if (!fs::is_directory(library_path)) {
|
||||
// If the file path is relative (and not a directory), prepend ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
library.insert(0, "${CMAKE_CURRENT_SOURCE_DIR}/");
|
||||
}
|
||||
} else if (library.find_first_of(R"(\/)") != std::string::npos) {
|
||||
// Error if the path contains a directory separator and the file doesn't exist
|
||||
throw std::runtime_error("Attempted to link against a library file that doesn't exist for target \"" + name + "\" in \"" +
|
||||
key + "\": " + library);
|
||||
} else {
|
||||
// NOTE: We cannot check if system libraries exist, so we leave them as-is
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
fix_relative_paths(target.link_libraries, "link-libraries");
|
||||
fix_relative_paths(target.private_link_libraries, "private-link-libraries");
|
||||
|
||||
t.optional("link-options", target.link_options);
|
||||
t.optional("private-link-options", target.private_link_options);
|
||||
|
||||
@@ -593,28 +692,28 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
|
||||
Condition<std::string> msvc_runtime;
|
||||
t.optional("msvc-runtime", msvc_runtime);
|
||||
for (const auto &condItr : msvc_runtime) {
|
||||
switch (parse_msvcRuntimeType(condItr.second)) {
|
||||
for (const auto &cond_itr : msvc_runtime) {
|
||||
switch (parse_msvcRuntimeType(cond_itr.second)) {
|
||||
case msvc_dynamic:
|
||||
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
|
||||
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
|
||||
break;
|
||||
case msvc_static:
|
||||
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
|
||||
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
|
||||
break;
|
||||
default: {
|
||||
std::string error = "Unknown runtime '" + condItr.second + "'\n";
|
||||
std::string error = "Unknown runtime '" + cond_itr.second + "'\n";
|
||||
error += "Available types:\n";
|
||||
for (std::string type_name : msvcRuntimeTypeNames) {
|
||||
error += " - " + type_name + "\n";
|
||||
}
|
||||
error.pop_back(); // Remove last newline
|
||||
const TomlBasicValue *report;
|
||||
if (condItr.first.empty()) {
|
||||
if (cond_itr.first.empty()) {
|
||||
report = &t.find("msvc-runtime");
|
||||
} else {
|
||||
report = &t.find(condItr.first).as_table().find("msvc-runtime").value();
|
||||
report = &t.find(cond_itr.first).as_table().find("msvc-runtime").value();
|
||||
}
|
||||
throw_key_error(error, condItr.second, *report);
|
||||
throw_key_error(error, cond_itr.second, *report);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,6 +732,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
property_list += list_val.as_string();
|
||||
}
|
||||
target.properties[condition][k] = property_list;
|
||||
} else if (v.is_integer()) {
|
||||
target.properties[condition][k] = std::to_string(v.as_integer());
|
||||
} else if (v.is_boolean()) {
|
||||
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
|
||||
} else {
|
||||
@@ -684,6 +785,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
tmplate.outline = parse_target(name, t, true);
|
||||
|
||||
t.optional("add-function", tmplate.add_function);
|
||||
t.optional("add-arguments", tmplate.add_arguments);
|
||||
t.optional("pass-sources-to-add-function", tmplate.pass_sources_to_add_function);
|
||||
t.optional("pass-sources", tmplate.pass_sources_to_add_function);
|
||||
|
||||
@@ -749,13 +851,33 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
std::istringstream feature_stream{features};
|
||||
std::string feature;
|
||||
while (std::getline(feature_stream, feature, ',')) {
|
||||
package.features.emplace_back(feature);
|
||||
// Disable default features with package-name[core,feature1]
|
||||
if (feature == "core") {
|
||||
package.default_features = false;
|
||||
} else {
|
||||
package.features.emplace_back(feature);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw_key_error("Invalid package name '" + package_str + "'", "packages", p);
|
||||
}
|
||||
vcpkg.packages.emplace_back(std::move(package));
|
||||
}
|
||||
|
||||
if (v.contains("overlay")) {
|
||||
std::string overlay;
|
||||
v.optional("overlay", overlay);
|
||||
vcpkg.overlay_triplets = vcpkg.overlay_ports = {overlay};
|
||||
if (v.contains("overlay-ports")) {
|
||||
throw_key_error("[vcpkg].overlay was already specified", "overlay-ports", v.find("overlay-ports"));
|
||||
}
|
||||
if (v.contains("overlay-triplets")) {
|
||||
throw_key_error("[vcpkg].overlay was already specified", "overlay-triplets", v.find("overlay-triplets"));
|
||||
}
|
||||
} else {
|
||||
v.optional("overlay-ports", vcpkg.overlay_ports);
|
||||
v.optional("overlay-triplets", vcpkg.overlay_triplets);
|
||||
}
|
||||
}
|
||||
|
||||
checker.check(conditions, true);
|
||||
@@ -771,7 +893,6 @@ const Project *Project::root() const {
|
||||
bool Project::cmake_minimum_version(int major, int minor) const {
|
||||
// NOTE: this code is like pulling teeth, sorry
|
||||
auto root_version = root()->cmake_version;
|
||||
puts(root_version.c_str());
|
||||
auto range_index = root_version.find("...");
|
||||
if (range_index != std::string::npos) {
|
||||
root_version.resize(range_index);
|
||||
@@ -788,6 +909,15 @@ bool Project::cmake_minimum_version(int major, int minor) const {
|
||||
return std::tie(root_major, root_minor) >= std::tie(major, minor);
|
||||
}
|
||||
|
||||
bool Project::is_condition_name(const std::string &name) {
|
||||
for (auto ch : name) {
|
||||
if (!std::isalnum(ch) && ch != '-' && ch != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_root_path(const std::string &path) {
|
||||
const auto toml_path = fs::path(path) / "cmake.toml";
|
||||
if (!fs::exists(toml_path)) {
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
# These will be generated by cmkr, so no point in tracking them
|
||||
**/CMakeLists.txt
|
||||
**/cmkr.cmake
|
||||
**/cmkr.cmake
|
||||
**/vcpkg.json
|
||||
Generated
+22
@@ -100,3 +100,25 @@ if(MSVC) # msvc
|
||||
)
|
||||
|
||||
endif()
|
||||
add_test(
|
||||
NAME
|
||||
compile-options
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/compile-options"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
if(WIN32) # windows
|
||||
add_test(
|
||||
NAME
|
||||
relative-paths
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/relative-paths"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -8,4 +8,4 @@ description = "Minimal example"
|
||||
type = "executable"
|
||||
sources = ["src/basic.cpp"]
|
||||
|
||||
# Declares an executable target called `basic` with `src/basic.cpp` as a source file. Equivalent to CMake's [add_executable](https://cmake.org/cmake/help/latest/command/add_executable.html)`(basic src/basic.cpp)`.
|
||||
# Declares an executable target called `basic` with `src/basic.cpp` as a source file. Equivalent to CMake's [add_executable](https://cmake.org/cmake/help/latest/command/add_executable.html)`(basic src/basic.cpp)`.
|
||||
|
||||
+14
-1
@@ -51,4 +51,17 @@ condition = "msvc"
|
||||
name = "msvc-runtime"
|
||||
working-directory = "msvc-runtime"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "compile-options"
|
||||
working-directory = "compile-options"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
condition = "windows"
|
||||
name = "relative-paths"
|
||||
working-directory = "relative-paths"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Example project that sets compiler flags and preprocessor defines for various platforms.
|
||||
|
||||
[project]
|
||||
name = "compile-options"
|
||||
description = "Compiler flags"
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc.compile-options = ["/W2"]
|
||||
msvc.compile-definitions = ["PLATFORM=\"msvc\""]
|
||||
gcc.compile-options = ["-Wall"]
|
||||
gcc.compile-definitions = ["PLATFORM=\"gcc\""]
|
||||
clang.compile-options = ["-Wall"]
|
||||
clang.compile-definitions = ["PLATFORM=\"clang\""]
|
||||
|
||||
# The `hello` target uses [conditions](/cmake-toml#conditions) to set different compiler flags and definitions depending on the platform. See the [targets](/cmake-toml/#targets) documentation for other things you can set.
|
||||
# _Note_: In general you only want to specify flags _required_ to compile your code without errors.
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr!");
|
||||
}
|
||||
+28
-23
@@ -1,23 +1,28 @@
|
||||
[project]
|
||||
name = "conditions"
|
||||
cmake-after = "set(CUSTOM ON)"
|
||||
|
||||
[conditions]
|
||||
custom = "CUSTOM"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
cmake-after = "message(STATUS cmake-after)"
|
||||
windows.cmake-after = "message(STATUS win32-after)"
|
||||
macos.cmake-after = "message(STATUS macos-after)"
|
||||
linux.cmake-after = "message(STATUS linux-after)"
|
||||
unix.cmake-after = "message(STATUS unix-after)"
|
||||
custom.cmake-after = "message(STATUS custom-after)"
|
||||
|
||||
[target.example.properties]
|
||||
AUTOMOC = false
|
||||
custom.OUTPUT_NAME = "example2"
|
||||
custom.AUTORCC = true
|
||||
AUTOGEN = "ON"
|
||||
[project]
|
||||
name = "conditions"
|
||||
cmake-after = "set(CUSTOM ON)"
|
||||
|
||||
[options]
|
||||
CONDITIONS_BUILD_TESTS = "root"
|
||||
|
||||
[conditions]
|
||||
custom = "CUSTOM"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
cmake-after = "message(STATUS cmake-after)"
|
||||
windows.cmake-after = "message(STATUS win32-after)"
|
||||
macos.cmake-after = "message(STATUS macos-after)"
|
||||
linux.cmake-after = "message(STATUS linux-after)"
|
||||
unix.cmake-after = "message(STATUS unix-after)"
|
||||
custom.cmake-after = "message(STATUS custom-after)"
|
||||
build-tests.cmake-after = "message(STATUS build-tests)"
|
||||
"CONDITIONS_BUILD_TESTS AND $<linux>".cmake-after = "message(STATUS linux-tests)"
|
||||
|
||||
[target.example.properties]
|
||||
AUTOMOC = false
|
||||
custom.OUTPUT_NAME = "example2"
|
||||
custom.AUTORCC = true
|
||||
AUTOGEN = "ON"
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
int main() { }
|
||||
int main() {
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <Windows.h>
|
||||
|
||||
void foo() { }
|
||||
#include <Windows.h>
|
||||
|
||||
void foo() {
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Require a C++11 compiler for the target `example`.
|
||||
|
||||
[project]
|
||||
name = "cxx-standard"
|
||||
description = "Changing C++ standard"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
# This is equivalent to CMake's [target_compile_features](https://cmake.org/cmake/help/latest/command/target_compile_features.html)`(example PRIVATE cxx_std_11)`. For more information on available C/C++ standards and features see [cmake-compile-features(7)](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html).
|
||||
# Require a C++11 compiler for the target `example`.
|
||||
|
||||
[project]
|
||||
name = "cxx-standard"
|
||||
description = "Changing C++ standard"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
# This is equivalent to CMake's [target_compile_features](https://cmake.org/cmake/help/latest/command/target_compile_features.html)`(example PRIVATE cxx_std_11)`. For more information on available C/C++ standards and features see [cmake-compile-features(7)](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html).
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
||||
int main()
|
||||
{
|
||||
auto tpl = std::make_tuple(1, 2);
|
||||
printf("Hello from C++11 %d\n", std::get<0>(tpl));
|
||||
}
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
||||
int main() {
|
||||
auto tpl = std::make_tuple(1, 2);
|
||||
printf("Hello from C++11 %d\n", std::get<0>(tpl));
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
name = "fetch-content"
|
||||
description = "Fetching from git"
|
||||
|
||||
[fetch-content]
|
||||
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
|
||||
[fetch-content.fmt]
|
||||
git = "https://github.com/fmtlib/fmt"
|
||||
tag = "7.1.3"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["fmt::fmt"]
|
||||
|
||||
# This is equivalent to calling CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html).
|
||||
# This is equivalent to calling CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html).
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int main() {
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -1,18 +1,18 @@
|
||||
[project]
|
||||
name = "globbing"
|
||||
description = "Globbing sources"
|
||||
|
||||
# Recursively glob in the mylib/ folder
|
||||
[target.mylib]
|
||||
type = "static"
|
||||
alias = "mylib::mylib"
|
||||
sources = ["mylib/**.hpp", "mylib/**.cpp"]
|
||||
include-directories = ["mylib/include"]
|
||||
|
||||
# Single-folder glob in example/src/
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["example/src/*.cpp"]
|
||||
link-libraries = ["mylib::mylib"]
|
||||
|
||||
# As you can see in the example above you can use `**.ext` to glob recursively and `*.ext` to glob non-recursively. This **does not** generate `file(GLOB ...)` commands, but instead globs when cmkr is run. Files are sorted to give deterministic results regardless of the platform used.
|
||||
[project]
|
||||
name = "globbing"
|
||||
description = "Globbing sources"
|
||||
|
||||
# Recursively glob in the mylib/ folder
|
||||
[target.mylib]
|
||||
type = "static"
|
||||
alias = "mylib::mylib"
|
||||
sources = ["mylib/**.hpp", "mylib/**.cpp"]
|
||||
include-directories = ["mylib/include"]
|
||||
|
||||
# Single-folder glob in example/src/
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["example/src/*.cpp"]
|
||||
link-libraries = ["mylib::mylib"]
|
||||
|
||||
# As you can see in the example above you can use `**.ext` to glob recursively and `*.ext` to glob non-recursively. This **does not** generate `file(GLOB ...)` commands, but instead globs when cmkr is run. Files are sorted to give deterministic results regardless of the platform used.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
int main() {
|
||||
std::cout << mylib::message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
#include <iostream>
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
int main() {
|
||||
std::cout << mylib::message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mylib
|
||||
{
|
||||
std::string message();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mylib {
|
||||
std::string message();
|
||||
} // namespace mylib
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
std::string mylib::message()
|
||||
{
|
||||
return "cmkr is awesome!";
|
||||
}
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
std::string mylib::message() {
|
||||
return "cmkr is awesome!";
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,13 +1,13 @@
|
||||
[project]
|
||||
name = "interface"
|
||||
description = "Header-only library"
|
||||
|
||||
[target.mylib]
|
||||
type = "interface"
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["mylib"]
|
||||
[project]
|
||||
name = "interface"
|
||||
description = "Header-only library"
|
||||
|
||||
[target.mylib]
|
||||
type = "interface"
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["mylib"]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace mylib
|
||||
{
|
||||
static const char* version() { return "v1.0"; }
|
||||
} // namespace mylib
|
||||
namespace mylib {
|
||||
static const char *version() {
|
||||
return "v1.0";
|
||||
}
|
||||
} // namespace mylib
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include "mylib/mylib.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("mylib version: %s\n", mylib::version());
|
||||
}
|
||||
#include <cstdio>
|
||||
|
||||
#include "mylib/mylib.hpp"
|
||||
|
||||
int main() {
|
||||
printf("mylib version: %s\n", mylib::version());
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ sources = ["src/main.cpp"]
|
||||
[target.dynamic-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc-runtime = "dynamic"
|
||||
msvc-runtime = "dynamic"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[project]
|
||||
name = "relative-paths"
|
||||
|
||||
[target.test-library]
|
||||
type = "static"
|
||||
sources = ["src/library-code.cpp"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.link-libraries = ["libs/test-library-x64-Release.lib"]
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
// Created by Anthony Printup on 9/18/2023.
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" void library_function() {
|
||||
std::puts("Hello from library_function!");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Created by Anthony Printup on 9/18/2023.
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef WIN32
|
||||
extern "C" void library_function();
|
||||
#endif
|
||||
int main() {
|
||||
puts("Hello from cmkr(relative-paths)!");
|
||||
#ifdef WIN32
|
||||
library_function();
|
||||
#endif
|
||||
}
|
||||
@@ -22,4 +22,4 @@ compile-definitions = ["APP_A"]
|
||||
type = "app"
|
||||
compile-definitions = ["APP_B"]
|
||||
|
||||
# **Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
|
||||
# **Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
|
||||
# Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://github.com/microsoft/vcpkg) and links an `example` target to it:
|
||||
|
||||
[project]
|
||||
name = "vcpkg"
|
||||
description = "Dependencies from vcpkg"
|
||||
|
||||
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
|
||||
# See https://vcpkg.io/en/packages.html for available packages
|
||||
# See https://vcpkg.io/en/packages or https://vcpkg.link for available packages
|
||||
[vcpkg]
|
||||
version = "2022.11.14"
|
||||
version = "2024.11.16"
|
||||
packages = ["fmt"]
|
||||
|
||||
[find-package]
|
||||
fmt = {}
|
||||
[find-package.fmt]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
@@ -19,4 +18,4 @@ sources = ["src/main.cpp"]
|
||||
link-libraries = ["fmt::fmt"]
|
||||
|
||||
# The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`.
|
||||
# To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
|
||||
# To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int main() {
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"$cmkr": "This file is automatically generated from cmake.toml - DO NOT EDIT",
|
||||
"$cmkr-url": "https://github.com/build-cpp/cmkr",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
"fmt"
|
||||
],
|
||||
"description": "Dependencies from vcpkg",
|
||||
"name": "vcpkg",
|
||||
"version-string": ""
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
# Disable clang-format in this folder
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
||||
-4
@@ -16,7 +16,3 @@ target_include_directories(toml11 INTERFACE toml11-3.6.0)
|
||||
# https://github.com/mpark/variant (BSL-1.0)
|
||||
add_library(mpark_variant INTERFACE)
|
||||
target_include_directories(mpark_variant INTERFACE variant-1.4.0/include)
|
||||
|
||||
# https://github.com/nlohmann/json (MIT)
|
||||
add_library(nlohmann_json INTERFACE)
|
||||
target_include_directories(nlohmann_json INTERFACE nlohmann-3.9.1/include)
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2021 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-25447
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user