Compare commits

...

235 Commits

Author SHA1 Message Date
Duncan Ogilvie 43c72160b5 Merge pull request #200 from de-ep/gitignore-CMKR_BUILD_DIR
Automatically create .gitignore in CMAKE_BINARY_DIR
2026-06-03 20:22:25 +02:00
deep ef3c1a288a Automatically create .gitignore in CMAKE_BINARY_DIR 2026-06-03 23:30:33 +08:00
Duncan Ogilvie 26acf23989 Merge pull request #199 from build-cpp/gitignore-update
Add CMakeUserPresets.json to generated .gitignore
2026-04-14 12:12:36 +02:00
Duncan Ogilvie 15886fc340 Add CMakeUserPresets.json to generated .gitignore 2026-04-14 12:09:54 +02:00
Duncan Ogilvie d5e34b0bcc Merge pull request #197 from ZehMatt/fix-option-prefix2
Fix a regression that no longer warns if condition is manually defined
2026-02-14 19:11:36 +01:00
ζeh Matt 916ae11cae Fix a regression that no longer warns if condition is manually defined 2026-02-14 18:14:39 +02:00
Duncan Ogilvie eaa361611a Merge pull request #196 from ZehMatt/fix-option-prefix
Fix warnings when options are not prefixed with the project name
2026-02-14 16:38:37 +01:00
ζeh Matt 2300612ba9 Fix warnings when options are not prefixed with the project name 2026-02-14 17:23:02 +02:00
Duncan Ogilvie 25f015bc83 Bump to 0.2.46 2026-02-01 16:39:51 +01:00
Duncan Ogilvie fc674b4c6b Merge pull request #195 from build-cpp/conditional-languages
Add support for conditional [project].languages
2026-02-01 16:39:16 +01:00
Duncan Ogilvie 433dbb0500 Add test for conditional Objective-C support
Closes #183
2026-02-01 16:37:23 +01:00
Duncan Ogilvie 4772a2b683 Add support for conditional [project].languages
Closes #184
2026-02-01 16:14:17 +01:00
Duncan Ogilvie fb4931df1f Merge pull request #193 from build-cpp/toml11-upgrade
Support TOML 1.1.0
2026-02-01 15:36:15 +01:00
Duncan Ogilvie e96cd30217 Merge pull request #194 from build-cpp/add-dependencies
Add support for [target].dependencies
2026-02-01 15:36:04 +01:00
Duncan Ogilvie 2db8d1ee1d Add support for [target].dependencies
Closes #177
2026-02-01 15:33:09 +01:00
Duncan Ogilvie 645737afd4 Merge pull request #191 from de-ep/fix-tilde-expansion
fix #186
2025-12-21 14:33:09 +01:00
deep 98c2e962c0 fix tilde expansion: add windows support 2025-12-21 14:44:32 +05:30
Duncan Ogilvie 438feccf0f Add support for toml 1.1.0 2025-12-20 15:11:36 +01:00
Duncan Ogilvie 80cc29b8e1 Upgrade toml11 to 3.8.1 2025-12-20 14:29:26 +01:00
Duncan Ogilvie 1a62034a8a Fix formatting 2025-12-20 13:49:34 +01:00
Duncan Ogilvie c12472cd8f Merge pull request #180 from d06i/fix
fix #172
2025-12-17 18:35:20 +01:00
deep 228553d43a fix #186 2025-12-10 19:23:24 +05:30
Duncan Ogilvie 8f4b376e42 Bump to 0.2.45 2025-08-26 17:16:10 +02:00
Duncan Ogilvie fcbf937c49 Merge pull request #189 from iamtimmy/main
template add-function arguments
2025-08-26 17:00:55 +02:00
iamtimmy e609c8a48f template add-function arguments
add arguments support to add-function functionality in templates
2025-08-26 16:46:32 +02:00
Duncan Ogilvie 735898dbac Update basics.md 2025-08-11 03:37:01 +02:00
Duncan Ogilvie f7126b0158 Update basics.md 2025-08-11 03:35:53 +02:00
Duncan Ogilvie b6702714eb Fix info 2025-08-11 03:31:57 +02:00
Duncan Ogilvie efa4f716c2 Add CMake phases to basics 2025-08-11 03:30:28 +02:00
Duncan Ogilvie 76b7b8e778 Clarify basics documentation page 2025-08-11 03:05:37 +02:00
Duncan Ogilvie 501b26ced5 Update fetch-content documentation 2025-06-25 01:50:29 +02:00
Duncan Ogilvie 5bc36275ad Merge pull request #185 from build-cpp/gh-actions-bump
Bump github actions
2025-05-30 01:16:56 +02:00
Duncan Ogilvie 35c837a181 Bump github actions 2025-05-30 00:24:22 +02:00
Duncan Ogilvie 710dfa4963 Merge pull request #182 from evanwporter/patch-1
bumped up upload version
2025-05-30 00:15:38 +02:00
Duncan Ogilvie c49ee6bafe Update README.md 2025-05-29 23:21:04 +02:00
Evan 368273bab2 bumped up upload version 2025-04-19 15:49:15 -07:00
d06i ffe08ae60c fix #172 2025-04-18 00:19:51 +03:00
Duncan Ogilvie b1249af365 Bump to 0.2.44 2025-01-19 15:12:34 +01:00
Duncan Ogilvie ea2ff31b5a Merge pull request #173 from build-cpp/fix-empty-properties
Make sure empty target properties are emitted correctly
2025-01-19 15:10:06 +01:00
Duncan Ogilvie a32eb66df4 Make sure empty target properties are emitted correctly 2025-01-19 14:53:50 +01:00
Duncan Ogilvie 9b85833240 Add link to example vcpkg repository 2024-12-06 18:19:35 +01:00
Duncan Ogilvie ccb3d0b9ef Bump to 0.2.43 2024-12-04 13:00:03 +01:00
Duncan Ogilvie 49cdab39bd Merge pull request #169 from build-cpp/namespace-targets
Support `::mylib` which that target that exists at configure-time
2024-12-04 12:55:57 +01:00
Duncan Ogilvie e318139314 Add documentation for ::mylib 2024-12-04 12:15:51 +01:00
Duncan Ogilvie 0f2a9a55fb Support link-libraries = ["::mylib"] which checks the if target exists
https://gitlab.kitware.com/cmake/cmake/-/issues/26497
2024-12-04 12:15:49 +01:00
Duncan Ogilvie 38cec2d5d1 Do not allow lambdas on a single line 2024-12-04 11:55:13 +01:00
Duncan Ogilvie 3caf593438 Bump to 0.2.42 2024-12-03 15:35:05 +01:00
Duncan Ogilvie c94dbd6416 Merge pull request #168 from build-cpp/target-directory-bug
Fix linking against a target that's also a directory
2024-12-03 13:41:16 +01:00
Duncan Ogilvie a1eaf601c6 Fix linking against a target that's also a directory
Closes #166
2024-12-03 13:39:32 +01:00
Duncan Ogilvie 10320c8e55 Bump to 0.2.41 2024-12-02 15:56:02 +01:00
Duncan Ogilvie 45e12c8d98 Merge pull request #165 from build-cpp/cmake-support
Fix vcpkg support for CMake 3.30
2024-12-02 15:55:43 +01:00
Duncan Ogilvie e12ebc060d Use FetchContent_MakeAvailable for vcpkg 2024-12-02 15:54:19 +01:00
Duncan Ogilvie c6e3fc159f Suppress deprecation warning for CMake <3.10 2024-12-02 15:50:22 +01:00
Duncan Ogilvie dc6f306d9c Bump to 0.2.40 2024-11-28 23:02:34 +01:00
Duncan Ogilvie e08bd5a311 Merge pull request #164 from build-cpp/vcpkg-overlay-bugfix
Use absolute paths for vcpkg overlays
2024-11-28 23:02:17 +01:00
Duncan Ogilvie 068977e2f5 Use absolute paths for vcpkg overlays 2024-11-28 23:01:14 +01:00
Duncan Ogilvie 73147747a4 Bump to 0.2.39 2024-11-28 21:26:27 +01:00
Duncan Ogilvie a64fa9cc34 Merge pull request #163 from build-cpp/vcpkg-fetch-folder
Move vcpkg download location directly in `${CMAKE_BINARY_DIR}/vcpkg`
2024-11-28 21:25:53 +01:00
Duncan Ogilvie eb6ccb39ee Move vcpkg download location directly in ${CMAKE_BINARY_DIR} 2024-11-28 21:24:03 +01:00
Duncan Ogilvie 91dd8ce51f Bump to 0.2.38 2024-11-28 14:40:22 +01:00
Duncan Ogilvie efbfca27bd Merge pull request #160 from build-cpp/vcpkg-triplets
Add support for `[vcpkg].overlay-triplets`
2024-11-28 14:38:46 +01:00
Duncan Ogilvie 78bcf2acf7 Add support for [vcpkg].overlay-triplets 2024-11-28 14:28:10 +01:00
Duncan Ogilvie 83559cc5d4 Improve target reference documentation 2024-11-28 14:28:08 +01:00
Duncan Ogilvie 5d85cc83e3 Ignore generated vcpkg.json 2024-11-28 13:44:11 +01:00
Duncan Ogilvie dea41d80b5 Bump to 0.2.37 2024-11-27 14:17:48 +01:00
Duncan Ogilvie b52cf5b241 Merge pull request #158 from build-cpp/vcpkg-default-features
Allow disabling default features with vcpkg
2024-11-27 14:16:25 +01:00
Duncan Ogilvie e9480a625f Allow disabling default features with vcpkg 2024-11-27 14:13:37 +01:00
Duncan Ogilvie 13a7a8788a Bump to 0.2.36 2024-11-26 13:36:24 +01:00
Duncan Ogilvie 45ca74099c Merge pull request #156 from build-cpp/vcpkg-overlay-ports
Add [vcpkg].overlay-ports feature
2024-11-26 13:35:32 +01:00
Duncan Ogilvie ae3d73896e Add [vcpkg].overlay-ports feature 2024-11-26 12:33:29 +01:00
Duncan Ogilvie 57042565d1 Merge pull request #154 from build-cpp/configure-depends
Use CMAKE_CONFIGURE_DEPENDS when supported
2024-11-03 11:44:02 +01:00
deep d02ee3ed24 Added check to make sure hash is valid hex (#153) 2024-11-03 10:49:33 +01:00
Duncan Ogilvie 4a15d2ba55 Use CMAKE_CONFIGURE_DEPENDS when supported 2024-11-03 10:33:32 +01:00
Duncan Ogilvie 87c0295f93 Fix CI 2024-10-14 13:02:20 +02:00
Duncan Ogilvie a608e6b778 Bump to 0.2.35 2024-10-13 16:16:36 +02:00
Duncan Ogilvie 523f5f4a8a Merge pull request #150 from build-cpp/msvc-runtime-root
Only emit `MSVC_RUNTIME_LIBRARY` in the root project
2024-09-06 14:39:47 +02:00
Duncan Ogilvie 510cf71af5 Only emit MSVC_RUNTIME_LIBRARY in the root project 2024-09-06 14:33:04 +02:00
Duncan Ogilvie 87d90138b4 Merge pull request #149 from build-cpp/relative-libs
Improve relative path functionality for link-libraries
2024-09-06 14:25:33 +02:00
Duncan Ogilvie 50615e0c27 Bump clang-format-lint-action
https://github.com/DoozyX/clang-format-lint-action/issues/73
2024-09-06 14:22:12 +02:00
Duncan Ogilvie 42ed3048b3 Improve relative path functionality for link-libraries 2024-09-06 13:50:43 +02:00
Duncan Ogilvie b0a1d79a42 Bump to 0.2.34 2024-08-05 15:28:58 +02:00
Duncan Ogilvie 5882bc082d Support integer properties
Closes #148
2024-08-05 15:28:38 +02:00
Duncan Ogilvie a90b3c6095 Add RC as a default language 2024-08-05 15:28:16 +02:00
Duncan Ogilvie 0a887c08f8 Bump to 0.2.33 2024-07-06 14:50:39 +02:00
Duncan Ogilvie 9f2cfe4083 Merge pull request #146 from build-cpp/improved-conditions
Improve error-prone condition handling
2024-07-06 14:49:32 +02:00
Duncan Ogilvie 15ad1dc6a7 Support nested condition syntax for named conditions 2024-07-06 14:43:32 +02:00
Duncan Ogilvie dabffeaed9 Add back the option name as a named condition 2024-07-06 14:43:32 +02:00
Duncan Ogilvie 59fe5c38b8 Consider PROJECT_OPTION to be a valid condition 2024-07-06 14:43:32 +02:00
Duncan Ogilvie dcdc7ac410 Add a few more default named conditions 2024-07-06 14:43:32 +02:00
Duncan Ogilvie 88377a8bfb Bump to 0.2.32 2024-07-06 14:03:55 +02:00
Duncan Ogilvie 3a9685be82 Merge pull request #145 from build-cpp/system-bug
Fix uninitialized 'system' field for fetch-content
2024-07-06 14:03:08 +02:00
Duncan Ogilvie 5576789c18 Fix uninitialized 'system' field for fetch-content 2024-07-06 14:02:33 +02:00
Duncan Ogilvie a362978f99 Bump to 0.2.31 2024-06-21 00:33:33 +02:00
Duncan Ogilvie a2f33297e4 Merge pull request #144 from build-cpp/msvc-runtime-subproject
Do not set CMAKE_MSVC_RUNTIME_LIBRARY if it's already set
2024-06-21 00:32:50 +02:00
Duncan Ogilvie 674c976647 Do not set CMAKE_MSVC_RUNTIME_LIBRARY if it's already set 2024-06-21 00:32:30 +02:00
Duncan Ogilvie 714a666e88 Merge pull request #143 from build-cpp/project-improvements
Project improvements
2024-06-01 01:25:26 +02:00
Duncan Ogilvie d613e433d5 Skip linting on pull requests for the same repository 2024-06-01 00:55:31 +02:00
Duncan Ogilvie 7c828d8740 Add custom targets for running cmkr/tests 2024-06-01 00:55:31 +02:00
Duncan Ogilvie 559f750c89 Fix undefined behavior with std::tolower/toupper 2024-06-01 00:55:29 +02:00
Duncan Ogilvie 4b6b72874e Remove nlohmann_json 2024-05-31 22:45:27 +02:00
Duncan Ogilvie 6bffa7cc71 Bump to 0.2.30 2024-05-31 22:36:39 +02:00
Duncan Ogilvie f415b432ed Also run linting on pull requests 2024-05-31 22:34:27 +02:00
Duncan Ogilvie 6c46664336 Merge pull request #142 from anthonyprintup/globbing-extension-fix
Fixed globbing when multiple extensions are present in the file name
2024-05-31 22:31:18 +02:00
Duncan Ogilvie f32aac63aa Review improvements 2024-05-31 22:24:01 +02:00
Anthony Printup 33d4cc4156 Fixed globbing when multiple extensions are present in the file name 2024-05-30 00:36:14 +02:00
Duncan Ogilvie 9288c8a87d Bump to 0.2.29 2024-04-15 03:08:33 +02:00
Duncan Ogilvie e36eee4420 Merge pull request #140 from build-cpp/improved-conditions
Allow arbitrary CMake expressions as conditions
2024-04-15 03:08:02 +02:00
Duncan Ogilvie 53820c9f65 Support $<condition> in unnamed conditions 2024-04-15 03:01:51 +02:00
Duncan Ogilvie ff7e4b8f23 Allow arbitrary CMake expressions as conditions 2024-04-15 02:12:02 +02:00
Duncan Ogilvie 1072cb44e2 Bump to 0.2.28 2024-04-14 00:08:11 +02:00
Duncan Ogilvie 926fc15fa2 Merge pull request #139 from build-cpp/vcpkg-bump
Bump to the latest vcpkg
2024-04-14 00:06:00 +02:00
Duncan Ogilvie e2c929adb0 Fix out-of-the-box vcpkg experience on macos 2024-04-13 23:57:30 +02:00
Duncan Ogilvie 0dec7ca4d5 Bump to the latest vcpkg 2024-04-13 23:21:25 +02:00
Duncan Ogilvie 45e346b4a3 Bump to 0.2.27 2024-03-28 11:38:01 +01:00
Duncan Ogilvie 7165986190 Merge pull request #138 from build-cpp/find-package-first
Generate find-package before fetch-content
2024-03-28 11:37:31 +01:00
Duncan Ogilvie be0ef6d615 Generate find-package before fetch-content 2024-03-28 11:36:15 +01:00
Duncan Ogilvie d607b9028c Bump to 0.2.26 2023-11-09 13:32:05 +01:00
Duncan Ogilvie 099b14552c Merge pull request #131 from build-cpp/fetch-subdir
Add [fetch-content].subdir
2023-11-09 13:31:48 +01:00
Duncan Ogilvie 771c80a41a Add [fetch-content].subdir
Closes #98
2023-11-09 13:28:29 +01:00
Duncan Ogilvie 58c7de1d60 Merge pull request #129 from build-cpp/gitignore-improvement
Improve the default .gitignore that's generated
2023-10-18 13:22:44 +02:00
Duncan Ogilvie a61e4bb999 Merge pull request #128 from build-cpp/language-documentation
Document the supported languages
2023-10-18 13:22:32 +02:00
Duncan Ogilvie 47aaeaf8ce Document the supported languages 2023-10-18 12:27:04 +02:00
Duncan Ogilvie 62609a0088 Improve the default .gitignore that's generated 2023-10-18 12:26:38 +02:00
Duncan Ogilvie e0d8a085db Merge pull request #121 from anthonyprintup/relative-paths
Added support for relative paths in `link-libraries`
2023-09-22 16:13:03 +02:00
Duncan Ogilvie 56da4144ae Merge pull request #125 from anthonyprintup/fix-124
fix(generator): Prevent calling `std::string::back` on an empty string
2023-09-22 16:12:20 +02:00
Duncan Ogilvie 9388b3f29d Merge pull request #123 from anthonyprintup/quotes-fix
Prevent wrapping test command arguments in quotes
2023-09-22 16:12:07 +02:00
Anthony Printup 079644cfeb fix(generator): Prevent calling std::string::back on an empty string
Fixes #124
2023-09-20 02:34:02 +02:00
Anthony Printup a7ca9f04c0 fix(relative-paths): Replaced has_parent_path with a simpler path check 2023-09-20 00:35:41 +02:00
Anthony Printup 339c2aac94 fix(generator): Prevent command arguments from being quoted when generating tests
Fixes #122
2023-09-18 18:36:58 +02:00
Anthony Printup c573c9a76d test(relative-paths): Add the test-library file to libs 2023-09-18 17:49:21 +02:00
Anthony Printup b24a0a2fdc test(relative-paths): Make link-libraries conditional to the Windows platform 2023-09-18 17:49:20 +02:00
Anthony Printup 854e8817c4 test(relative-paths): Remove inlined CMake from cmake.toml 2023-09-18 17:49:11 +02:00
Anthony Printup 1c2947b2fc fix(relative-paths): Prevent processing paths which may contain CMake macros, added a check to see if the library that's being linked exists on disk 2023-09-18 17:35:46 +02:00
Anthony Printup 19e61aa1aa test(relative-paths): Attempt to fix the tests workflow 2023-09-18 16:41:37 +02:00
Anthony Printup bfa1e1fe0d test(relative-paths): Added the latest relative-paths test to the tests workflow 2023-09-18 15:49:17 +02:00
Anthony Printup 56bd78f7ad feat(project-parser): Added support for relative paths in link-libraries
Fixes #116
2023-09-18 15:44:44 +02:00
Anthony Printup 4362b3547f test(relative-paths): Added tests for #116 2023-09-18 15:43:46 +02:00
Duncan Ogilvie 08204d9847 Bump to 0.2.25 2023-09-16 18:51:26 +02:00
Duncan Ogilvie 2202aa8173 Merge pull request #120 from anthonyprintup/new-lines-fix
Trim additional newline characters at the end of CMakeLists.txt
2023-09-16 18:49:45 +02:00
Anthony Printup d3829fb6cd Trim additional new lines at the end of the generated CMakeLists.txt 2023-09-16 18:49:30 +02:00
Duncan Ogilvie 3fb095210a Merge pull request #118 from ZehMatt/vcpkg-errors
Report better error messages for vcpkg projects and features
2023-08-25 22:21:52 +02:00
ζeh Matt 9f99365a25 Report better error messages for vcpkg projects and features 2023-08-25 15:58:24 +03:00
Duncan Ogilvie be387141b5 Add a 'Compiler flags' example to the documentation 2023-07-09 19:26:07 +02:00
Duncan Ogilvie 09ded38f70 Merge pull request #115 from build-cpp/clang-cl-condition
Add `clang-cl` and `clang-any` conditions
2023-07-09 18:01:06 +02:00
Duncan Ogilvie ad90b1ad3e Add 'clang-cl' and 'clang-any' conditions 2023-07-09 16:20:05 +02:00
Duncan Ogilvie 5bfa03d311 Bump to 0.2.24 2023-06-12 19:12:47 +02:00
Duncan Ogilvie bdd9d393d6 Merge pull request #113 from build-cpp/editorconfig
Add `.editorconfig` and linting
2023-06-12 18:28:12 +02:00
Duncan Ogilvie 1deaec2b41 Add .editorconfig and linting
Closes #112
2023-06-12 18:26:13 +02:00
Duncan Ogilvie cfee3bbc14 Merge pull request #111 from anthonyprintup/fetch-content-system
Fix #110
2023-06-12 17:48:54 +02:00
Anthony Printup bcaa60cabe feat: Added SYSTEM flag support to fetch-content 2023-06-12 15:41:20 +02:00
Duncan Ogilvie 7b1d33395e Bump to 0.2.23 2023-03-25 11:25:18 +00:00
Duncan Ogilvie 240fafc102 Merge pull request #101 from build-cpp/minor-cleanup
Cleanup
2023-03-25 11:24:44 +00:00
Duncan Ogilvie 7a7ddf2699 Speed up the pipeline by using Ninja 2023-03-25 11:19:48 +00:00
Duncan Ogilvie 605a04e72d Fix formatting of files 2023-03-24 11:07:02 +00:00
Duncan Ogilvie fd7f078127 Add workflow for checking clang-format 2023-03-24 11:04:58 +00:00
Duncan Ogilvie ee0e0d71d9 Remove a bunch of dead code 2023-03-24 11:03:45 +00:00
Duncan Ogilvie 9d6897b572 Suppress warnings related to DOWNLOAD_EXTRACT_TIMESTAMP 2023-03-24 11:03:40 +00:00
Duncan Ogilvie f8c34e5545 Error when [vcpkg] is used from a non-root project 2023-03-24 10:43:17 +00:00
Duncan Ogilvie e6d783ea20 Fix a few clang-tidy warnings 2023-03-24 10:41:59 +00:00
Duncan Ogilvie ee7fe38a7b Merge pull request #100 from build-cpp/include-subdir-bugfix
Fix a bug where includes in a subdir would not work
2023-03-24 10:13:30 +00:00
Duncan Ogilvie 66b2aad843 Fix a bug where includes in a subdir would not work 2023-03-24 10:04:32 +00:00
Duncan Ogilvie d2e376080a Bump to 0.2.22 2023-03-09 21:36:11 +01:00
Duncan Ogilvie 634908c651 Merge pull request #97 from build-cpp/cmkr-init-regression
Fix a regression where cmkr init wouldn't create any source files
2023-03-09 21:35:25 +01:00
Duncan Ogilvie 33911f3f1b Fix a regression where cmkr init wouldn't create any source files 2023-03-09 21:35:11 +01:00
Duncan Ogilvie e650073bef Bump to 0.2.21 2023-03-08 09:48:40 +01:00
Duncan Ogilvie 96e44f7771 Merge pull request #96 from build-cpp/check-sources
Improve error messages and check if specified sources exist
2023-03-08 09:47:43 +01:00
Duncan Ogilvie cb9ad5cdb3 Improve error messages and check if specified sources exist 2023-03-08 09:41:40 +01:00
Duncan Ogilvie c785b540d3 Document target templates 2023-03-08 09:01:39 +01:00
Duncan Ogilvie 275b73eefa Remove debug print 2023-03-02 08:43:45 +01:00
Duncan Ogilvie ea49a3acb7 Bump to 0.2.20 2023-02-28 21:31:18 +01:00
Duncan Ogilvie 257e14c869 Merge pull request #93 from build-cpp/gitattributes
Automatically generate .gitattributes and .gitignore with cmkr init
2023-02-28 21:30:34 +01:00
Duncan Ogilvie 7508816fe7 Merge pull request #94 from build-cpp/user-experience
Add a special "root" value for `[options]`
2023-02-28 21:27:11 +01:00
Duncan Ogilvie 45678bc5bc Automatically generate .gitattributes and .gitignore with cmkr init 2023-02-28 21:26:07 +01:00
Duncan Ogilvie b869e5646f Add a special "root" value for [options] 2023-02-28 19:34:31 +01:00
Duncan Ogilvie a499df84b1 Bump to 0.2.19 2023-02-28 17:11:55 +01:00
Duncan Ogilvie 49440f99e0 Merge pull request #92 from build-cpp/improved-options
Improve and document the `[options]` feature
2023-02-20 02:13:50 +01:00
Duncan Ogilvie 0c19c3da0c Improve the code related to [options] 2023-02-20 02:09:36 +01:00
Duncan Ogilvie 233cadadd0 Merge pull request #90 from mike1k/sanity
Error on unknown language & unacceptable source file.
2023-02-20 01:31:47 +01:00
Duncan Ogilvie 58a5a935e5 Add a bunch of missing languages and extensions and refactor 2023-02-20 01:18:48 +01:00
Duncan Ogilvie e69427bf94 Merge pull request #91 from build-cpp/clang-condition
Fix the `clang` condition and CI improvements
2023-02-19 22:30:15 +01:00
Duncan Ogilvie bc6359805d Change the clang condition to not detect clang-cl
References:
- https://github.com/cursey/safetyhook/issues/18
- https://discourse.cmake.org/t/clang-on-windows-detected-with-msvc-interface/3470
2023-02-19 22:23:34 +01:00
Duncan Ogilvie 8dcc11e349 Add helper function Project::cmake_minimum_version for version-dependent features 2023-02-19 22:19:12 +01:00
Duncan Ogilvie 51dc49e6f1 Improve CI performance for non-tagged builds 2023-02-19 20:06:58 +01:00
mike 7d27c28b59 Fixed failing tests due to implicit project languages (internal list empty) 2023-02-09 10:53:48 -06:00
Duncan Ogilvie ae6bea3b58 Improve reference documentation 2023-01-28 11:57:22 +01:00
Duncan Ogilvie 5a0eda7abc Automatically generate release notes 2023-01-15 13:34:36 +01:00
Duncan Ogilvie f17923c18b Bump to 0.2.18 2023-01-14 21:28:17 +01:00
Duncan Ogilvie ad17890e23 Merge pull request #85 from build-cpp/settings-rename
Rename `[settings]` to `[variables]`
2023-01-14 21:14:12 +01:00
Duncan Ogilvie c0b98b3ef4 Add documentation for the [variables] section 2023-01-14 18:12:57 +01:00
Duncan Ogilvie 2c6fc569af Preserve backwards compatibility 2023-01-14 16:17:06 +01:00
Duncan Ogilvie 12ee23a44c Improve error messages 2023-01-14 15:52:19 +01:00
Duncan Ogilvie 82342a6b6f Rename [settings] to [variables] 2023-01-14 15:51:57 +01:00
Duncan Ogilvie 1d95fab0ce Switch to giscus for documentation comments 2023-01-02 14:56:38 +01:00
Duncan Ogilvie 44a77329bf Merge pull request #83 from build-cpp/document-basics
Document basics
2023-01-02 12:43:24 +01:00
Duncan Ogilvie e68c5fccdc Add utteranc.es comments 2023-01-02 12:43:20 +01:00
Duncan Ogilvie e2722d2c09 Initial draft of new documentation 2023-01-02 12:02:24 +01:00
Duncan Ogilvie 2627864383 Merge pull request #78 from build-cpp/fix-tests
Successfully fail when running cmkr subcommands
2022-11-29 10:11:45 +01:00
Duncan Ogilvie 05e21f734a Fix the tests 2022-11-29 10:06:34 +01:00
Duncan Ogilvie 1fd18503cd Successfully fail when running cmkr subcommands 2022-11-24 00:32:31 +01:00
Duncan Ogilvie 8b19441d34 Bump to 0.2.17 2022-11-24 00:31:42 +01:00
Duncan Ogilvie 2437eb90fe Minor improvements
- Drastically improve quality of generated CMake
- Fix a regression with recursive globbing in the root
- Automatically include CSharpUtilities when using CSharp
2022-11-24 00:23:30 +01:00
Duncan Ogilvie 6e4006ec07 Fix typo in the docs 2022-10-25 00:59:58 +02:00
Duncan Ogilvie 697b638723 Fix warning on macos 2022-10-11 14:19:29 +02:00
Duncan Ogilvie 109c4bc99f Bump to 0.2.16 2022-10-08 10:48:16 +02:00
Duncan Ogilvie cad85f0bb1 Merge pull request #70 from build-cpp/fix-msvc-runtime
Actually generate the set(CMAKE_MSVC_RUNTIME_LIBRARY)
2022-10-08 10:47:56 +02:00
Duncan Ogilvie 53c88ceafa Actually generate the set(CMAKE_MSVC_RUNTIME_LIBRARY)
Closes #69 (nice)
2022-10-08 10:37:27 +02:00
Duncan Ogilvie d2aa15b1c7 Bump to 0.2.15 2022-10-07 22:21:07 +02:00
Duncan Ogilvie e6ded077cf Merge pull request #65 from gmh5225/feature-msvc-static
[feature] msvc-static
2022-10-07 22:20:36 +02:00
Duncan Ogilvie 3615ccab94 Document msvc-runtime feature 2022-10-07 22:15:32 +02:00
Duncan Ogilvie 73622aa5ba Remove the add_test hook to generate tests the same on all platforms 2022-10-07 22:09:54 +02:00
Duncan Ogilvie 69d844a152 Refactor msvc-static to msvc-runtime 2022-10-07 17:46:36 +02:00
gmh5225 af9a117f50 Fix merge 2022-10-07 06:50:26 +08:00
gmh5225 5fe8220728 Merge branch 'feature-msvc-static' of https://github.com/gmh5225/cmkr into feature-msvc-static 2022-10-07 06:41:43 +08:00
gmh5225 28f541cd3c [feature] add CMP0091 2022-10-07 06:41:11 +08:00
gmh5225 2f3fd7b95e Merge branch 'main' into feature-msvc-static 2022-10-07 05:35:11 +08:00
Duncan Ogilvie 07d99c582d Merge pull request #68 from build-cpp/headers-crash
Fix the way target.headers is merged into target.sources
2022-10-06 18:30:13 +02:00
Duncan Ogilvie 5af2385cc3 Fix the way target.headers is merged into target.sources
- It would crash if you did not specify target.sources
- Headers with condition would be merged incorrectly
2022-10-06 18:22:51 +02:00
gmh5225 981c48dbc5 [feature] msvc-static 2022-10-02 10:43:32 +08:00
Duncan Ogilvie 153bdbe591 Bump to 0.2.14 2022-07-19 14:17:47 +02:00
Duncan Ogilvie 97f1c5c1cc Fix a bug when there are regex characters in the path
Closes #61
2022-07-19 14:05:47 +02:00
Duncan Ogilvie 5b6d9c3826 Replace illegal characters in the project name with _ when doing cmkr init 2022-07-19 13:42:56 +02:00
Duncan Ogilvie a90988b81a Update the formatting to now allow functions on a single line
This makes it less annoying when the IDE is auto-formatting while you type
2022-07-19 13:41:43 +02:00
Duncan Ogilvie 3a1298f4e8 Merge pull request #60 from pmeerw/fix-warning
error: fix warning, signedness of comparison
2022-06-28 19:47:39 +02:00
Peter Meerwald-Stadler 534f955827 error: fix warning, signedness of comparison
cmkr/src/error.cpp:23:24: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
   23 |     assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string))));
2022-06-28 18:21:15 +02:00
Duncan Ogilvie ef537ce084 Bump to 0.2.13 2022-06-01 19:27:03 +02:00
Duncan Ogilvie 9cdd0f7344 Escape lists when generating commands 2022-06-01 19:26:41 +02:00
Duncan Ogilvie e69cf4d2b9 Add install.optional flag 2022-06-01 19:06:53 +02:00
Duncan Ogilvie e98a906231 Document the install.component option 2022-06-01 16:59:54 +02:00
Duncan Ogilvie 9a82f8c796 Temporarily remove the crt-linkage and library-linkage options
These require a lot more work to integrate properly with vcpkg, reimplement triplet detection and set a custom triplet
2022-06-01 16:57:09 +02:00
Duncan Ogilvie 13255c68cf Use FetchContent_MakeAvailable for vcpkg in case they add CMakeLists.txt 2022-06-01 16:53:38 +02:00
Duncan Ogilvie 5768460827 Update credits 2022-04-08 18:16:38 +02:00
Duncan Ogilvie 50d4a905b6 Merge pull request #55 from ZehMatt/fix/#54
Fix #54: Fix settings not being properly quoted
2022-04-01 12:49:02 +02:00
ζeh Matt f957cec2dc Update CMakeLists.txt for tests 2022-04-01 03:37:04 +03:00
Duncan Ogilvie 232e49e087 Add support for vcpkg CRT and library linkage customization 2022-04-01 02:27:41 +02:00
ζeh Matt 7408d42160 Fix #54: Fix settings not being properly quoted 2022-04-01 03:15:32 +03:00
103 changed files with 3969 additions and 26756 deletions
+3 -2
View File
@@ -10,9 +10,10 @@ AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: true AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
AllowShortLambdasOnASingleLine: None
AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
@@ -72,7 +73,7 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60 PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right PointerAlignment: Right
ReflowComments: true ReflowComments: true
SortIncludes: true SortIncludes: false
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true
+24
View File
@@ -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
+16 -9
View File
@@ -1,21 +1,25 @@
name: CMake name: build
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: cmake:
# Skip building pull requests from the same repository # Skip building pull requests from the same repository
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }} if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [windows-2019, macos-10.15, ubuntu-20.04] os: [windows-2022, macos-latest, ubuntu-22.04]
env: env:
BUILD_TYPE: Release BUILD_TYPE: 'Release'
CMAKE_GENERATOR: 'Ninja'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Visual Studio Development Environment
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.1
- name: Tag cmkr.cmake - name: Tag cmkr.cmake
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
@@ -38,28 +42,31 @@ jobs:
ctest -C ${{ env.BUILD_TYPE }} --verbose ctest -C ${{ env.BUILD_TYPE }} --verbose
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: ${{ github.event.repository.name }}-${{ matrix.os }} name: ${{ github.event.repository.name }}-${{ matrix.os }}
path: install/bin/* path: install/bin/*
- name: Get lowercase OS name - name: Get lowercase OS name
id: osname id: osname
uses: ASzc/change-string-case-action@v1 uses: ASzc/change-string-case-action@ccb130a4e483d3e86287289183704dc9bf53e77e # master 2024-04-05
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
string: ${{ runner.os }} string: ${{ runner.os }}
- name: Compress artifacts - name: Compress artifacts
uses: vimtor/action-zip@v1 uses: vimtor/action-zip@1379ea20d4c5705669ba81fd626dd01b1c738f26 # v1.2
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
files: install/bin/ files: install/bin/
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }} prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
files: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip files: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
generate_release_notes: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+41
View File
@@ -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
View File
@@ -9,3 +9,5 @@ build*/
.idea/ .idea/
cmake-build*/ cmake-build*/
CMakeLists.txt.user CMakeLists.txt.user
.vscode/
.DS_Store
+25 -55
View File
@@ -1,23 +1,20 @@
# This file is automatically generated from cmake.toml - DO NOT EDIT # This file is automatically generated from cmake.toml - DO NOT EDIT
# See https://github.com/build-cpp/cmkr for more information # See https://github.com/build-cpp/cmkr for more information
cmake_minimum_required(VERSION 2.8...3.8) cmake_minimum_required(VERSION 2.8...3.31)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build") message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
endif() endif()
# Regenerate CMakeLists.txt automatically in the root project
set(CMKR_ROOT_PROJECT OFF) set(CMKR_ROOT_PROJECT OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(CMKR_ROOT_PROJECT ON) set(CMKR_ROOT_PROJECT ON)
# Enable folder support # Enable folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
# Create a configure-time dependency on cmake.toml to improve IDE support # Create a configure-time dependency on cmake.toml to improve IDE support
if(CMKR_ROOT_PROJECT)
configure_file(cmake.toml cmake.toml COPYONLY) configure_file(cmake.toml cmake.toml COPYONLY)
endif() endif()
@@ -25,7 +22,7 @@ project(cmkr
LANGUAGES LANGUAGES
CXX CXX
VERSION VERSION
0.2.12 0.2.46
DESCRIPTION DESCRIPTION
"CMakeLists generator from TOML" "CMakeLists generator from TOML"
) )
@@ -33,7 +30,7 @@ project(cmkr
include("cmake/generate_documentation.cmake") include("cmake/generate_documentation.cmake")
include("cmake/generate_resources.cmake") include("cmake/generate_resources.cmake")
# third_party # Subdirectory: third_party
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER}) set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER) if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party") set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party")
@@ -43,7 +40,7 @@ endif()
add_subdirectory(third_party) add_subdirectory(third_party)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER}) set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# tests # Subdirectory: tests
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER}) set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER) if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests") set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests")
@@ -53,64 +50,35 @@ endif()
add_subdirectory(tests) add_subdirectory(tests)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER}) set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Target: cmkr_generate_documentation
# Target cmkr_generate_documentation
set(CMKR_TARGET cmkr_generate_documentation)
set(cmkr_generate_documentation_SOURCES "")
set(CMKR_SOURCES ${cmkr_generate_documentation_SOURCES})
add_library(cmkr_generate_documentation INTERFACE) add_library(cmkr_generate_documentation INTERFACE)
if(cmkr_generate_documentation_SOURCES) set(CMKR_TARGET cmkr_generate_documentation)
target_sources(cmkr_generate_documentation INTERFACE ${cmkr_generate_documentation_SOURCES})
endif()
generate_documentation() generate_documentation()
unset(CMKR_TARGET) # Target: cmkr
unset(CMKR_SOURCES) set(cmkr_SOURCES
cmake.toml
# Target cmkr "cmake/cmkr.cmake"
set(CMKR_TARGET cmkr) "cmake/version.hpp.in"
set(cmkr_SOURCES "")
list(APPEND cmkr_SOURCES
"src/arguments.cpp"
"src/build.cpp"
"src/cmake_generator.cpp"
"src/error.cpp"
"src/help.cpp"
"src/main.cpp"
"src/project_parser.cpp"
"include/arguments.hpp" "include/arguments.hpp"
"include/build.hpp" "include/build.hpp"
"include/cmake_generator.hpp" "include/cmake_generator.hpp"
"include/error.hpp"
"include/fs.hpp" "include/fs.hpp"
"include/help.hpp" "include/help.hpp"
"include/literals.hpp" "include/literals.hpp"
"include/project_parser.hpp" "include/project_parser.hpp"
"cmake/cmkr.cmake" "src/arguments.cpp"
"cmake/version.hpp.in" "src/build.cpp"
"src/cmake_generator.cpp"
"src/help.cpp"
"src/main.cpp"
"src/project_parser.cpp"
) )
list(APPEND cmkr_SOURCES
cmake.toml
)
set(CMKR_SOURCES ${cmkr_SOURCES})
add_executable(cmkr) add_executable(cmkr)
if(cmkr_SOURCES) target_sources(cmkr PRIVATE ${cmkr_SOURCES})
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
endif()
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
if(NOT CMKR_VS_STARTUP_PROJECT)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmkr)
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES})
target_compile_features(cmkr PRIVATE target_compile_features(cmkr PRIVATE
@@ -126,13 +94,15 @@ target_link_libraries(cmkr PRIVATE
ghc_filesystem ghc_filesystem
mpark_variant mpark_variant
ordered_map ordered_map
nlohmann_json
) )
generate_resources(${CMKR_TARGET}) get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
if(NOT CMKR_VS_STARTUP_PROJECT)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmkr)
endif()
unset(CMKR_TARGET) set(CMKR_TARGET cmkr)
unset(CMKR_SOURCES) include("cmake/custom_targets.cmake")
install( install(
TARGETS TARGETS
+18 -13
View File
@@ -1,5 +1,7 @@
# cmkr # cmkr
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/build-cpp/cmkr)
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io). `cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io).
`cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example: `cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
@@ -13,27 +15,29 @@ type = "executable"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
``` ```
`cmkr` can bootstrap itself from CMake and you only need CMake to use it. `cmkr` can bootstrap itself and you only need CMake and a C++ compiler to use it.
## Getting started ## Getting started
To get started run the following commands from your project directory: To get started, run the following commands from your project directory:
```sh ```sh
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
cmake -P cmkr.cmake cmake -P cmkr.cmake
``` ```
After the bootstrapping process finishes, modify [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) and open the project in your favorite IDE or build with CMake: After the bootstrapping process finishes, customize [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) for your project and run CMake:
```sh ```sh
cmake -B build cmake -B build
cmake --build build cmake --build build
``` ```
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt`. Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt` when necessary.
In CI settings the `cmkr` bootstrapping process is skipped so there is no extra configure-time overhead in your pipelines. <sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub>
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional overhead in your pipelines.
## Template repositories ## Template repositories
@@ -53,10 +57,10 @@ Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/rele
``` ```
Usage: cmkr [arguments] Usage: cmkr [arguments]
arguments: arguments:
init [executable|library|shared|static|interface] Starts a new project in the same directory. init [executable|library|shared|static|interface] Create a project.
gen Generates CMakeLists.txt file. gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build. build <extra cmake args> Run cmake and build.
install Run cmake --install. Needs admin privileges. install Run cmake --install.
clean Clean the build directory. clean Clean the build directory.
help Show help. help Show help.
version Current cmkr version. version Current cmkr version.
@@ -64,9 +68,10 @@ arguments:
## Credits ## Credits
- https://github.com/gulrak/filesystem - [gulrak/filesystem](https://github.com/gulrak/filesystem)
- https://github.com/Tessil/ordered-map - [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
- https://github.com/ToruNiina/toml11 - [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
- https://github.com/mpark/variant - [mpark/variant](https://github.com/mpark/variant)
- https://www.svgrepo.com/svg/192268/hammer - [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer)
- https://github.com/can1357 for buying `cmkr.build` ❤️ - [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
+4 -7
View File
@@ -1,17 +1,17 @@
[cmake] [cmake]
version = "2.8...3.8" version = "2.8...3.31"
cmkr-include = false cmkr-include = false
[project] [project]
name = "cmkr" name = "cmkr"
version = "0.2.12" version = "0.2.46"
description = "CMakeLists generator from TOML" description = "CMakeLists generator from TOML"
languages = ["CXX"] languages = ["CXX"]
subdirs = ["third_party", "tests"]
include-after = [ include-after = [
"cmake/generate_documentation.cmake", "cmake/generate_documentation.cmake",
"cmake/generate_resources.cmake" "cmake/generate_resources.cmake"
] ]
subdirs = ["third_party", "tests"]
[target.cmkr_generate_documentation] [target.cmkr_generate_documentation]
type = "interface" type = "interface"
@@ -36,11 +36,8 @@ link-libraries = [
"ghc_filesystem", "ghc_filesystem",
"mpark_variant", "mpark_variant",
"ordered_map", "ordered_map",
"nlohmann_json",
] ]
cmake-after = """ include-after = ["cmake/custom_targets.cmake"]
generate_resources(${CMKR_TARGET})
"""
[[install]] [[install]]
targets = ["cmkr"] targets = ["cmkr"]
+30 -5
View File
@@ -2,7 +2,7 @@ include_guard()
# Change these defaults to point to your infrastructure if desired # Change these defaults to point to your infrastructure if desired
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE) set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
set(CMKR_TAG "v0.2.12" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE) set(CMKR_TAG "v0.2.46" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE) set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake # To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
@@ -10,6 +10,7 @@ if(CMAKE_SCRIPT_MODE_FILE)
set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build") set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build")
set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}") set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}") file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}")
file(WRITE "${CMAKE_BINARY_DIR}/.gitignore" "# This file is autogenerated by cmkr.\n*\n")
endif() endif()
# Set these from the command line to customize for development/debugging purposes # Set these from the command line to customize for development/debugging purposes
@@ -58,9 +59,16 @@ else()
endif() endif()
# Use cached cmkr if found # Use cached cmkr if found
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}") if(DEFINED ENV{CMKR_CACHE})
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}") set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}") string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
if(CMKR_DIRECTORY_PREFIX MATCHES "^~")
if(WIN32)
string(REGEX REPLACE "^~" "$ENV{USERPROFILE}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
elseif(UNIX)
string(REGEX REPLACE "^~" "$ENV{HOME}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
endif()
endif()
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$") if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/") set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
endif() endif()
@@ -72,10 +80,27 @@ endif()
set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}") set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}") set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
# Helper function to check if a string starts with a prefix
# Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61
function(cmkr_startswith str prefix result)
string(LENGTH "${prefix}" prefix_length)
string(LENGTH "${str}" str_length)
if(prefix_length LESS_EQUAL str_length)
string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix)
if(prefix STREQUAL str_prefix)
set("${result}" ON PARENT_SCOPE)
return()
endif()
endif()
set("${result}" OFF PARENT_SCOPE)
endfunction()
# Handle upgrading logic # Handle upgrading logic
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE) if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
if(CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr") cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD)
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}") cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE)
if(CMKR_STARTSWITH_BUILD)
if(DEFINED ENV{CMKR_CACHE})
message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'") message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
if(EXISTS "${CMKR_CACHED_EXECUTABLE}") if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
@@ -86,7 +111,7 @@ if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE) unset(CMKR_EXECUTABLE CACHE)
endif() endif()
elseif(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}" AND CMKR_EXECUTABLE MATCHES "^${CMKR_DIRECTORY_PREFIX}") elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE)
message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE) unset(CMKR_EXECUTABLE CACHE)
endif() endif()
+18
View File
@@ -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()
+12 -12
View File
@@ -1,21 +1,21 @@
option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT}) option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT})
set(CMKR_TESTS "" CACHE INTERNAL "List of test directories in the order declared in tests/cmake.toml")
if(CMKR_GENERATE_DOCUMENTATION)
# Hook the add_test function to capture the tests in the order declared in tests/cmake.toml
function(add_test)
cmake_parse_arguments(TEST "" "WORKING_DIRECTORY" "" ${ARGN})
get_filename_component(TEST_WORKING_DIRECTORY "${TEST_WORKING_DIRECTORY}" NAME)
list(APPEND CMKR_TESTS "${TEST_WORKING_DIRECTORY}")
set(CMKR_TESTS "${CMKR_TESTS}" CACHE INTERNAL "")
_add_test(${test} ${ARGN})
endfunction()
endif()
function(generate_documentation) function(generate_documentation)
if(CMKR_GENERATE_DOCUMENTATION) if(CMKR_GENERATE_DOCUMENTATION)
message(STATUS "[cmkr] Generating documentation...") message(STATUS "[cmkr] Generating documentation...")
# Extract the order of the tests
set(CMKR_TESTS "")
file(READ "${PROJECT_SOURCE_DIR}/tests/cmake.toml" tests_toml NO_HEX_CONVERSION)
string(REGEX MATCHALL "working-directory = \"([^\"]+)\"" tests_match "${tests_toml}")
foreach(match ${tests_match})
if(match MATCHES "working-directory = \"([^\"]+)\"")
list(APPEND CMKR_TESTS "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "This should not happen (wrong regex?)")
endif()
endforeach()
# Delete previously generated examples # Delete previously generated examples
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples") set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")
file(GLOB example_files "${example_folder}/*.md") file(GLOB example_files "${example_folder}/*.md")
+18
View File
@@ -21,4 +21,22 @@ gh_edit_branch: "main"
gh_edit_view_mode: "edit" gh_edit_view_mode: "edit"
gh_edit_source: docs gh_edit_source: docs
ga_tracking: G-DY172KY7Z9
production_url : https://cmkr.build production_url : https://cmkr.build
footer_content: |
<script src="https://giscus.app/client.js"
data-repo="build-cpp/cmkr"
data-repo-id="MDEwOlJlcG9zaXRvcnkyOTQwMDc3MzY="
data-category="Documentation"
data-category-id="DIC_kwDOEYYzuM4CTXeA"
data-mapping="pathname"
data-strict="1"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="en"
crossorigin="anonymous"
async>
</script>
+143
View File
@@ -0,0 +1,143 @@
---
layout: page
title: Basics
nav_order: 1
---
# Basics
To effectively use cmkr it helps to understand the basic concepts of CMake. This page serves as an introduction to CMake for beginners. Links to the CMake documentation are included for reference.
## Projects
A CMake **project** is a collection of targets. In the context of libraries the project usually corresponds to the **package** that other projects can depend on.
<sub>Visual Studio: a CMake **project** corresponds to a _solution_.</sub>
## Targets
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or static/dynamic library you can build.
<sub>Visual Studio: a **target** corresponds to a _project_.</sub>
Most important target types:
|Type|Purpose|
|-|-|
|Executable|Program you run on your computer (`.exe`)|
|Static library|Library code _compiled into_ the final executable (`.lib`, `.a`)|
|Dynamic library|Library code loaded at runtime (`.dll`, `.so`, `.dylib`)|
|Interface|Used for organizational purposes (common flags/includes/libraries)|
There are also other [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but they are not important for now.
## Target Properties
Targets have a collection of **properties** that describe how the compiler builds them.
The most commonly-used properties:
- [_Sources_](https://cmake.org/cmake/help/latest/command/target_sources.html): the `*.cpp` files used to build the target.
- [_Include directories_](https://cmake.org/cmake/help/latest/command/target_include_directories.html): Paths to directories the compiler will search when doing `#include`.
- [_Compile definitions_](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html): Preprocessor macro definitions used for compilation (`-DMYOPTION`).
- [_Compile options_](https://cmake.org/cmake/help/latest/command/target_compile_options.html): Command line flags for the compiler (platform/compiler specific).
- [_Compile features_](https://cmake.org/cmake/help/latest/command/target_compile_features.html): C/C++ standard version to build with.
- [_Link libraries_](https://cmake.org/cmake/help/latest/command/target_link_libraries.html): The **dependencies** required to build this target.
- [_Link options_](https://cmake.org/cmake/help/latest/command/target_link_options.html): Command line flags for the linker (platform/linker specific).
<sub>See the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for an exhaustive list of target properties.</sub>
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line, CMake also (transitively) propagates properties of the target you link to based on their _visibility_.
You should think of **linking** in CMake as _depending on_.
{:.info}
The propagation of properties depends on their **visibility**:
- **Private**: properties that are only used when building the target itself.
- **Interface**: properties that are used when depending on this target.
- **Public**: combination of private and interface.
In practice you default to **private**, unless consumers of your library _require_ the property to build their target. In that case you use **public**.
## Example
The most intuitive example is with _include directories_. Imagine there are two targets:
1. `StringUtils`: A library with string utilities.
- _Sources_: `StringUtils/src/stringutils.cpp`
- _Include directories_: `StringUtils/include`
2. `DataProcessor`: An executable that uses functionality from `StringUtils` to process some data.
- _Sources_: `DataProcessor/src/main.cpp`
- _Link libraries_: `StringUtils`
The _include directories_ property of `StringUtils` has to be **public**. If it was **private**, the `DataProcessor` target would fail to `#include <stringutils.hpp>` since the _include directories_ property is not propagated.
The `cmake.toml` for this example would look something like this:
```toml
[project]
name = "DataProcessor"
[target.StringUtils]
type = "static"
sources = ["StringUtils/src/stringutils.cpp"]
headers = ["StringUtils/include/stringutils.hpp"]
include-directories = ["StringUtils/include"] # public (default for libraries)
[target.DataProcessor]
type = "executable"
sources = ["DataProcessor/src/main.cpp"]
link-libraries = ["StringUtils"] # private (default for executables)
```
The generated `CMakeLists.txt` (simplified) looks like this:
```cmake
project(DataProcessor)
# Target: StringUtils
add_library(StringUtils STATIC
"StringUtils/include/stringutils.hpp"
"StringUtils/src/stringutils.cpp"
)
target_include_directories(StringUtils PUBLIC
"StringUtils/include"
)
# Target: DataProcessor
add_executable(DataProcessor
"DataProcessor/src/main.cpp"
)
target_link_libraries(DataProcessor PRIVATE
StringUtils
)
```
## CMake Phases
CMake works in three phases:
1. **Configure**: Execute `CMakeLists.txt` (find dependencies, create targets, OS/compiler-specific conditions, etc).
2. **Generate**: Generate build files (propagates target properties and handles [_generator expressions_](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html)).
3. **Build**: Execute the compiler to produce executables/libraries.
CMake keeps generated files separate from your source in a **build directory**. You specify a [**generator**](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) (Visual Studio/Ninja) and [**build type**](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations) (Debug/Release) during configuration.
```bash
# Configure project and generate build files in build directory
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build the project
cmake --build build --config Release
```
We specify both `CMAKE_BUILD_TYPE` and `--config` to be compatible with generators that support multiple configurations at once ((like Visual Studio/Xcode/Ninja Multi-Config).
{:.info}
**Build Types**:
- `Debug`: No optimization, debug symbols
- `Release`: Full optimization, no debug symbols
- `RelWithDebInfo`: Medium optimizations, debug symbols
- `MinSizeRel`: Optimize for size, no debug symbols
**Generators**: CMake auto-detects (Makefiles on Linux/macOS, Visual Studio on Windows), but you can specify explicitly with `-G` during the _configure_ step.
+167 -28
View File
@@ -2,14 +2,15 @@
layout: page layout: page
title: Reference title: Reference
permalink: /cmake-toml/ permalink: /cmake-toml/
nav_order: 1 nav_order: 3
--- ---
# Reference # Reference
This page is a reference for the options in the `cmake.toml` file. If you think anything is missing or unclear, please [edit this page](https://github.com/build-cpp/cmkr/edit/main/docs/cmake-toml.md) or open an [issue](https://github.com/build-cpp/cmkr/issues). This page is a reference for the options in the `cmake.toml` file. If you think anything is missing or unclear, please [edit this page](https://github.com/build-cpp/cmkr/edit/main/docs/cmake-toml.md) or open an [issue](https://github.com/build-cpp/cmkr/issues).
See the [examples](/examples) section for concrete examples. This is a reference page. Check out the [examples](/examples) and the [cmkr topic](https://github.com/topics/cmkr) as well.
{:.info}
## CMake configuration ## CMake configuration
@@ -27,6 +28,7 @@ name = "myproject"
version = "1.0.0" version = "1.0.0"
description = "Description of the project" description = "Description of the project"
languages = ["C", "CXX"] languages = ["C", "CXX"]
msvc-runtime = "" # dynamic (implicit default), static
cmake-before = """ cmake-before = """
message(STATUS "CMake injected before the project() call") message(STATUS "CMake injected before the project() call")
""" """
@@ -37,17 +39,45 @@ include-before = ["cmake/before-project.cmake"]
include-after = ["cmake/after-project.cmake"] include-after = ["cmake/after-project.cmake"]
``` ```
### Languages
Supported languages are (see [`enable_language`](https://cmake.org/cmake/help/latest/command/enable_language.html) for more information):
- `C`
- `CXX` → C++
- `CSharp` → C#
- `CUDA`
- `OBJC` → Objective-C
- `OBJCXX` → Objective-C++
- `Fortran`
- `HIP`
- `ISPC`
- `Swift`
- `ASM`
- `ASM_MASM` → [Microsoft Macro Assembler (MASM)](https://learn.microsoft.com/en-US/cpp/assembler/masm/masm-for-x64-ml64-exe)
- `ASM_NASM` → [Netwide Assembler (NASM)](https://www.nasm.us)
- `ASM_MARMASM` [Microsoft ARM Assembler](https://learn.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-command-line-reference)
- `ASM-ATT`
- `Java` (undocumented)
- `RC` (undocumented)
After a language is enabled, adding sources files with the corresponding extension to your target will automatically use the appropriate compiler/assembler for it.
_Note_: It is generally discouraged to disable the `C` language, unless you are absolutely sure it is not used. Sometimes projects added with `fetch-content` implicitly require it and the error messages can be extremely confusing.
## Conditions ## Conditions
You can specify your own conditions and use them in any `condition` field: You can specify your own named conditions and use them in any `condition` field:
```toml ```toml
[conditions] [conditions]
arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8" ptr64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4" 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: You can also prefix most keys with `condition.` to represent a conditional:
@@ -55,9 +85,13 @@ You can also prefix most keys with `condition.` to represent a conditional:
[target] [target]
type = "executable" type = "executable"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
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): The following conditions are predefined (you can override them if you desire):
```toml ```toml
@@ -68,11 +102,20 @@ unix = "UNIX"
bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\"" bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\""
linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\"" linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\""
gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\"" gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\""
clang = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
msvc = "MSVC" msvc = "MSVC"
clang = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
clang-cl = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
clang-any = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
root = "CMKR_ROOT_PROJECT" root = "CMKR_ROOT_PROJECT"
x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8" x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4" x32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
android = "ANDROID"
apple = "APPLE"
bsd = "BSD"
cygwin = "CYGWIN"
ios = "IOS"
xcode = "XCODE"
wince = "WINCE"
``` ```
## Subdirectories ## Subdirectories
@@ -90,18 +133,47 @@ include-before = ["cmake/before-subdir.cmake"]
include-after = ["cmake/after-subdir.cmake"] include-after = ["cmake/after-subdir.cmake"]
``` ```
## Options
```toml
[options]
MYPROJECT_BUILD_TESTS = false
MYPROJECT_SPECIAL_OPTION = { value = true, help = "Docstring for this option." }
MYPROJECT_BUILD_EXAMPLES = "root"
```
Options correspond to [CMake cache variables](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html) that can be used to customize your project at configure-time. You can configure with `cmake -DMYPROJECT_BUILD_TESTS=ON` to enable the option. Every option automatically gets a corresponding [condition](#conditions). Additionally, a normalized condition is created based on the `[project].name` (i.e. `MYPROJECT_BUILD_TESTS` becomes `build-tests`).
The special value `root` can be used to set the option to `true` if the project is compiled as the root project (it will be `false` if someone is including your project via `[fetch-content]` or `[subdir]`).
## Variables
```toml
[variables]
MYBOOL = true
MYSTRING = "hello"
```
Variables emit a [`set`](https://cmake.org/cmake/help/latest/command/set.html) and can be used to configure subprojects and packages.
## Vcpkg ## Vcpkg
```toml ```toml
[vcpkg] [vcpkg]
version = "2021.05.12" version = "2024.11.16"
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2021.05.12.tar.gz" url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2024.11.16.tar.gz"
packages = ["fmt", "zlib"] packages = ["fmt", "zlib"]
overlay-ports = ["my-ports"]
overlay-triplets = ["my-triplets"]
``` ```
The vcpkg `version` will automatically generate the `url` from the [official repository](https://github.com/microsoft/vcpkg/releases). For a custom registry you can specify your own `url` (and omit the `version`). You can browse available packages on [vcpkg.io](https://vcpkg.io/en/packages.html). 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 ## Packages
@@ -119,49 +191,60 @@ components = ["mycomponent"]
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release. **Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
```toml ```toml
# Include CMake project from git
[fetch-content.gitcontent] [fetch-content.gitcontent]
condition = "mycondition" condition = "mycondition"
git = "https://github.com/myuser/gitcontent" git = "https://github.com/myuser/gitcontent"
tag = "v0.1" tag = "v0.1"
shallow = false # shallow clone (--depth 1)
system = false
subdir = "" # folder containing CMakeLists.txt
# Include a CMake project from a URL
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
# Other supported algorithms:
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
[fetch-content.svncontent] [fetch-content.svncontent]
condition = "mycondition" condition = "mycondition"
svn = "https://svn-host.com/url" svn = "https://svn-host.com/url"
rev = "svn_rev" rev = "svn_rev"
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
hash = "123123123123"
``` ```
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 ## Targets
```toml ```toml
[target.mytarget] [target.mytarget]
condition = "mycondition" condition = "mycondition"
alias = "mytarget::mytarget" 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"] headers = ["src/mytarget.h"]
sources = ["src/mytarget.cpp"] sources = ["src/mytarget.cpp"]
msvc-runtime = "" # dynamic (implicit default), static
# The keys below match the target_xxx CMake commands # The keys below match the target_xxx CMake commands
# Keys prefixed with private- will get PRIVATE visibility # Keys prefixed with private- will get PRIVATE visibility
compile-definitions = [""] compile-definitions = [""] # preprocessor define (-DFOO)
private-compile-definitions = [""] private-compile-definitions = [""]
compile-features = [""] compile-features = [""] # C++ standard version (cxx_std_20)
private-compile-features = [""] private-compile-features = [""]
compile-options = [""] compile-options = [""] # compiler flags
private-compile-options = [""] private-compile-options = [""]
include-directories = [""] include-directories = [""] # include paths/directories
private-include-directories = [""] private-include-directories = [""]
link-directories = [""] link-directories = [""] # library directories
private-link-directories = [""] private-link-directories = [""]
link-libraries = [""] link-libraries = [""] # dependencies
private-link-libraries = [""] private-link-libraries = [""]
link-options = [""] link-options = [""] # linker flags
private-link-options = [""] private-link-options = [""]
precompile-headers = [""] precompile-headers = [""] # precompiled headers
private-precompile-headers = [""] private-precompile-headers = [""]
cmake-before = """ cmake-before = """
@@ -180,6 +263,60 @@ CXX_STANDARD_REQUIRED = true
FOLDER = "MyFolder" 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:
```toml
[template.example]
condition = "MYPROJECT_BUILD_EXAMPLES"
type = "executable"
link-libraries = ["myproject::mylib"]
add-function = ""
add-arguments = ["myoption"]
pass-sources = false
# Properties from the template are merged with the ones here
[target.myexample]
type = "example"
sources = ["src/myexample.cpp"]
```
The properties declared on a `template` are the same as the ones you use for targets. The only exceptions are:
- `add-function`: Specifies a custom add function. Projects like [pybind11](https://pybind11.readthedocs.io/en/stable/cmake/index.html#new-findpython-mode) have their own `add_xxx` function, which you can specify here.
- `add-arguments`: Arguments to pass to the `add-function` before the list of sources. See [cmake_parse_arguments](https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html) for more details.
- `pass-sources`: Pass sources directly to the add function instead of using `target_sources`.
## Tests and installation (unfinished) ## Tests and installation (unfinished)
**Note**: The `[[test]]` and `[[install]]` are unfinished features and will likely change in a future release. **Note**: The `[[test]]` and `[[install]]` are unfinished features and will likely change in a future release.
@@ -200,7 +337,9 @@ working-directory = "mytest-dir"
condition = "mycondition" condition = "mycondition"
targets = ["mytarget", "mytest"] targets = ["mytarget", "mytest"]
destination = ["bin"] destination = ["bin"]
component = "mycomponent"
files = ["content/my.png"] files = ["content/my.png"]
dirs = [""] dirs = ["include"]
configs = [""] configs = ["Release", "Debug"]
optional = false
``` ```
+34
View File
@@ -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>
+3 -2
View File
@@ -16,8 +16,9 @@ Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) from [GitHub](https://github.com)
name = "fetch-content" name = "fetch-content"
description = "Fetching from git" description = "Fetching from git"
[fetch-content] [fetch-content.fmt]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" } git = "https://github.com/fmtlib/fmt"
tag = "7.1.3"
[target.example] [target.example]
type = "executable" type = "executable"
+34
View File
@@ -0,0 +1,34 @@
---
# Automatically generated from tests/msvc-runtime/cmake.toml - DO NOT EDIT
layout: default
title: Static MSVC runtime
permalink: /examples/msvc-runtime
parent: Examples
nav_order: 8
---
# Static MSVC runtime
```toml
[project]
name = "msvc-runtime"
description = "Static MSVC runtime"
msvc-runtime = "static"
# This target will compile with a static runtime
[target.static-runtime]
type = "executable"
sources = ["src/main.cpp"]
# This target overrides the [project].msvc-runtime
[target.dynamic-runtime]
type = "executable"
sources = ["src/main.cpp"]
msvc-runtime = "dynamic"
```
<sup><sub>This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml).</sub></sup>
+30
View File
@@ -0,0 +1,30 @@
---
# Automatically generated from tests/objective-c/cmake.toml - DO NOT EDIT
layout: default
title: Objective-C
permalink: /examples/objective-c
parent: Examples
nav_order: 11
---
# Objective-C
Add Objective-C sources on Apple platforms:
```toml
[project]
name = "objective-c"
description = "Objective-C"
languages = ["C"]
apple.languages = ["OBJC"]
[target.hello]
type = "executable"
sources = ["src/main.c"]
apple.sources = ["src/apple.m"]
apple.link-libraries = ["$<LINK_LIBRARY:FRAMEWORK,Foundation>"]
```
<sup><sub>This page was automatically generated from [tests/objective-c/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/objective-c/cmake.toml).</sub></sup>
+4 -4
View File
@@ -20,11 +20,11 @@ description = "Target templates"
type = "executable" type = "executable"
sources = ["src/templates.cpp"] sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"] compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties # Unlike interface targets you can also inherit properties
[template.app.properties] properties = {
CXX_STANDARD = "11" CXX_STANDARD = "11",
CXX_STANDARD_REQUIRED = true CXX_STANDARD_REQUIRED = true,
}
[target.app-a] [target.app-a]
type = "app" type = "app"
+4 -5
View File
@@ -9,7 +9,7 @@ nav_order: 4
# Dependencies from vcpkg # Dependencies from vcpkg
Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://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 ```toml
[project] [project]
@@ -17,13 +17,12 @@ name = "vcpkg"
description = "Dependencies from vcpkg" description = "Dependencies from vcpkg"
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions # See https://github.com/microsoft/vcpkg/releases for vcpkg versions
# See https://vcpkg.io/en/packages.html for available packages # See https://vcpkg.io/en/packages or https://vcpkg.link for available packages
[vcpkg] [vcpkg]
version = "2021.05.12" version = "2024.11.16"
packages = ["fmt"] packages = ["fmt"]
[find-package] [find-package.fmt]
fmt = {}
[target.example] [target.example]
type = "executable" type = "executable"
+19 -15
View File
@@ -1,12 +1,12 @@
--- ---
layout: home layout: home
title: Index title: Documentation
nav_order: 0 nav_order: 0
--- ---
# Index # Documentation
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io). [`cmkr`](https://github.com/build-cpp/cmkr), pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io).
`cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example: `cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
@@ -19,27 +19,29 @@ type = "executable"
sources = ["src/main.cpp"] sources = ["src/main.cpp"]
``` ```
`cmkr` can bootstrap itself from CMake and you only need CMake to use it. `cmkr` can bootstrap itself, and you only need CMake and a C++ compiler to use it.
## Getting started ## Getting started
To get started run the following commands from your project directory: To get started, run the following commands from your project directory:
```sh ```sh
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
cmake -P cmkr.cmake cmake -P cmkr.cmake
``` ```
After the bootstrapping process finishes, modify [`cmake.toml`](https://build-cpp.github.io/cmkr/cmake-toml) and open the project in your favorite IDE or build with CMake: After the bootstrapping process finishes, customize [`cmake.toml`](./cmake-toml) for your project and run CMake:
```sh ```sh
cmake -B build cmake -B build
cmake --build build cmake --build build
``` ```
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt`. Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt` when necessary.
In CI settings the `cmkr` bootstrapping process is skipped so there is no extra configure-time overhead in your pipelines. <sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub>
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional overhead in your pipelines.
## Template repositories ## Template repositories
@@ -59,10 +61,10 @@ Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/rele
``` ```
Usage: cmkr [arguments] Usage: cmkr [arguments]
arguments: arguments:
init [executable|library|shared|static|interface] Starts a new project in the same directory. init [executable|library|shared|static|interface] Create a project.
gen Generates CMakeLists.txt file. gen Generates CMakeLists.txt file.
build <extra cmake args> Run cmake and build. build <extra cmake args> Run cmake and build.
install Run cmake --install. Needs admin privileges. install Run cmake --install.
clean Clean the build directory. clean Clean the build directory.
help Show help. help Show help.
version Current cmkr version. version Current cmkr version.
@@ -70,8 +72,10 @@ arguments:
## Credits ## Credits
- https://github.com/gulrak/filesystem - [gulrak/filesystem](https://github.com/gulrak/filesystem)
- https://github.com/Tessil/ordered-map - [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
- https://github.com/ToruNiina/toml11 - [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
- https://github.com/mpark/variant - [mpark/variant](https://github.com/mpark/variant)
- https://www.svgrepo.com/svg/192268/hammer - [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer)
- [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
+83
View File
@@ -0,0 +1,83 @@
---
layout: page
title: Philosophy
nav_order: 2
published: false
---
# Philosophy
## Problem statement
Similar to writing good C++, writing good CMake is difficult. The main difference is that nobody actually wants to learn CMake. The build system is something that should "just work".
There have been many attempts at creating new build systems (with varying levels of success). Naturally this causes [competing standards](https://xkcd.com/927/), which is undesirable. CMake is pretty much the de facto standard, and it has seen extensive use in complex software projects.
One of the main issues of CMake is the turing-complete scripting language you use to describe your projects. As your project gets more complex this can be very helpful, but it also unnecessarily complicates simple projects. There have been discussions about a declarative language on the [CMake issue tracker](https://gitlab.kitware.com/cmake/cmake/-/issues/19891) to solve this problem, but it looks like a "LISP-like" language is seriously being considered...
## The solution
The way cmkr (pronounced "cmaker") solves this problem is by using [TOML](https://toml.io/). Below is a minimal `cmake.toml` project:
```toml
[project]
name = "cmkr_for_beginners"
[target.hello_world]
type = "executable"
sources = ["src/main.cpp"]
```
The key difference between `cmkr` and other build systems is that it _generates_ CMake. This means that your projects are fully compatible with the CMake ecosystem, and you can try it out without having to rewrite your whole build system.
TODO: link/include more examples? Talk about conditions, packaging (missing)
### Another layer?!
A common issue people have with cmkr is that it introduces an additional layer of indirection to your build system. CMake is already a meta-buildsystem, which means you could call cmkr a "meta-meta-buildsystem".
Presumably the reason for this friction is that additional layers of abstraction introduce additional complexity. Because of this cmkr has been designed to be completely seamless:
- The user doesn't have to install any additional software to use cmkr. All you need is a semi-recent version of CMake and a C++ compiler.
- Modifying `cmake.toml` automatically triggers a regeneration of `CMakeLists.txt`. There is no change to your build process.
- The `CMakeLists.txt` is generated to be human-readable. This means you can easily "eject" from cmkr and go back to CMake.
An additional argument for cmkr is that anecdotally people say it "just works". Because of its simplicity it's also easy to teach, even to people without programming experience.
There is also precedent in the JavaScript community. Bundlers and translators are the norm there and their developer experience is miles ahead of C++.
<sub>Not to say the JavaScript ecosystem is without its flaws, but generators does not appear to be one of them</sub>
### Unsupported features
Because cmkr is still in early in development there are many missing/unfinished features. It was decided that users can bridge the gap by including CMake at arbitrary locations.
This has the advantage that it forces complexity in the build system to be self-contained and modular, a problem all too common in projects as they age.
## Enterprise
Words like "bootstrapping" and "generating code" might worry engineers working in an enterprise environment, and rightly so. From the beginning it has been a priority to make cmkr suitable for use in big corporations with strict protocols for security and reproducibility.
### No additional dependencies
As mentioned above, the only thing you need is a working C++ compiler and a semi-recent version of CMake. It is assumed that you are already building (and executing) C++ projects on your servers, so cmkr does not introduce additional requirements.
All the logic for downloading and compiling the `cmkr` executable is self-contained in a ~250 line `cmkr.cmake` script. You can easily audit it and see if it's up to your standards.
### Reproducibility
Per default the `cmkr.cmake` bootstrapping script contains the exact version of cmkr used to generate your project. As long as the cmkr repository is available you will build the exact same version of cmkr, which will generate the exact same `CMakeLists.txt` file.
This also means that cmkr can decide to break backwards compatibility without affecting legacy projects. An effort will always be made to maintain backwards compatibility though.
### Integrity
As an additional safeguard you can modify `cmkr.cmake` to pin the version tag to a commit hash. This hash is checked to ensure the integrity of the upstream repository.
### Availability
You can easily point `cmkr.cmake` to a mirror of the cmkr repository to ensure availability should something catastrophic happen.
### Not executed in CI
The final (and key) feature is that the bootstrapping process is never executed in CI environments. This means `cmkr` is only ever executed on your developer's machines and not on your infrastructure.
View File
-2
View File
@@ -1,2 +0,0 @@
bundle exec just-the-docs rake search:init
bundle exec jekyll serve --livereload
View File
-2
View File
@@ -7,5 +7,3 @@ const char *handle_args(int argc, char **argv);
} // namespace args } // namespace args
} // namespace cmkr } // namespace cmkr
const char *cmkr_args_handle_args(int, char **);
-6
View File
@@ -11,9 +11,3 @@ int install();
} // namespace build } // namespace build
} // namespace cmkr } // namespace cmkr
int cmkr_build_run(int argc, char **argv);
int cmkr_build_clean();
int cmkr_build_install();
-27
View File
@@ -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);
+1 -3
View File
@@ -1,8 +1,6 @@
#pragma once #pragma once
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || \ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
(defined(__cplusplus) && __cplusplus >= 201703L)) && \
defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS #define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
-4
View File
@@ -9,7 +9,3 @@ const char *message() noexcept;
} // namespace help } // namespace help
} // namespace cmkr } // namespace cmkr
const char *cmkr_help_version(void);
const char *cmkr_help_message(void);
+42 -11
View File
@@ -1,9 +1,11 @@
#pragma once #pragma once
#include <mpark/variant.hpp>
#include <string>
#include <tsl/ordered_map.h>
#include <vector> #include <vector>
#include <string>
#include <mpark/variant.hpp>
#include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
namespace cmkr { namespace cmkr {
namespace parser { namespace parser {
@@ -13,18 +15,18 @@ using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>; using ConditionVector = Condition<std::vector<std::string>>;
struct Setting { struct Variable {
std::string name; std::string name;
std::string comment; std::string help;
mpark::variant<bool, std::string> val; mpark::variant<bool, std::string> value;
bool cache = false; bool cache = false;
bool force = false; bool force = false;
}; };
struct Option { struct Option {
std::string name; std::string name;
std::string comment; std::string help;
bool val = false; mpark::variant<bool, std::string> value;
}; };
struct Package { struct Package {
@@ -43,9 +45,18 @@ struct Vcpkg {
struct Package { struct Package {
std::string name; std::string name;
std::vector<std::string> features; std::vector<std::string> features;
bool default_features = true;
}; };
std::vector<Package> packages; std::vector<Package> packages;
// https://github.com/Microsoft/vcpkg-docs/blob/main/vcpkg/users/buildsystems/cmake-integration.md
std::vector<std::string> overlay_ports;
std::vector<std::string> overlay_triplets;
bool enabled() const {
return !packages.empty();
}
}; };
enum TargetType { enum TargetType {
@@ -67,7 +78,6 @@ struct Target {
TargetType type = target_last; TargetType type = target_last;
std::string type_name; std::string type_name;
ConditionVector headers;
ConditionVector sources; ConditionVector sources;
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands // https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
@@ -95,6 +105,8 @@ struct Target {
ConditionVector precompile_headers; ConditionVector precompile_headers;
ConditionVector private_precompile_headers; ConditionVector private_precompile_headers;
ConditionVector dependencies;
std::string condition; std::string condition;
std::string alias; std::string alias;
Condition<tsl::ordered_map<std::string, std::string>> properties; Condition<tsl::ordered_map<std::string, std::string>> properties;
@@ -108,6 +120,7 @@ struct Target {
struct Template { struct Template {
Target outline; Target outline;
std::string add_function; std::string add_function;
std::vector<std::string> add_arguments;
bool pass_sources_to_add_function = false; bool pass_sources_to_add_function = false;
}; };
@@ -128,6 +141,7 @@ struct Install {
std::vector<std::string> configs; std::vector<std::string> configs;
std::string destination; std::string destination;
std::string component; std::string component;
bool optional = false;
}; };
struct Subdir { struct Subdir {
@@ -149,9 +163,21 @@ struct Content {
Condition<std::string> cmake_after; Condition<std::string> cmake_after;
ConditionVector include_before; ConditionVector include_before;
ConditionVector include_after; ConditionVector include_after;
bool system = false;
std::string subdir;
}; };
enum MsvcRuntimeType {
msvc_dynamic,
msvc_static,
msvc_last,
};
extern const char *msvcRuntimeTypeNames[msvc_last];
struct Project { struct Project {
const Project *parent;
// This is the CMake version required to use all cmkr versions. // This is the CMake version required to use all cmkr versions.
std::string cmake_version = "3.15"; std::string cmake_version = "3.15";
std::string cmkr_include = "cmkr.cmake"; std::string cmkr_include = "cmkr.cmake";
@@ -168,12 +194,14 @@ struct Project {
std::string project_name; std::string project_name;
std::string project_version; std::string project_version;
std::string project_description; std::string project_description;
std::vector<std::string> project_languages; ConditionVector project_languages;
bool project_allow_unknown_languages = false;
MsvcRuntimeType project_msvc_runtime = msvc_last;
Condition<std::string> cmake_before; Condition<std::string> cmake_before;
Condition<std::string> cmake_after; Condition<std::string> cmake_after;
ConditionVector include_before; ConditionVector include_before;
ConditionVector include_after; ConditionVector include_after;
std::vector<Setting> settings; std::vector<Variable> variables;
std::vector<Option> options; std::vector<Option> options;
std::vector<Package> packages; std::vector<Package> packages;
Vcpkg vcpkg; Vcpkg vcpkg;
@@ -186,6 +214,9 @@ struct Project {
std::vector<Subdir> subdirs; std::vector<Subdir> subdirs;
Project(const Project *parent, const std::string &path, bool build); Project(const Project *parent, const std::string &path, bool build);
const Project *root() const;
bool cmake_minimum_version(int major, int minor) const;
static bool is_condition_name(const std::string &name);
}; };
bool is_root_path(const std::string &path); bool is_root_path(const std::string &path);
+4 -16
View File
@@ -2,10 +2,8 @@
#include "build.hpp" #include "build.hpp"
#include "cmake_generator.hpp" #include "cmake_generator.hpp"
#include "help.hpp" #include "help.hpp"
#include "fs.hpp" #include "fs.hpp"
#include <exception>
#include <iostream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -37,17 +35,17 @@ const char *handle_args(int argc, char **argv) {
} else if (main_arg == "build") { } else if (main_arg == "build") {
auto ret = build::run(argc, argv); auto ret = build::run(argc, argv);
if (ret) if (ret)
return "CMake build error!"; throw std::runtime_error("CMake build failed!");
return "CMake build completed!"; return "CMake build completed!";
} else if (main_arg == "install") { } else if (main_arg == "install") {
auto ret = build::install(); auto ret = build::install();
if (ret) if (ret)
return "CMake install error!"; throw std::runtime_error("CMake install failed!");
return "CMake install completed!"; return "CMake install completed!";
} else if (main_arg == "clean") { } else if (main_arg == "clean") {
auto ret = build::clean(); auto ret = build::clean();
if (ret) if (ret)
return "CMake clean error!"; throw std::runtime_error("CMake clean failed!");
return "Cleaned build directory!"; return "Cleaned build directory!";
} else { } else {
throw std::runtime_error(cmkr::help::message()); throw std::runtime_error(cmkr::help::message());
@@ -55,13 +53,3 @@ const char *handle_args(int argc, char **argv) {
} }
} // namespace args } // namespace args
} // namespace cmkr } // namespace cmkr
const char *cmkr_args_handle_args(int argc, char **argv) {
try {
return cmkr::args::handle_args(argc, argv);
} catch (const std::exception &e) {
return e.what();
} catch (...) {
return "Unknown error!";
}
}
+4 -38
View File
@@ -1,14 +1,10 @@
#include "build.hpp" #include "build.hpp"
#include "cmake_generator.hpp" #include "cmake_generator.hpp"
#include "error.hpp"
#include "project_parser.hpp" #include "project_parser.hpp"
#include "fs.hpp" #include "fs.hpp"
#include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <system_error>
namespace cmkr { namespace cmkr {
namespace build { namespace build {
@@ -17,7 +13,7 @@ int run(int argc, char **argv) {
parser::Project project(nullptr, ".", true); parser::Project project(nullptr, ".", true);
if (argc > 2) { if (argc > 2) {
for (int i = 2; i < argc; ++i) { for (int i = 2; i < argc; ++i) {
project.build_args.push_back(argv[i]); project.build_args.emplace_back(argv[i]);
} }
} }
std::stringstream ss; std::stringstream ss;
@@ -48,13 +44,13 @@ int run(int argc, char **argv) {
} }
int clean() { int clean() {
bool ret = false; bool success = false;
parser::Project project(nullptr, ".", true); parser::Project project(nullptr, ".", true);
if (fs::exists(project.build_dir)) { 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); fs::create_directory(project.build_dir);
} }
return !ret; return success ? EXIT_SUCCESS : EXIT_FAILURE;
} }
int install() { int install() {
@@ -64,33 +60,3 @@ int install() {
} }
} // namespace build } // namespace build
} // namespace cmkr } // namespace cmkr
int cmkr_build_run(int argc, char **argv) {
try {
return cmkr::build::run(argc, argv);
} catch (const std::system_error &e) {
return e.code().value();
} catch (...) {
return cmkr::error::Status(cmkr::error::Status::Code::BuildError);
}
}
int cmkr_build_clean(void) {
try {
return cmkr::build::clean();
} catch (const std::system_error &e) {
return e.code().value();
} catch (...) {
return cmkr::error::Status(cmkr::error::Status::Code::CleanError);
}
}
int cmkr_build_install(void) {
try {
return cmkr::build::install();
} catch (const std::system_error &e) {
return e.code().value();
} catch (...) {
return cmkr::error::Status(cmkr::error::Status::Code::InstallError);
}
}
+767 -225
View File
File diff suppressed because it is too large Load Diff
-25
View File
@@ -1,25 +0,0 @@
#include "error.hpp"
#include <cassert>
namespace cmkr {
namespace error {
Status::Status(Code ec) noexcept : ec_(ec) {}
Status::operator int() const noexcept { return static_cast<int>(ec_); }
Status::Code Status::code() const noexcept { return ec_; }
} // namespace error
} // namespace cmkr
// strings for cmkr::error::Status::Code
static const char *err_string[] = {
"Success", "Runtime error", "Initialization error", "CMake generation error", "Build error", "Clean error", "Install error",
};
const char *cmkr_error_status(int i) {
assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string))));
return err_string[i];
}
+3 -5
View File
@@ -4,7 +4,9 @@
namespace cmkr { namespace cmkr {
namespace help { namespace help {
const char *version() noexcept { return "cmkr version " CMKR_VERSION; } const char *version() noexcept {
return "cmkr version " CMKR_VERSION;
}
const char *message() noexcept { const char *message() noexcept {
return R"lit( return R"lit(
@@ -21,7 +23,3 @@ arguments:
} }
} // namespace help } // namespace help
} // namespace cmkr } // namespace cmkr
const char *cmkr_help_version(void) { return cmkr::help::version(); }
const char *cmkr_help_message(void) { return cmkr::help::message(); }
+323 -49
View File
@@ -4,8 +4,6 @@
#include <deque> #include <deque>
#include <stdexcept> #include <stdexcept>
#include <toml.hpp> #include <toml.hpp>
#include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
namespace cmkr { namespace cmkr {
namespace parser { namespace parser {
@@ -21,16 +19,27 @@ static TargetType parse_targetType(const std::string &name) {
return target_last; return target_last;
} }
const char *msvcRuntimeTypeNames[msvc_last] = {"dynamic", "static"};
static MsvcRuntimeType parse_msvcRuntimeType(const std::string &name) {
for (int i = 0; i < msvc_last; i++) {
if (name == msvcRuntimeTypeNames[i]) {
return static_cast<MsvcRuntimeType>(i);
}
}
return msvc_last;
}
using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>; using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>;
static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) { static std::string format_key_message(const std::string &message, const toml::key &ky, const TomlBasicValue &value) {
auto loc = value.location(); auto loc = value.location();
auto line_number_str = std::to_string(loc.line()); auto line_number_str = std::to_string(loc.line());
auto line_width = line_number_str.length(); auto line_width = line_number_str.length();
auto line_str = loc.line_str(); const auto &line_str = loc.line_str();
std::ostringstream oss; std::ostringstream oss;
oss << "[error] " << error << '\n'; oss << message << "\n";
oss << " --> " << loc.file_name() << ':' << loc.line() << '\n'; oss << " --> " << loc.file_name() << ':' << loc.line() << '\n';
oss << std::string(line_width + 2, ' ') << "|\n"; oss << std::string(line_width + 2, ' ') << "|\n";
@@ -48,14 +57,24 @@ static std::string format_key_error(const std::string &error, const toml::key &k
return oss.str(); return oss.str();
} }
static void throw_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
throw std::runtime_error(format_key_message("[error] " + error, ky, value));
}
static void print_key_warning(const std::string &message, const toml::key &ky, const TomlBasicValue &value) {
puts(format_key_message("[warning] " + message, ky, value).c_str());
}
class TomlChecker { class TomlChecker {
const TomlBasicValue &m_v; const TomlBasicValue &m_v;
tsl::ordered_set<toml::key> m_visited; tsl::ordered_set<toml::key> m_visited;
tsl::ordered_set<toml::key> m_conditionVisited; tsl::ordered_set<toml::key> m_conditionVisited;
public: public:
TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {} 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(const TomlChecker &) = delete;
TomlChecker(TomlChecker &&) = delete; TomlChecker(TomlChecker &&) = delete;
@@ -109,36 +128,40 @@ class TomlChecker {
return toml::find(m_v, ky); return toml::find(m_v, ky);
} }
void visit(const toml::key &ky) { m_visited.emplace(ky); } void visit(const toml::key &ky) {
m_visited.emplace(ky);
}
bool visisted(const toml::key &ky) const { return m_visited.contains(ky); } bool visisted(const toml::key &ky) const {
return m_visited.contains(ky);
}
void check(const tsl::ordered_map<std::string, std::string> &conditions) const { void check(const tsl::ordered_map<std::string, std::string> &conditions) const {
for (const auto &itr : m_v.as_table()) { for (const auto &itr : m_v.as_table()) {
const auto &ky = itr.first; const auto &ky = itr.first;
if (m_conditionVisited.contains(ky)) { if (m_conditionVisited.contains(ky)) {
if (!conditions.contains(ky)) { if (!conditions.contains(ky) && Project::is_condition_name(ky)) {
throw std::runtime_error(format_key_error("Unknown condition '" + ky + "'", ky, itr.second)); throw_key_error("Unknown condition '" + ky + "'", ky, itr.second);
} }
for (const auto &jtr : itr.second.as_table()) { for (const auto &jtr : itr.second.as_table()) {
if (!m_visited.contains(jtr.first)) { if (!m_visited.contains(jtr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second)); throw_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second);
} }
} }
} else if (!m_visited.contains(ky)) { } else if (!m_visited.contains(ky)) {
if (itr.second.is_table()) { if (itr.second.is_table()) {
for (const auto &jtr : itr.second.as_table()) { for (const auto &jtr : itr.second.as_table()) {
if (!m_visited.contains(jtr.first)) { if (!m_visited.contains(jtr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second)); throw_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second);
} }
} }
} }
throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second)); throw_key_error("Unknown key '" + ky + "'", ky, itr.second);
} else if (ky == "condition") { } else if (ky == "condition") {
std::string condition = itr.second.as_string(); std::string condition = itr.second.as_string();
if (!conditions.contains(condition)) { if (!conditions.contains(condition) && Project::is_condition_name(condition)) {
throw std::runtime_error(format_key_error("Unknown condition '" + condition + "'", condition, itr.second)); throw_key_error("Unknown condition '" + condition + "'", condition, itr.second);
} }
} }
} }
@@ -152,7 +175,8 @@ class TomlCheckerRoot {
bool m_checked = false; bool m_checked = false;
public: public:
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {} explicit TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
}
TomlCheckerRoot(const TomlCheckerRoot &) = delete; TomlCheckerRoot(const TomlCheckerRoot &) = delete;
TomlCheckerRoot(TomlCheckerRoot &&) = delete; TomlCheckerRoot(TomlCheckerRoot &&) = delete;
@@ -175,7 +199,7 @@ class TomlCheckerRoot {
if (check_root) { if (check_root) {
for (const auto &itr : m_root.as_table()) { for (const auto &itr : m_root.as_table()) {
if (!m_visisted.contains(itr.first)) { if (!m_visisted.contains(itr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + itr.first + "'", itr.first, itr.second)); throw_key_error("Unknown key '" + itr.first + "'", itr.first, itr.second);
} }
} }
} }
@@ -185,7 +209,7 @@ class TomlCheckerRoot {
} }
}; };
Project::Project(const Project *parent, const std::string &path, bool build) { Project::Project(const Project *parent, const std::string &path, bool build) : parent(parent) {
const auto toml_path = fs::path(path) / "cmake.toml"; const auto toml_path = fs::path(path) / "cmake.toml";
if (!fs::exists(toml_path)) { if (!fs::exists(toml_path)) {
throw std::runtime_error("File not found '" + toml_path.string() + "'"); throw std::runtime_error("File not found '" + toml_path.string() + "'");
@@ -203,7 +227,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
cmake.required("version", cmake_version); cmake.required("version", cmake_version);
if (cmake.contains("bin-dir")) { if (cmake.contains("bin-dir")) {
throw std::runtime_error("bin-dir has been renamed to build-dir"); throw_key_error("bin-dir has been renamed to build-dir", "bin-dir", cmake.find("bin-dir"));
} }
cmake.optional("build-dir", build_dir); cmake.optional("build-dir", build_dir);
@@ -241,11 +265,22 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake"; conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake";
conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")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["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake";
conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake";
conditions["msvc"] = R"cmake(MSVC)cmake"; conditions["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["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["root"] = R"cmake(CMKR_ROOT_PROJECT)cmake";
conditions["x64"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 8)cmake"; conditions["x64"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 8)cmake";
conditions["x32"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 4)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 { } else {
conditions = parent->conditions; conditions = parent->conditions;
templates = parent->templates; templates = parent->templates;
@@ -254,6 +289,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
if (checker.contains("conditions")) { if (checker.contains("conditions")) {
auto conds = toml::find<decltype(conditions)>(toml, "conditions"); auto conds = toml::find<decltype(conditions)>(toml, "conditions");
for (const auto &cond : conds) { 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; conditions[cond.first] = cond.second;
} }
} }
@@ -263,12 +301,33 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
project.required("name", project_name); project.required("name", project_name);
project.optional("version", project_version); project.optional("version", project_version);
project.optional("description", project_description); project.optional("description", project_description);
// The implicit default is ["C", "CXX"], so make sure this list isn't
// empty or projects without languages explicitly defined will error.
project_languages[""] = {"C", "CXX"};
project.optional("languages", project_languages); project.optional("languages", project_languages);
project.optional("allow-unknown-languages", project_allow_unknown_languages);
project.optional("cmake-before", cmake_before); project.optional("cmake-before", cmake_before);
project.optional("cmake-after", cmake_after); project.optional("cmake-after", cmake_after);
project.optional("include-before", include_before); project.optional("include-before", include_before);
project.optional("include-after", include_after); project.optional("include-after", include_after);
project.optional("subdirs", project_subdirs); project.optional("subdirs", project_subdirs);
std::string msvc_runtime;
project.optional("msvc-runtime", msvc_runtime);
if (!msvc_runtime.empty()) {
project_msvc_runtime = parse_msvcRuntimeType(msvc_runtime);
if (project_msvc_runtime == msvc_last) {
std::string error = "Unknown runtime '" + msvc_runtime + "'\n";
error += "Available types:\n";
for (std::string type_name : msvcRuntimeTypeNames) {
error += " - " + type_name + "\n";
}
error.pop_back(); // Remove last newline
throw_key_error(error, msvc_runtime, project.find("msvc-runtime"));
}
}
} }
if (checker.contains("subdir")) { if (checker.contains("subdir")) {
@@ -288,36 +347,65 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
} }
} }
if (checker.contains("settings")) { if (checker.contains("variables")) {
using set_map = tsl::ordered_map<std::string, TomlBasicValue>; using set_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &sets = toml::find<set_map>(toml, "settings"); auto vars = toml::find<set_map>(toml, "variables");
for (const auto &itr : sets) { if (checker.contains("settings")) {
Setting s; print_key_warning("[settings] has been renamed to [variables]", "settings", toml.at("settings"));
const auto &sets = toml::find<set_map>(toml, "settings");
for (const auto &itr : sets) {
if (!vars.insert(itr).second) {
throw_key_error("Key '" + itr.first + "' shadows existing variable", itr.first, itr.second);
}
}
}
for (const auto &itr : vars) {
Variable s;
s.name = itr.first; s.name = itr.first;
const auto &value = itr.second; const auto &value = itr.second;
if (value.is_boolean()) { if (value.is_boolean()) {
s.val = value.as_boolean(); s.value = value.as_boolean();
} else if (value.is_string()) { } else if (value.is_string()) {
s.val = value.as_string(); s.value = value.as_string();
} else { } else {
auto &setting = checker.create(value); auto &setting = checker.create(value);
setting.optional("comment", s.comment); setting.optional("help", s.help);
if (setting.contains("value")) { if (setting.contains("value")) {
const auto &v = setting.find("value"); const auto &v = setting.find("value");
if (v.is_boolean()) { if (v.is_boolean()) {
s.val = v.as_boolean(); s.value = v.as_boolean();
} else { } else {
s.val = v.as_string(); s.value = v.as_string();
} }
} }
setting.optional("cache", s.cache); setting.optional("cache", s.cache);
setting.optional("force", s.force); setting.optional("force", s.force);
} }
settings.push_back(s); variables.push_back(s);
} }
} }
if (checker.contains("options")) { 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>; using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &opts = toml::find<opts_map>(toml, "options"); const auto &opts = toml::find<opts_map>(toml, "options");
for (const auto &itr : opts) { for (const auto &itr : opts) {
@@ -325,13 +413,52 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
o.name = itr.first; o.name = itr.first;
const auto &value = itr.second; const auto &value = itr.second;
if (value.is_boolean()) { if (value.is_boolean()) {
o.val = value.as_boolean(); o.value = value.as_boolean();
} else { } else if (value.is_string()) {
auto str = std::string(value.as_string());
if (str == "root") {
o.value = std::string("${CMKR_ROOT_PROJECT}");
} else {
throw_key_error("Unsupported option value '" + str + "'", str, value);
}
} else if (value.is_table()) {
auto &option = checker.create(value); auto &option = checker.create(value);
option.optional("comment", o.comment); option.optional("help", o.help);
option.optional("value", o.val); if (option.contains("value")) {
const auto &ovalue = option.find("value");
if (ovalue.is_boolean()) {
o.value = ovalue.as_boolean();
} else if (ovalue.is_string()) {
auto str = std::string(ovalue.as_string());
if (str == "root") {
o.value = std::string("${CMKR_ROOT_PROJECT}");
} else {
throw_key_error("Unsupported option value '" + str + "'", str, value);
}
} else {
throw_key_error(toml::concat_to_string("Unsupported value type: ", ovalue.type()), "value", value);
}
}
} else {
throw_key_error(toml::concat_to_string("Unsupported value type: ", itr.second.type()), itr.first, itr.second);
} }
options.push_back(o); options.push_back(o);
// Add a condition matching the option name
if (!conditions.emplace(o.name, o.name).second) {
print_key_warning("Option '" + o.name + "' would create a condition '" + o.name + "' that already exists", o.name, value);
}
// Add an implicit condition for the option
auto ncondition = normalize(o.name);
if (ncondition.find(nproject_prefix) == 0) {
ncondition = ncondition.substr(nproject_prefix.size());
}
if (!ncondition.empty() && ncondition != o.name) {
if (!conditions.emplace(ncondition, o.name).second) {
print_key_warning("Option '" + o.name + "' would create a condition '" + ncondition + "' that already exists", o.name, value);
}
}
} }
} }
@@ -368,6 +495,15 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
c.optional("cmake-after", content.cmake_after); c.optional("cmake-after", content.cmake_after);
c.optional("include-before", content.include_before); c.optional("include-before", content.include_before);
c.optional("include-after", content.include_after); 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()) { for (const auto &argItr : itr.second.as_table()) {
std::string value; std::string value;
@@ -413,17 +549,30 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
key = "URL"; key = "URL";
} else if (hash_algorithms.contains(key)) { } else if (hash_algorithms.contains(key)) {
std::string algo; std::string algo;
for (auto c : key) { for (auto ch : key) {
algo.push_back(std::toupper(c)); if (ch >= 'a' && ch <= 'z') {
ch -= ('a' - 'A');
}
algo.push_back(ch);
} }
key = "URL_HASH"; 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; value = algo + "=" + value;
} else if (key == "hash") { } else if (key == "hash") {
key = "URL_HASH"; key = "URL_HASH";
} else if (key == "subdir") {
key = "SOURCE_SUBDIR";
} else if (is_cmake_arg(key)) { } else if (is_cmake_arg(key)) {
// allow passthrough of ExternalProject options // allow passthrough of ExternalProject options
} else if (!c.visisted(key)) { } else if (!c.visisted(key)) {
throw std::runtime_error(format_key_error("Unknown key '" + argItr.first + "'", argItr.first, argItr.second)); throw_key_error("Unknown key '" + argItr.first + "'", argItr.first, argItr.second);
} }
// Make sure not to emit keys like "condition" in the FetchContent call // Make sure not to emit keys like "condition" in the FetchContent call
@@ -438,7 +587,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
} }
if (checker.contains("bin")) { if (checker.contains("bin")) {
throw std::runtime_error("[[bin]] has been renamed to [[target]]"); throw_key_error("[[bin]] has been renamed to [target.<name>]", "", toml.at("bin"));
} }
auto parse_target = [&](const std::string &name, TomlChecker &t, bool isTemplate) { auto parse_target = [&](const std::string &name, TomlChecker &t, bool isTemplate) {
@@ -477,12 +626,21 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
} }
} }
error.pop_back(); // Remove last newline error.pop_back(); // Remove last newline
throw std::runtime_error(format_key_error(error, target.type_name, t.find("type"))); throw_key_error(error, target.type_name, t.find("type"));
} }
t.optional("headers", target.headers);
t.optional("sources", target.sources); t.optional("sources", target.sources);
// Merge the headers into the sources
ConditionVector headers;
t.optional("headers", headers);
for (const auto &itr : headers) {
auto &dest = target.sources[itr.first];
for (const auto &jtr : itr.second) {
dest.push_back(jtr);
}
}
t.optional("compile-definitions", target.compile_definitions); t.optional("compile-definitions", target.compile_definitions);
t.optional("private-compile-definitions", target.private_compile_definitions); t.optional("private-compile-definitions", target.private_compile_definitions);
@@ -501,16 +659,71 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
t.optional("link-libraries", target.link_libraries); t.optional("link-libraries", target.link_libraries);
t.optional("private-link-libraries", target.private_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("link-options", target.link_options);
t.optional("private-link-options", target.private_link_options); t.optional("private-link-options", target.private_link_options);
t.optional("precompile-headers", target.precompile_headers); t.optional("precompile-headers", target.precompile_headers);
t.optional("private-precompile-headers", target.private_precompile_headers); t.optional("private-precompile-headers", target.private_precompile_headers);
if (!target.headers.empty()) { t.optional("dependencies", target.dependencies);
auto &sources = target.sources.nth(0).value();
const auto &headers = target.headers.nth(0)->second; Condition<std::string> msvc_runtime;
sources.insert(sources.end(), headers.begin(), headers.end()); t.optional("msvc-runtime", msvc_runtime);
for (const auto &cond_itr : msvc_runtime) {
switch (parse_msvcRuntimeType(cond_itr.second)) {
case msvc_dynamic:
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
break;
case msvc_static:
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
break;
default: {
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 (cond_itr.first.empty()) {
report = &t.find("msvc-runtime");
} else {
report = &t.find(cond_itr.first).as_table().find("msvc-runtime").value();
}
throw_key_error(error, cond_itr.second, *report);
}
}
} }
t.optional("condition", target.condition); t.optional("condition", target.condition);
@@ -527,6 +740,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
property_list += list_val.as_string(); property_list += list_val.as_string();
} }
target.properties[condition][k] = property_list; 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()) { } else if (v.is_boolean()) {
target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF"; target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
} else { } else {
@@ -564,13 +779,13 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
for (const auto &type_name : targetTypeNames) { for (const auto &type_name : targetTypeNames) {
if (name == type_name) { if (name == type_name) {
throw std::runtime_error(format_key_error("Reserved template name '" + name + "'", name, itr.second)); throw_key_error("Reserved template name '" + name + "'", name, itr.second);
} }
} }
for (const auto &tmplate : templates) { for (const auto &tmplate : templates) {
if (name == tmplate.outline.name) { if (name == tmplate.outline.name) {
throw std::runtime_error(format_key_error("Template '" + name + "' already defined", name, itr.second)); throw_key_error("Template '" + name + "' already defined", name, itr.second);
} }
} }
@@ -578,7 +793,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
tmplate.outline = parse_target(name, t, true); tmplate.outline = parse_target(name, t, true);
t.optional("add-function", tmplate.add_function); 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-to-add-function", tmplate.pass_sources_to_add_function);
t.optional("pass-sources", tmplate.pass_sources_to_add_function);
templates.push_back(tmplate); templates.push_back(tmplate);
} }
@@ -619,6 +836,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
i.optional("configs", inst.configs); i.optional("configs", inst.configs);
i.required("destination", inst.destination); i.required("destination", inst.destination);
i.optional("component", inst.component); i.optional("component", inst.component);
i.optional("optional", inst.optional);
installs.push_back(inst); installs.push_back(inst);
} }
} }
@@ -627,6 +845,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
auto &v = checker.create(toml, "vcpkg"); auto &v = checker.create(toml, "vcpkg");
v.optional("url", vcpkg.url); v.optional("url", vcpkg.url);
v.optional("version", vcpkg.version); v.optional("version", vcpkg.version);
for (const auto &p : v.find("packages").as_array()) { for (const auto &p : v.find("packages").as_array()) {
Vcpkg::Package package; Vcpkg::Package package;
const auto &package_str = p.as_string().str; const auto &package_str = p.as_string().str;
@@ -640,18 +859,73 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
std::istringstream feature_stream{features}; std::istringstream feature_stream{features};
std::string feature; std::string feature;
while (std::getline(feature_stream, 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 { } else {
throw std::runtime_error("Invalid vcpkg package '" + package_str + "'"); throw_key_error("Invalid package name '" + package_str + "'", "packages", p);
} }
vcpkg.packages.emplace_back(std::move(package)); 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); checker.check(conditions, true);
} }
const Project *Project::root() const {
auto root = this;
while (root->parent != nullptr)
root = root->parent;
return root;
}
bool Project::cmake_minimum_version(int major, int minor) const {
// NOTE: this code is like pulling teeth, sorry
auto root_version = root()->cmake_version;
auto range_index = root_version.find("...");
if (range_index != std::string::npos) {
root_version.resize(range_index);
}
auto period_index = root_version.find('.');
auto root_major = atoi(root_version.substr(0, period_index).c_str());
int root_minor = 0;
if (period_index != std::string::npos) {
auto end_index = root_version.find('.', period_index + 1);
root_minor = atoi(root_version.substr(period_index + 1, end_index).c_str());
}
return std::tie(root_major, root_minor) >= std::tie(major, minor);
}
bool 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) { bool is_root_path(const std::string &path) {
const auto toml_path = fs::path(path) / "cmake.toml"; const auto toml_path = fs::path(path) / "cmake.toml";
if (!fs::exists(toml_path)) { if (!fs::exists(toml_path)) {
+1
View File
@@ -1,3 +1,4 @@
# These will be generated by cmkr, so no point in tracking them # These will be generated by cmkr, so no point in tracking them
**/CMakeLists.txt **/CMakeLists.txt
**/cmkr.cmake **/cmkr.cmake
**/vcpkg.json
+51 -8
View File
@@ -14,7 +14,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/basic" "${CMAKE_CURRENT_LIST_DIR}/basic"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -24,7 +24,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/interface" "${CMAKE_CURRENT_LIST_DIR}/interface"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -34,7 +34,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/fetch-content" "${CMAKE_CURRENT_LIST_DIR}/fetch-content"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -44,7 +44,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/conditions" "${CMAKE_CURRENT_LIST_DIR}/conditions"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -54,7 +54,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/vcpkg" "${CMAKE_CURRENT_LIST_DIR}/vcpkg"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -64,7 +64,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/cxx-standard" "${CMAKE_CURRENT_LIST_DIR}/cxx-standard"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -74,7 +74,7 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/globbing" "${CMAKE_CURRENT_LIST_DIR}/globbing"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
@@ -84,7 +84,50 @@ add_test(
WORKING_DIRECTORY WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/templates" "${CMAKE_CURRENT_LIST_DIR}/templates"
COMMAND COMMAND
$<TARGET_FILE:cmkr> "$<TARGET_FILE:cmkr>"
build build
) )
if(MSVC) # msvc
add_test(
NAME
msvc-runtime
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/msvc-runtime"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
endif()
add_test(
NAME
compile-options
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/compile-options"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
if(WIN32) # windows
add_test(
NAME
relative-paths
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/relative-paths"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
endif()
add_test(
NAME
objective-c
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/objective-c"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
+26
View File
@@ -45,3 +45,29 @@ name = "templates"
working-directory = "templates" working-directory = "templates"
command = "$<TARGET_FILE:cmkr>" command = "$<TARGET_FILE:cmkr>"
arguments = ["build"] arguments = ["build"]
[[test]]
condition = "msvc"
name = "msvc-runtime"
working-directory = "msvc-runtime"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "compile-options"
working-directory = "compile-options"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
condition = "windows"
name = "relative-paths"
working-directory = "relative-paths"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "objective-c"
working-directory = "objective-c"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
+18
View File
@@ -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.
+5
View File
@@ -0,0 +1,5 @@
#include <cstdio>
int main() {
puts("Hello from cmkr!");
}
+5
View File
@@ -2,6 +2,9 @@
name = "conditions" name = "conditions"
cmake-after = "set(CUSTOM ON)" cmake-after = "set(CUSTOM ON)"
[options]
CONDITIONS_BUILD_TESTS = "root"
[conditions] [conditions]
custom = "CUSTOM" custom = "CUSTOM"
@@ -15,6 +18,8 @@ macos.cmake-after = "message(STATUS macos-after)"
linux.cmake-after = "message(STATUS linux-after)" linux.cmake-after = "message(STATUS linux-after)"
unix.cmake-after = "message(STATUS unix-after)" unix.cmake-after = "message(STATUS unix-after)"
custom.cmake-after = "message(STATUS custom-after)" custom.cmake-after = "message(STATUS custom-after)"
build-tests.cmake-after = "message(STATUS build-tests)"
"CONDITIONS_BUILD_TESTS AND $<linux>".cmake-after = "message(STATUS linux-tests)"
[target.example.properties] [target.example.properties]
AUTOMOC = false AUTOMOC = false
+2 -1
View File
@@ -1 +1,2 @@
int main() { } int main() {
}
@@ -1,3 +1,4 @@
#include <Windows.h> #include <Windows.h>
void foo() { } void foo() {
}
+1 -2
View File
@@ -1,8 +1,7 @@
#include <cstdio> #include <cstdio>
#include <tuple> #include <tuple>
int main() int main() {
{
auto tpl = std::make_tuple(1, 2); auto tpl = std::make_tuple(1, 2);
printf("Hello from C++11 %d\n", std::get<0>(tpl)); printf("Hello from C++11 %d\n", std::get<0>(tpl));
} }
+3 -2
View File
@@ -4,8 +4,9 @@
name = "fetch-content" name = "fetch-content"
description = "Fetching from git" description = "Fetching from git"
[fetch-content] [fetch-content.fmt]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" } git = "https://github.com/fmtlib/fmt"
tag = "7.1.3"
[target.example] [target.example]
type = "executable" type = "executable"
+1 -2
View File
@@ -1,6 +1,5 @@
#include <fmt/core.h> #include <fmt/core.h>
int main() int main() {
{
fmt::print("Hello, world!\n"); fmt::print("Hello, world!\n");
} }
+2 -3
View File
@@ -2,7 +2,6 @@
#include <string> #include <string>
namespace mylib namespace mylib {
{
std::string message(); std::string message();
} } // namespace mylib
+1 -2
View File
@@ -1,6 +1,5 @@
#include <mylib/mylib.hpp> #include <mylib/mylib.hpp>
std::string mylib::message() std::string mylib::message() {
{
return "cmkr is awesome!"; return "cmkr is awesome!";
} }
+4 -3
View File
@@ -1,4 +1,5 @@
namespace mylib namespace mylib {
{ static const char *version() {
static const char* version() { return "v1.0"; } return "v1.0";
}
} // namespace mylib } // namespace mylib
+2 -3
View File
@@ -2,7 +2,6 @@
#include "mylib/mylib.hpp" #include "mylib/mylib.hpp"
int main() int main() {
{ printf("mylib version: %s\n", mylib::version());
printf("mylib version: %s\n", mylib::version())
} }
+15
View File
@@ -0,0 +1,15 @@
[project]
name = "msvc-runtime"
description = "Static MSVC runtime"
msvc-runtime = "static"
# This target will compile with a static runtime
[target.static-runtime]
type = "executable"
sources = ["src/main.cpp"]
# This target overrides the [project].msvc-runtime
[target.dynamic-runtime]
type = "executable"
sources = ["src/main.cpp"]
msvc-runtime = "dynamic"
+5
View File
@@ -0,0 +1,5 @@
#include <cstdio>
int main() {
puts("Hello from cmkr(msvc-static)!");
}
+13
View File
@@ -0,0 +1,13 @@
# Add Objective-C sources on Apple platforms:
[project]
name = "objective-c"
description = "Objective-C"
languages = ["C"]
apple.languages = ["OBJC"]
[target.hello]
type = "executable"
sources = ["src/main.c"]
apple.sources = ["src/apple.m"]
apple.link-libraries = ["$<LINK_LIBRARY:FRAMEWORK,Foundation>"]
+7
View File
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
void platform_specific() {
NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
NSString *message = @"Hello from Objective-C!\n";
[stdout writeData:[message dataUsingEncoding:NSUTF8StringEncoding] error:nil];
}
+14
View File
@@ -0,0 +1,14 @@
#include <stdio.h>
#ifdef __APPLE__
void platform_specific();
#else
void platform_specific() {
puts("This is not an Apple platform.");
}
#endif
int main() {
platform_specific();
return 0;
}
+11
View File
@@ -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"]
@@ -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!");
}
+12
View File
@@ -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
}
+4 -4
View File
@@ -8,11 +8,11 @@ description = "Target templates"
type = "executable" type = "executable"
sources = ["src/templates.cpp"] sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"] compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties # Unlike interface targets you can also inherit properties
[template.app.properties] properties = {
CXX_STANDARD = "11" CXX_STANDARD = "11",
CXX_STANDARD_REQUIRED = true CXX_STANDARD_REQUIRED = true,
}
[target.app-a] [target.app-a]
type = "app" type = "app"
+4 -5
View File
@@ -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] [project]
name = "vcpkg" name = "vcpkg"
description = "Dependencies from vcpkg" description = "Dependencies from vcpkg"
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions # See https://github.com/microsoft/vcpkg/releases for vcpkg versions
# See https://vcpkg.io/en/packages.html for available packages # See https://vcpkg.io/en/packages or https://vcpkg.link for available packages
[vcpkg] [vcpkg]
version = "2021.05.12" version = "2024.11.16"
packages = ["fmt"] packages = ["fmt"]
[find-package] [find-package.fmt]
fmt = {}
[target.example] [target.example]
type = "executable" type = "executable"
+1 -2
View File
@@ -1,6 +1,5 @@
#include <fmt/core.h> #include <fmt/core.h>
int main() int main() {
{
fmt::print("Hello, world!\n"); fmt::print("Hello, world!\n");
} }
-11
View File
@@ -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": ""
}
+3
View File
@@ -0,0 +1,3 @@
# Disable clang-format in this folder
DisableFormat: true
SortIncludes: Never
Generated Vendored
+2 -5
View File
@@ -11,12 +11,9 @@ target_compile_definitions(ordered_map INTERFACE NOMINMAX)
# https://github.com/ToruNiina/toml11 (MIT) # https://github.com/ToruNiina/toml11 (MIT)
add_library(toml11 INTERFACE) add_library(toml11 INTERFACE)
target_include_directories(toml11 INTERFACE toml11-3.6.0) target_include_directories(toml11 INTERFACE toml11-3.8.1)
target_compile_definitions(toml11 INTERFACE TOML11_USE_UNRELEASED_TOML_FEATURES)
# https://github.com/mpark/variant (BSL-1.0) # https://github.com/mpark/variant (BSL-1.0)
add_library(mpark_variant INTERFACE) add_library(mpark_variant INTERFACE)
target_include_directories(mpark_variant INTERFACE variant-1.4.0/include) target_include_directories(mpark_variant INTERFACE variant-1.4.0/include)
# https://github.com/nlohmann/json (MIT)
add_library(nlohmann_json INTERFACE)
target_include_directories(nlohmann_json INTERFACE nlohmann-3.9.1/include)
-21
View File
@@ -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.
File diff suppressed because it is too large Load Diff
@@ -25,21 +25,14 @@
#ifndef TOML_FOR_MODERN_CPP #ifndef TOML_FOR_MODERN_CPP
#define TOML_FOR_MODERN_CPP #define TOML_FOR_MODERN_CPP
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
#if __cplusplus < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#define TOML11_VERSION_MAJOR 3 #define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 5 #define TOML11_VERSION_MINOR 8
#define TOML11_VERSION_PATCH 0 #define TOML11_VERSION_PATCH 1
#include "toml/parser.hpp" #include "toml/parser.hpp"
#include "toml/literal.hpp" #include "toml/literal.hpp"
#include "toml/serializer.hpp" #include "toml/serializer.hpp"
#include "toml/get.hpp" #include "toml/get.hpp"
#include "toml/macros.hpp"
#endif// TOML_FOR_MODERN_CPP #endif// TOML_FOR_MODERN_CPP
@@ -17,11 +17,41 @@ namespace color_ansi
{ {
namespace detail namespace detail
{ {
inline int colorize_index() inline int colorize_index()
{ {
static const int index = std::ios_base::xalloc(); static const int index = std::ios_base::xalloc();
return index; return index;
} }
// Control color mode globally
class color_mode
{
public:
inline void enable()
{
should_color_ = true;
}
inline void disable()
{
should_color_ = false;
}
inline bool should_color() const
{
return should_color_;
}
static color_mode& status()
{
static color_mode status_;
return status_;
}
private:
bool should_color_ = false;
};
} // detail } // detail
inline std::ostream& colorize(std::ostream& os) inline std::ostream& colorize(std::ostream& os)
@@ -55,6 +85,21 @@ inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} {if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os) inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} {if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
inline void enable()
{
return detail::color_mode::status().enable();
}
inline void disable()
{
return detail::color_mode::status().disable();
}
inline bool should_color()
{
return detail::color_mode::status().should_color();
}
} // color_ansi } // color_ansi
// ANSI escape sequence is the only and default colorization method currently // ANSI escape sequence is the only and default colorization method currently
@@ -29,7 +29,7 @@ namespace detail
// to output character as an error message. // to output character as an error message.
inline std::string show_char(const char c) inline std::string show_char(const char c)
{ {
// It supress an error that occurs only in Debug mode of MSVC++ on Windows. // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
// I'm not completely sure but they check the value of char to be in the // I'm not completely sure but they check the value of char to be in the
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
// has negative value (if char has sign). So here it re-interprets c as // has negative value (if char has sign). So here it re-interprets c as
@@ -154,7 +154,7 @@ struct sequence<Head, Tail...>
invoke(location& loc) invoke(location& loc)
{ {
const auto first = loc.iter(); const auto first = loc.iter();
const auto rslt = Head::invoke(loc); auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.reset(first); loc.reset(first);
@@ -4,11 +4,18 @@
#define TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
#include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
#else
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
#endif
// This file provides mainly two classes, `preserve_comments` and `discard_comments`. // This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>` // Those two are a container that have the same interface as `std::vector<std::string>`
// but bahaves in the opposite way. `preserve_comments` is just the same as // but bahaves in the opposite way. `preserve_comments` is just the same as
@@ -339,7 +346,7 @@ operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::differ
// //
// Why this is chose as the default type is because the last version (2.x.y) // Why this is chose as the default type is because the last version (2.x.y)
// does not contain any comments in a value. To minimize the impact on the // does not contain any comments in a value. To minimize the impact on the
// efficiency, this is choosed as a default. // efficiency, this is chosen as a default.
// //
// To reduce the memory footprint, later we can try empty base optimization (EBO). // To reduce the memory footprint, later we can try empty base optimization (EBO).
struct discard_comments struct discard_comments
@@ -418,14 +425,14 @@ struct discard_comments
// empty, so accessing through operator[], front/back, data causes address // empty, so accessing through operator[], front/back, data causes address
// error. // error.
reference operator[](const size_type) noexcept {return *data();} reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
const_reference operator[](const size_type) const noexcept {return *data();} const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
reference front() noexcept {return *data();} reference front() noexcept {never_call("toml::discard_comment::front");}
const_reference front() const noexcept {return *data();} const_reference front() const noexcept {never_call("toml::discard_comment::front");}
reference back() noexcept {return *data();} reference back() noexcept {never_call("toml::discard_comment::back");}
const_reference back() const noexcept {return *data();} const_reference back() const noexcept {never_call("toml::discard_comment::back");}
pointer data() noexcept {return nullptr;} pointer data() noexcept {return nullptr;}
const_pointer data() const noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;}
@@ -443,6 +450,18 @@ struct discard_comments
const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};}
const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
const_reverse_iterator crend() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};}
private:
[[noreturn]] static void never_call(const char *const this_function)
{
#ifdef __has_builtin
# if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
# endif
#endif
throw std::logic_error{this_function};
}
}; };
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
@@ -21,22 +21,7 @@ namespace toml
namespace detail namespace detail
{ {
// TODO: find more sophisticated way to handle this // TODO: find more sophisticated way to handle this
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) #if defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -51,6 +36,21 @@ inline std::tm gmtime_s(const std::time_t* src)
if (result) { throw std::runtime_error("gmtime_s failed."); } if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst; return dst;
} }
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#else // fallback. not threadsafe #else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
@@ -85,9 +85,9 @@ enum class month_t : std::uint8_t
struct local_date struct local_date
{ {
std::int16_t year; // A.D. (like, 2018) std::int16_t year{}; // A.D. (like, 2018)
std::uint8_t month; // [0, 11] std::uint8_t month{}; // [0, 11]
std::uint8_t day; // [1, 31] std::uint8_t day{}; // [1, 31]
local_date(int y, month_t m, int d) local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)), : year (static_cast<std::int16_t>(y)),
@@ -181,12 +181,12 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
struct local_time struct local_time
{ {
std::uint8_t hour; // [0, 23] std::uint8_t hour{}; // [0, 23]
std::uint8_t minute; // [0, 59] std::uint8_t minute{}; // [0, 59]
std::uint8_t second; // [0, 60] std::uint8_t second{}; // [0, 60]
std::uint16_t millisecond; // [0, 999] std::uint16_t millisecond{}; // [0, 999]
std::uint16_t microsecond; // [0, 999] std::uint16_t microsecond{}; // [0, 999]
std::uint16_t nanosecond; // [0, 999] std::uint16_t nanosecond{}; // [0, 999]
local_time(int h, int m, int s, local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0) int ms = 0, int us = 0, int ns = 0)
@@ -297,8 +297,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
struct time_offset struct time_offset
{ {
std::int8_t hour; // [-12, 12] std::int8_t hour{}; // [-12, 12]
std::int8_t minute; // [-59, 59] std::int8_t minute{}; // [-59, 59]
time_offset(int h, int m) time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)), : hour (static_cast<std::int8_t>(h)),
@@ -364,8 +364,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
struct local_datetime struct local_datetime
{ {
local_date date; local_date date{};
local_time time; local_time time{};
local_datetime(local_date d, local_time t): date(d), time(t) {} local_datetime(local_date d, local_time t): date(d), time(t) {}
@@ -478,9 +478,9 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
struct offset_datetime struct offset_datetime
{ {
local_date date; local_date date{};
local_time time; local_time time{};
time_offset offset; time_offset offset{};
offset_datetime(local_date d, local_time t, time_offset o) offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o) : date(d), time(t), offset(o)
@@ -2,14 +2,32 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP #ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP
#include <stdexcept>
#include <array>
#include <string> #include <string>
#include <stdexcept>
#include <cstring>
#include "source_location.hpp" #include "source_location.hpp"
namespace toml namespace toml
{ {
struct file_io_error : public std::runtime_error
{
public:
file_io_error(int errnum, const std::string& msg, const std::string& fname)
: std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)),
errno_(errnum)
{}
int get_errno() const noexcept {return errno_;}
private:
int errno_;
};
struct exception : public std::exception struct exception : public std::exception
{ {
public: public:
@@ -2,7 +2,6 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_FROM_HPP #ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP #define TOML11_FROM_HPP
#include "traits.hpp"
namespace toml namespace toml
{ {
@@ -140,7 +140,7 @@ get(basic_value<C, M, V>&& v)
// ============================================================================ // ============================================================================
// std::string_view // std::string_view
#if __cplusplus >= 201703L #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view> inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
@@ -215,6 +215,7 @@ template<typename T, typename C,
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...) detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...)
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
detail::negation< // not toml::array detail::negation< // not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
@@ -255,19 +256,37 @@ get(const basic_value<C, M, V>&);
// toml::from<T>::from_toml(v) // toml::from<T>::from_toml(v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V>
std::size_t S = sizeof(::toml::from<T>)> detail::enable_if_t<detail::has_specialized_from<T>::value, T>
T get(const basic_value<C, M, V>&); get(const basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value,
// and it does not have `from<T>` nor `from_toml`.
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&> std::is_constructible<T, const basic_value<C, M, V>&>,
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
detail::negation<detail::has_specialized_from<T>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>&); get(const basic_value<C, M, V>&);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, basic_value<C, M, V>&>,
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
detail::negation<detail::has_specialized_from<T>>
>::value, T>
get(basic_value<C, M, V>&);
// ============================================================================ // ============================================================================
// array-like types; most likely STL container, like std::vector, etc. // array-like types; most likely STL container, like std::vector, etc.
@@ -321,6 +340,7 @@ template<typename T, typename C,
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back detail::negation<detail::has_push_back_method<T>>, // w/o push_back
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
detail::negation< // T is not toml::array detail::negation< // T is not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
@@ -338,8 +358,10 @@ get(const basic_value<C, M, V>& v)
{v.location(), "here"} {v.location(), "here"}
})); }));
} }
std::transform(ar.cbegin(), ar.cend(), container.begin(), for(std::size_t i=0; i<ar.size(); ++i)
[](const value& x){return ::toml::get<value_type>(x);}); {
container[i] = ::toml::get<value_type>(ar[i]);
}
return container; return container;
} }
@@ -438,9 +460,16 @@ get(const basic_value<C, M, V>& v)
return ud; return ud;
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V>
std::size_t> detail::enable_if_t<detail::has_specialized_from<T>::value, T>
T get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{
return ::toml::from<T>::from_toml(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(basic_value<C, M, V>& v)
{ {
return ::toml::from<T>::from_toml(v); return ::toml::from<T>::from_toml(v);
} }
@@ -448,14 +477,29 @@ T get(const basic_value<C, M, V>& v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
std::is_constructible<T, const basic_value<C, M, V>&> std::is_constructible<T, const basic_value<C, M, V>&>, // T is constructible from toml::value
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return T(v); return T(v);
} }
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
std::is_constructible<T, basic_value<C, M, V>&>, // T is constructible from toml::value
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
>::value, T>
get(basic_value<C, M, V>& v)
{
return T(v);
}
// ============================================================================ // ============================================================================
// find // find
@@ -1031,6 +1075,50 @@ find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
return get_or(tab.at(ky), std::forward<T>(opt)); return get_or(tab.at(ky), std::forward<T>(opt));
} }
// ---------------------------------------------------------------------------
// recursive find-or with type deduction (find_or(value, keys, opt))
template<typename Value, typename ... Ks,
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
// here we need to add SFINAE in the template parameter to avoid
// infinite recursion in type deduction on gcc
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
-> decltype(find_or(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
{
if(!v.is_table())
{
return detail::last_one(std::forward<Ks>(keys)...);
}
auto&& tab = std::forward<Value>(v).as_table();
if(tab.count(ky) == 0)
{
return detail::last_one(std::forward<Ks>(keys)...);
}
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
}
// ---------------------------------------------------------------------------
// recursive find_or with explicit type specialization, find_or<int>(value, keys...)
template<typename T, typename Value, typename ... Ks,
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
// here we need to add SFINAE in the template parameter to avoid
// infinite recursion in type deduction on gcc
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
-> decltype(find_or<T>(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
{
if(!v.is_table())
{
return detail::last_one(std::forward<Ks>(keys)...);
}
auto&& tab = std::forward<Value>(v).as_table();
if(tab.count(ky) == 0)
{
return detail::last_one(std::forward<Ks>(keys)...);
}
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
}
// ============================================================================ // ============================================================================
// expect // expect
@@ -2,7 +2,6 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_INTO_HPP #ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP #define TOML11_INTO_HPP
#include "traits.hpp"
namespace toml namespace toml
{ {
@@ -5,7 +5,6 @@
#include <istream> #include <istream>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <fstream>
#include "combinator.hpp" #include "combinator.hpp"
@@ -119,8 +118,8 @@ using lex_local_time = lex_partial_time;
// =========================================================================== // ===========================================================================
using lex_quotation_mark = character<'"'>; using lex_quotation_mark = character<'"'>;
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
in_range<0x0a, 0x1F>, // is allowed in_range<0x0A, 0x1F>,
character<0x22>, character<0x5C>, character<0x22>, character<0x5C>,
character<0x7F>>>; character<0x7F>>>;
@@ -133,6 +132,9 @@ using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
character<'b'>, character<'f'>, character<'b'>, character<'f'>,
character<'n'>, character<'r'>, character<'n'>, character<'r'>,
character<'t'>, character<'t'>,
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
character<'e'>, // ESC (0x1B)
#endif
lex_escape_unicode_short, lex_escape_unicode_short,
lex_escape_unicode_long lex_escape_unicode_long
>; >;
@@ -166,7 +168,7 @@ using lex_basic_string = sequence<lex_quotation_mark,
// | ^- expected newline, but got '"'. // | ^- expected newline, but got '"'.
// ``` // ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was // As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`. // split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s. // `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to // In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body. // the string body.
@@ -178,8 +180,8 @@ using lex_ml_basic_string_close = sequence<
maybe<lex_quotation_mark>, maybe<lex_quotation_mark> maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>; >;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
in_range<0x0a, 0x1F>, // is tab in_range<0x0A, 0x1F>,
character<0x5C>, // backslash character<0x5C>, // backslash
character<0x7F>, // DEL character<0x7F>, // DEL
lex_ml_basic_string_delim>>; lex_ml_basic_string_delim>>;
@@ -196,8 +198,8 @@ using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body, lex_ml_basic_body,
lex_ml_basic_string_close>; lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
in_range<0x10, 0x19>, character<0x27>>>; character<0x7F>, character<0x27>>>;
using lex_apostrophe = character<'\''>; using lex_apostrophe = character<'\''>;
using lex_literal_string = sequence<lex_apostrophe, using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>, repeat<lex_literal_char, unlimited>,
@@ -212,7 +214,7 @@ using lex_ml_literal_string_close = sequence<
>; >;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>, in_range<0x0A, 0x1F>,
character<0x7F>, character<0x7F>,
lex_ml_literal_string_delim>>; lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>, using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
@@ -225,12 +227,6 @@ using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>; lex_ml_literal_string, lex_literal_string>;
// =========================================================================== // ===========================================================================
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol = either<character<'\t'>, exclude<in_range<0x00, 0x19>>>;
using lex_comment = sequence<lex_comment_start_symbol,
repeat<lex_non_eol, unlimited>>;
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>; using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit, using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
@@ -265,6 +261,34 @@ using lex_array_table = sequence<lex_array_table_open,
maybe<lex_ws>, maybe<lex_ws>,
lex_array_table_close>; lex_array_table_close>;
using lex_utf8_1byte = in_range<0x00, 0x7F>;
using lex_utf8_2byte = sequence<
in_range<'\xC2', '\xDF'>,
in_range<'\x80', '\xBF'>
>;
using lex_utf8_3byte = sequence<either<
sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>,
sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>,
sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>>
>, in_range<'\x80', '\xBF'>>;
using lex_utf8_4byte = sequence<either<
sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>,
sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>>
>, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>;
using lex_utf8_code = either<
lex_utf8_1byte,
lex_utf8_2byte,
lex_utf8_3byte,
lex_utf8_4byte
>;
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>;
using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
} // detail } // detail
} // toml } // toml
#endif // TOML_LEXER_HPP #endif // TOML_LEXER_HPP
@@ -12,8 +12,11 @@ inline namespace toml_literals
{ {
// implementation // implementation
inline ::toml::value literal_internal_impl(::toml::detail::location loc) inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{ {
using value_type = ::toml::basic_value<
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them. // if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence< using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>, ::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -50,7 +53,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
// If it is neither a table-key or a array-of-table-key, it may be a value. // If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key) if(!is_table_key && !is_aots_key)
{ {
if(auto data = ::toml::detail::parse_value<::toml::value>(loc)) if(auto data = ::toml::detail::parse_value<value_type>(loc, 0))
{ {
return data.unwrap(); return data.unwrap();
} }
@@ -67,7 +70,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
// It is a valid toml file. // It is a valid toml file.
// It should be parsed as if we parse a file with this content. // It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc)) if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
{ {
return data.unwrap(); return data.unwrap();
} }
@@ -78,11 +81,13 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
} }
inline ::toml::value operator"" _toml(const char* str, std::size_t len) inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{ {
::toml::detail::location loc( ::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"), std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len)); std::vector<char>(str, str + len));
// literal length does not include the null character at the end.
return literal_internal_impl(std::move(loc)); return literal_internal_impl(std::move(loc));
} }
@@ -91,7 +96,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L #if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is // value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char // NOT compatible to char
inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len) inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char8_t* str, std::size_t len)
{ {
::toml::detail::location loc( ::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"), std::string("TOML literal encoded in a C++ code"),
+121
View File
@@ -0,0 +1,121 @@
#ifndef TOML11_MACROS_HPP
#define TOML11_MACROS_HPP
#define TOML11_STRINGIZE_AUX(x) #x
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
#define TOML11_CONCATENATE_AUX(x, y) x##y
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
// ============================================================================
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
// ----------------------------------------------------------------------------
// TOML11_ARGS_SIZE
#define TOML11_INDEX_RSEQ() \
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define TOML11_ARGS_SIZE_IMPL(\
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \
ARG31, ARG32, N, ...) N
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
// ----------------------------------------------------------------------------
// TOML11_FOR_EACH_VA_ARGS
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
// ----------------------------------------------------------------------------
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
// use it in the following way.
// ```cpp
// namespace foo
// {
// struct Foo
// {
// std::string s;
// double d;
// int i;
// };
// } // foo
//
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
// ```
// And then you can use `toml::find<foo::Foo>(file, "foo");`
//
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME;
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
namespace toml { \
template<> \
struct from<NAME> \
{ \
template<typename C, template<typename ...> class T, \
template<typename ...> class A> \
static NAME from_toml(const basic_value<C, T, A>& v) \
{ \
NAME obj; \
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
return obj; \
} \
}; \
template<> \
struct into<NAME> \
{ \
static value into_toml(const NAME& obj) \
{ \
::toml::value v = ::toml::table{}; \
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
return v; \
} \
}; \
} /* toml */
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
#endif// TOML11_MACROS_HPP
File diff suppressed because it is too large Load Diff
@@ -70,16 +70,16 @@ struct region_base
struct location final : public region_base struct location final : public region_base
{ {
using const_iterator = typename std::vector<char>::const_iterator; using const_iterator = typename std::vector<char>::const_iterator;
using difference_type = typename const_iterator::difference_type; using difference_type = typename std::iterator_traits<const_iterator>::difference_type;
using source_ptr = std::shared_ptr<const std::vector<char>>; using source_ptr = std::shared_ptr<const std::vector<char>>;
location(std::string name, std::vector<char> cont) location(std::string source_name, std::vector<char> cont)
: source_(std::make_shared<std::vector<char>>(std::move(cont))), : source_(std::make_shared<std::vector<char>>(std::move(cont))),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin()) line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{} {}
location(std::string name, const std::string& cont) location(std::string source_name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())), : source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin()) line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{} {}
location(const location&) = default; location(const location&) = default;
@@ -92,7 +92,7 @@ struct location final : public region_base
char front() const noexcept override {return *iter_;} char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`. // this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;} std::add_const<const_iterator>::type iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();} const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();} const_iterator end() const noexcept {return source_->cend();}
@@ -227,8 +227,7 @@ struct region final : public region_base
region& operator+=(const region& other) region& operator+=(const region& other)
{ {
// different regions cannot be concatenated // different regions cannot be concatenated
assert(this->begin() == other.begin() && this->end() == other.end() && assert(this->source_ == other.source_ && this->last_ == other.first_);
this->last_ == other.first_);
this->last_ = other.last_; this->last_ = other.last_;
return *this; return *this;
@@ -343,9 +342,9 @@ struct region final : public region_base
})) }))
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter); auto s = make_string(std::next(comment_found), iter);
if(str.back() == '\r') {str.pop_back();} if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(str)); com.push_back(std::move(s));
} }
else else
{ {
@@ -396,9 +395,9 @@ struct region final : public region_base
})) }))
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), this->line_end()); auto s = make_string(std::next(comment_found), this->line_end());
if(str.back() == '\r') {str.pop_back();} if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(str)); com.push_back(std::move(s));
} }
} }
} }
@@ -2,10 +2,19 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP #ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP
#include <cmath>
#include <cstdio> #include <cstdio>
#include <limits> #include <limits>
#if defined(_WIN32)
#include <locale.h>
#elif defined(__APPLE__) || defined(__FreeBSD__)
#include <xlocale.h>
#elif defined(__linux__)
#include <locale.h>
#endif
#include "lexer.hpp" #include "lexer.hpp"
#include "value.hpp" #include "value.hpp"
@@ -26,19 +35,24 @@ namespace toml
// a `"` and escaping some special character is boring. // a `"` and escaping some special character is boring.
template<typename charT, typename traits, typename Alloc> template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc> std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& key) format_key(const std::basic_string<charT, traits, Alloc>& k)
{ {
if(k.empty())
{
return std::string("\"\"");
}
// check the key can be a bare (unquoted) key // check the key can be a bare (unquoted) key
detail::location loc(key, std::vector<char>(key.begin(), key.end())); detail::location loc(k, std::vector<char>(k.begin(), k.end()));
detail::lex_unquoted_key::invoke(loc); detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end()) if(loc.iter() == loc.end())
{ {
return key; // all the tokens are consumed. the key is unquoted-key. return k; // all the tokens are consumed. the key is unquoted-key.
} }
//if it includes special characters, then format it in a "quoted" key. //if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\""); std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : key) for(const char c : k)
{ {
switch(c) switch(c)
{ {
@@ -49,7 +63,19 @@ format_key(const std::basic_string<charT, traits, Alloc>& key)
case '\f': {serialized += "\\f"; break;} case '\f': {serialized += "\\f"; break;}
case '\n': {serialized += "\\n"; break;} case '\n': {serialized += "\\n"; break;}
case '\r': {serialized += "\\r"; break;} case '\r': {serialized += "\\r"; break;}
default : {serialized += c; break;} default: {
if (c >= 0x00 && c < 0x20)
{
std::array<char, 7> buf;
std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast<int>(c));
serialized += buf.data();
}
else
{
serialized += c;
}
break;
}
} }
} }
serialized += "\""; serialized += "\"";
@@ -60,9 +86,12 @@ template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc> std::basic_string<charT, traits, Alloc>
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys) format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{ {
std::basic_string<charT, traits, Alloc> serialized; if(keys.empty())
if(keys.empty()) {return serialized;} {
return std::string("\"\"");
}
std::basic_string<charT, traits, Alloc> serialized;
for(const auto& ky : keys) for(const auto& ky : keys)
{ {
serialized += format_key(ky); serialized += format_key(ky);
@@ -97,8 +126,10 @@ struct serializer
const int float_prec = std::numeric_limits<toml::floating>::max_digits10, const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false, const bool can_be_inlined = false,
const bool no_comment = false, const bool no_comment = false,
std::vector<toml::key> ks = {}) std::vector<toml::key> ks = {},
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment), : can_be_inlined_(can_be_inlined), no_comment_(no_comment),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks)) float_prec_(float_prec), width_(w), keys_(std::move(ks))
{} {}
~serializer() = default; ~serializer() = default;
@@ -109,18 +140,93 @@ struct serializer
} }
std::string operator()(const integer_type i) const std::string operator()(const integer_type i) const
{ {
return std::to_string(i); #if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto str = std::to_string(i);
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
return str;
} }
std::string operator()(const floating_type f) const std::string operator()(const floating_type f) const
{ {
if(std::isnan(f))
{
if(std::signbit(f))
{
return std::string("-nan");
}
else
{
return std::string("nan");
}
}
else if(!std::isfinite(f))
{
if(std::signbit(f))
{
return std::string("-inf");
}
else
{
return std::string("inf");
}
}
// set locale to "C".
// To make it thread-local, we use OS-specific features.
// If we set process-global locale, it can break other thread that also
// outputs something simultaneously.
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto fmt = "%.*g"; const auto fmt = "%.*g";
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
// +1 for null character(\0) // +1 for null character(\0)
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0'); std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
// restore the original locale
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
std::string token(buf.begin(), std::prev(buf.end())); std::string token(buf.begin(), std::prev(buf.end()));
if(token.back() == '.') // 1. => 1.0 if(!token.empty() && token.back() == '.') // 1. => 1.0
{ {
token += '0'; token += '0';
} }
@@ -144,8 +250,9 @@ struct serializer
{ {
if(s.kind == string_t::basic) if(s.kind == string_t::basic)
{ {
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) &&
this->width_ != (std::numeric_limits<std::size_t>::max)())
{ {
// if linefeed or double-quote is contained, // if linefeed or double-quote is contained,
// make it multiline basic string. // make it multiline basic string.
@@ -244,92 +351,18 @@ struct serializer
std::string operator()(const array_type& v) const std::string operator()(const array_type& v) const
{ {
if(!v.empty() && v.front().is_table())// v is an array of tables
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined,
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
if(this->can_be_inlined_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
failed = true;
break;
}
token += t;
token += ",\n";
}
if(!failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
if(v.empty()) if(v.empty())
{ {
return std::string("[]"); return std::string("[]");
} }
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array. // not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment. // first, try to make it inline if none of the elements have a comment.
if(!this->has_comment_inside(v)) if( ! this->has_comment_inside(v))
{ {
const auto inl = this->make_inline_array(v); const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ && if(inl.size() < this->width_ &&
@@ -350,7 +383,7 @@ struct serializer
token += "[\n"; token += "[\n";
for(const auto& item : v) for(const auto& item : v)
{ {
if(!item.comments().empty() && !no_comment_) if( ! item.comments().empty() && !no_comment_)
{ {
// if comment exists, the element must be the only element in the line. // if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed. // e.g. the following is not allowed.
@@ -376,15 +409,25 @@ struct serializer
token += '\n'; token += '\n';
} }
token += toml::visit(*this, item); token += toml::visit(*this, item);
if(token.back() == '\n') {token.pop_back();} if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n"; token += ",\n";
continue; continue;
} }
std::string next_elem; std::string next_elem;
next_elem += toml::visit(*this, item); if(item.is_table())
{
serializer ser(*this);
ser.can_be_inlined_ = true;
ser.width_ = (std::numeric_limits<std::size_t>::max)();
next_elem += toml::visit(ser, item);
}
else
{
next_elem += toml::visit(*this, item);
}
// comma before newline. // comma before newline.
if(next_elem.back() == '\n') {next_elem.pop_back();} if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue. // if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_) if(current_line.size() + next_elem.size() + 1 < this->width_)
@@ -411,7 +454,10 @@ struct serializer
} }
if(!current_line.empty()) if(!current_line.empty())
{ {
if(current_line.back() != '\n') {current_line += '\n';} if(!current_line.empty() && current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line; token += current_line;
} }
token += "]\n"; token += "]\n";
@@ -467,7 +513,19 @@ struct serializer
case '\f': {retval += "\\f"; break;} case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;} case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;} case '\r': {retval += "\\r"; break;}
default : {retval += c; break;} default :
{
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
} }
} }
return retval; return retval;
@@ -501,7 +559,21 @@ struct serializer
} }
break; break;
} }
default: {retval += *i; break;} default :
{
const auto c = *i;
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
} }
} }
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string. // Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
@@ -557,8 +629,10 @@ struct serializer
for(const auto& item : v) for(const auto& item : v)
{ {
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer((std::numeric_limits<std::size_t>::max)(), token += visit(serializer(
this->float_prec_, true), item); (std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
} }
token += ']'; token += ']';
return token; return token;
@@ -577,8 +651,10 @@ struct serializer
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first); token += format_key(kv.first);
token += '='; token += '=';
token += visit(serializer((std::numeric_limits<std::size_t>::max)(), token += visit(serializer(
this->float_prec_, true), kv.second); (std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
} }
token += '}'; token += '}';
return token; return token;
@@ -588,8 +664,16 @@ struct serializer
{ {
std::string token; std::string token;
// print non-table stuff first. because after printing [foo.bar], the // print non-table elements first.
// remaining non-table values will be assigned into [foo.bar], not [foo] // ```toml
// [foo] # a table we're writing now here
// key = "value" # <- non-table element, "key"
// # ...
// [foo.bar] # <- table element, "bar"
// ```
// because after printing [foo.bar], the remaining non-table values will
// be assigned into [foo.bar], not [foo]. Those values should be printed
// earlier.
for(const auto& kv : v) for(const auto& kv : v)
{ {
if(kv.second.is_table() || is_array_of_tables(kv.second)) if(kv.second.is_table() || is_array_of_tables(kv.second))
@@ -597,21 +681,16 @@ struct serializer
continue; continue;
} }
if(!kv.second.comments().empty() && !no_comment_) token += write_comments(kv.second);
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto key_and_sep = format_key(kv.first) + " = "; const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ? const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0; this->width_ - key_and_sep.size() : 0;
token += key_and_sep; token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_, true), token += visit(serializer(residual_width, this->float_prec_,
kv.second); /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n') if(token.back() != '\n')
{ {
token += '\n'; token += '\n';
@@ -637,45 +716,172 @@ struct serializer
ks.push_back(kv.first); ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_, auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks), !multiline_table_printed, this->no_comment_, ks,
kv.second); /*has_comment*/ !kv.second.comments().empty()), kv.second);
// If it is the first time to print a multi-line table, it would be
// helpful to separate normal key-value pair and subtables by a
// newline.
// (this checks if the current key-value pair contains newlines.
// but it is not perfect because multi-line string can also contain
// a newline. in such a case, an empty line will be written) TODO
if((!multiline_table_printed) && if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
{ {
multiline_table_printed = true; multiline_table_printed = true;
} token += '\n'; // separate key-value pairs and subtables
else
{
// still inline tables only.
tmp += '\n';
}
if(!kv.second.comments().empty() && !no_comment_) token += write_comments(kv.second);
{ token += tmp;
for(const auto& c : kv.second.comments())
// care about recursive tables (all tables in each level prints
// newline and there will be a full of newlines)
if(tmp.substr(tmp.size() - 2, 2) != "\n\n" &&
tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" )
{ {
token += '#';
token += c;
token += '\n'; token += '\n';
} }
} }
token += tmp; else
{
token += write_comments(kv.second);
token += tmp;
token += '\n';
}
} }
return token; return token;
} }
std::string make_array_of_tables(const array_type& v) const
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we can format it as the following.
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
// This function checks if inlinization is possible or not, and then
// format the array-of-tables in a proper way.
//
// Note about comments:
//
// If the array itself has a comment (value_has_comment_ == true), we
// should try to make it inline.
// ```toml
// # comment about array
// array = [
// # comment about table element
// {of = "table"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array]]
// of = "table"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
if(this->can_be_inlined_ || this->value_has_comment_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
// write comments for the table itself
token += write_comments(item);
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
// if the value itself has a comment, ignore the line width limit
if( ! this->value_has_comment_)
{
failed = true;
break;
}
}
token += t;
token += ",\n";
}
if( ! failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += write_comments(item);
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
std::string write_comments(const value_type& v) const
{
std::string retval;
if(this->no_comment_) {return retval;}
for(const auto& c : v.comments())
{
retval += '#';
retval += c;
retval += '\n';
}
return retval;
}
bool is_array_of_tables(const value_type& v) const bool is_array_of_tables(const value_type& v) const
{ {
if(!v.is_array()) {return false;} if(!v.is_array() || v.as_array().empty()) {return false;}
const auto& a = v.as_array(); return is_array_of_tables(v.as_array());
return !a.empty() && a.front().is_table(); }
bool is_array_of_tables(const array_type& v) const
{
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
// check all the element in an array to check if the array is an array
// of tables.
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
return elem.is_table();
});
} }
private: private:
bool can_be_inlined_; bool can_be_inlined_;
bool no_comment_; bool no_comment_;
bool value_has_comment_;
int float_prec_; int float_prec_;
std::size_t width_; std::size_t width_;
std::vector<toml::key> keys_; std::vector<toml::key> keys_;
@@ -699,7 +905,7 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments(); oss << v.comments();
oss << '\n'; // to split the file comment from the first element oss << '\n'; // to split the file comment from the first element
} }
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v); const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v);
oss << serialized; oss << serialized;
return oss.str(); return oss.str();
} }
@@ -720,7 +926,7 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
nocomment(std::basic_ostream<charT, traits>& os) nocomment(std::basic_ostream<charT, traits>& os)
{ {
// by default, it is zero. and by defalut, it shows comments. // by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 1; os.iword(detail::comment_index(os)) = 1;
return os; return os;
} }
@@ -729,7 +935,7 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
showcomment(std::basic_ostream<charT, traits>& os) showcomment(std::basic_ostream<charT, traits>& os)
{ {
// by default, it is zero. and by defalut, it shows comments. // by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 0; os.iword(detail::comment_index(os)) = 0;
return os; return os;
} }
@@ -746,7 +952,7 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
const int fprec = static_cast<int>(os.precision()); const int fprec = static_cast<int>(os.precision());
os.width(0); os.width(0);
// by defualt, iword is initialized byl 0. And by default, toml11 outputs // by default, iword is initialized by 0. And by default, toml11 outputs
// comments. So `0` means showcomment. 1 means nocommnet. // comments. So `0` means showcomment. 1 means nocommnet.
const bool no_comment = (1 == os.iword(detail::comment_index(os))); const bool no_comment = (1 == os.iword(detail::comment_index(os)));
@@ -3,6 +3,7 @@
#ifndef TOML11_SOURCE_LOCATION_HPP #ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP
#include <cstdint> #include <cstdint>
#include <sstream>
#include "region.hpp" #include "region.hpp"
@@ -124,7 +125,7 @@ inline std::string format_underline(const std::string& message,
std::ostringstream retval; std::ostringstream retval;
if(colorize) if(color::should_color() || colorize)
{ {
retval << color::colorize; // turn on ANSI color retval << color::colorize; // turn on ANSI color
} }
@@ -136,12 +137,18 @@ inline std::string format_underline(const std::string& message,
// if it is "[error]", it removes that part from the message shown. // if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]") if(message.size() > 7 && message.substr(0, 7) == "[error]")
{ {
retval << color::bold << color::red << "[error]" << color::reset retval
#ifndef TOML11_NO_ERROR_PREFIX
<< color::bold << color::red << "[error]" << color::reset
#endif
<< color::bold << message.substr(7) << color::reset << '\n'; << color::bold << message.substr(7) << color::reset << '\n';
} }
else else
{ {
retval << color::bold << color::red << "[error] " << color::reset retval
#ifndef TOML11_NO_ERROR_PREFIX
<< color::bold << color::red << "[error] " << color::reset
#endif
<< color::bold << message << color::reset << '\n'; << color::bold << message << color::reset << '\n';
} }
@@ -2,13 +2,17 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_STRING_HPP #ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP #define TOML11_STRING_HPP
#include "version.hpp"
#include <cstdint> #include <cstdint>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#if __cplusplus >= 201703L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>) #if __has_include(<string_view>)
#define TOML11_USING_STRING_VIEW 1
#include <string_view> #include <string_view>
#endif #endif
#endif #endif
@@ -53,7 +57,7 @@ struct string
string& operator+=(const std::string& rhs) {str += rhs; return *this;} string& operator+=(const std::string& rhs) {str += rhs; return *this;}
string& operator+=(const string& rhs) {str += rhs.str; return *this;} string& operator+=(const string& rhs) {str += rhs.str; return *this;}
#if __cplusplus >= 201703L #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
explicit string(std::string_view s): kind(string_t::basic), str(s){} explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){} string(std::string_view s, string_t k): kind(k), str(s){}
@@ -2,6 +2,11 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP #ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP
#include "from.hpp"
#include "into.hpp"
#include "version.hpp"
#include <chrono> #include <chrono>
#include <forward_list> #include <forward_list>
#include <string> #include <string>
@@ -9,7 +14,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#if __cplusplus >= 201703L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>) #if __has_include(<string_view>)
#include <string_view> #include <string_view>
#endif // has_include(<string_view>) #endif // has_include(<string_view>)
@@ -84,6 +89,22 @@ struct has_into_toml_method_impl
static std::false_type check(...); static std::false_type check(...);
}; };
struct has_specialized_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*);
};
struct has_specialized_into_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
};
/// Intel C++ compiler can not use decltype in parent class declaration, here /// Intel C++ compiler can not use decltype in parent class declaration, here
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
@@ -114,6 +135,11 @@ template<typename T>
struct has_into_toml_method struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){}; : decltype(has_into_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
#undef decltype #undef decltype
#endif #endif
@@ -121,7 +147,7 @@ struct has_into_toml_method
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++17 and/or/not // C++17 and/or/not
#if __cplusplus >= 201703L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
using std::conjunction; using std::conjunction;
using std::disjunction; using std::disjunction;
@@ -183,8 +209,10 @@ template<typename T>
struct is_container : conjunction< struct is_container : conjunction<
negation<is_map<T>>, // not a map negation<is_map<T>>, // not a map
negation<std::is_same<T, std::string>>, // not a std::string negation<std::is_same<T, std::string>>, // not a std::string
#if __cplusplus >= 201703L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
negation<std::is_same<T, std::string_view>>, // not a std::string_view negation<std::is_same<T, std::string_view>>, // not a std::string_view
#endif // has_include(<string_view>)
#endif #endif
has_iterator<T>, // has T::iterator has_iterator<T>, // has T::iterator
has_value_type<T> // has T::value_type has_value_type<T> // has T::value_type
@@ -206,7 +234,7 @@ struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++14 index_sequence // C++14 index_sequence
#if __cplusplus >= 201402L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::index_sequence; using std::index_sequence;
using std::make_index_sequence; using std::make_index_sequence;
@@ -236,12 +264,12 @@ struct index_sequence_maker<0>
template<std::size_t N> template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type; using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // __cplusplus >= 2014 #endif // cplusplus >= 2014
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++14 enable_if_t // C++14 enable_if_t
#if __cplusplus >= 201402L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::enable_if_t; using std::enable_if_t;
@@ -250,12 +278,12 @@ using std::enable_if_t;
template<bool B, typename T> template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type; using enable_if_t = typename std::enable_if<B, T>::type;
#endif // __cplusplus >= 2014 #endif // cplusplus >= 2014
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// return_type_of_t // return_type_of_t
#if __cplusplus >= 201703L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
template<typename F, typename ... Args> template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>; using return_type_of_t = std::invoke_result_t<F, Args...>;
@@ -21,9 +21,14 @@ class basic_value;
using character = char; using character = char;
using key = std::string; using key = std::string;
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wshadow"
#endif
using boolean = bool; using boolean = bool;
using integer = std::int64_t; using integer = std::int64_t;
using floating = double; // "float" is a keyward, cannot use it here. using floating = double; // "float" is a keyword, cannot use it here.
// the following stuffs are structs defined here, so aliases are not needed. // the following stuffs are structs defined here, so aliases are not needed.
// - string // - string
// - offset_datetime // - offset_datetime
@@ -32,12 +37,26 @@ using floating = double; // "float" is a keyward, cannot use it here.
// - local_date // - local_date
// - local_time // - local_time
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
// default toml::value and default array/table. these are defined after defining // default toml::value and default array/table. these are defined after defining
// basic_value itself. // basic_value itself.
// using value = basic_value<discard_comments, std::unordered_map, std::vector>; // using value = basic_value<discard_comments, std::unordered_map, std::vector>;
// using array = typename value::array_type; // using array = typename value::array_type;
// using table = typename value::table_type; // using table = typename value::table_type;
// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
// GCC -Wshadow=global.
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# if 7 <= __GNUC__
# pragma GCC diagnostic ignored "-Wshadow=global"
# else // gcc-6 or older
# pragma GCC diagnostic ignored "-Wshadow"
# endif
#endif
enum class value_t : std::uint8_t enum class value_t : std::uint8_t
{ {
empty = 0, empty = 0,
@@ -52,6 +71,9 @@ enum class value_t : std::uint8_t
array = 9, array = 9,
table = 10, table = 10,
}; };
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
template<typename charT, typename traits> template<typename charT, typename traits>
inline std::basic_ostream<charT, traits>& inline std::basic_ostream<charT, traits>&
@@ -147,4 +169,5 @@ template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>
} // detail } // detail
} // toml } // toml
#endif// TOML11_TYPES_H #endif// TOML11_TYPES_H
@@ -7,8 +7,9 @@
#include <utility> #include <utility>
#include "traits.hpp" #include "traits.hpp"
#include "version.hpp"
#if __cplusplus >= 201402L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] # define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__) #elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) # define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
@@ -21,7 +22,7 @@
namespace toml namespace toml
{ {
#if __cplusplus >= 201402L #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::make_unique; using std::make_unique;
@@ -33,7 +34,7 @@ inline std::unique_ptr<T> make_unique(Ts&& ... args)
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...)); return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
} }
#endif // __cplusplus >= 2014 #endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
namespace detail namespace detail
{ {
@@ -89,5 +90,61 @@ T from_string(const std::string& str, T opt)
return v; return v;
} }
namespace detail
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
template<typename T>
decltype(auto) last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept
{
return last_one(std::forward<Ts>(tail)...);
}
#else // C++11
// The following code
// ```cpp
// 1 | template<typename T, typename ... Ts>
// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
// 4 | {
// 5 | return last_one(std::forward<Ts>(tail)...);
// 6 | }
// ```
// does not work because the function `last_one(...)` is not yet defined at
// line #3, so `decltype()` cannot deduce the type returned from `last_one`.
// So we need to determine return type in a different way, like a meta func.
template<typename T, typename ... Ts>
struct last_one_in_pack
{
using type = typename last_one_in_pack<Ts...>::type;
};
template<typename T>
struct last_one_in_pack<T>
{
using type = T;
};
template<typename ... Ts>
using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type;
template<typename T>
T&& last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>>
last_one(T&& /*head*/, Ts&& ... tail)
{
return last_one(std::forward<Ts>(tail)...);
}
#endif
} // detail
}// toml }// toml
#endif // TOML11_UTILITY #endif // TOML11_UTILITY
@@ -66,9 +66,20 @@ throw_key_not_found_error(const Value& v, const key& ky)
// ``` // ```
// It actually points to the top-level table at the first character, // It actually points to the top-level table at the first character,
// not `[table]`. But it is too confusing. To avoid the confusion, the error // not `[table]`. But it is too confusing. To avoid the confusion, the error
// message should explicitly say "key not found in the top-level table". // message should explicitly say "key not found in the top-level table",
// or "the parsed file is empty" if there is no content at all (0 bytes in file).
const auto loc = v.location(); const auto loc = v.location();
if(loc.line() == 1 && loc.region() == 1) if(loc.line() == 1 && loc.region() == 0)
{
// First line with a zero-length region means "empty file".
// The region will be generated at `parse_toml_file` function
// if the file contains no bytes.
throw std::out_of_range(format_underline(concat_to_string(
"key \"", ky, "\" not found in the top-level table"), {
{loc, "the parsed file is empty"}
}));
}
else if(loc.line() == 1 && loc.region() == 1)
{ {
// Here it assumes that top-level table starts at the first character. // Here it assumes that top-level table starts at the first character.
// The region corresponds to the top-level table will be generated at // The region corresponds to the top-level table will be generated at
@@ -90,7 +101,7 @@ throw_key_not_found_error(const Value& v, const key& ky)
// ```toml // ```toml
// a = {b = "c"} // a = {b = "c"}
// ``` // ```
// toml11 consideres the inline table body as the table region. Here, // toml11 considers the inline table body as the table region. Here,
// `{b = "c"}` is the region of the table "a". The size of the region // `{b = "c"}` is the region of the table "a". The size of the region
// is 9, not 1. The shotest inline table still has two characters, `{` // is 9, not 1. The shotest inline table still has two characters, `{`
// and `}`. The size cannot be 1. // and `}`. The size cannot be 1.
@@ -99,7 +110,7 @@ throw_key_not_found_error(const Value& v, const key& ky)
// ```toml // ```toml
// [a] // [a]
// ``` // ```
// toml11 consideres the whole table key as the table region. Here, // toml11 considers the whole table key as the table region. Here,
// `[a]` is the table region. The size is 3, not 1. // `[a]` is the table region. The size is 3, not 1.
// //
throw std::out_of_range(format_underline(concat_to_string( throw std::out_of_range(format_underline(concat_to_string(
@@ -236,6 +247,7 @@ class basic_value
} }
basic_value& operator=(const basic_value& v) basic_value& operator=(const basic_value& v)
{ {
if(this == std::addressof(v)) {return *this;}
this->cleanup(); this->cleanup();
this->region_info_ = v.region_info_; this->region_info_ = v.region_info_;
this->comments_ = v.comments_; this->comments_ = v.comments_;
@@ -258,6 +270,7 @@ class basic_value
} }
basic_value& operator=(basic_value&& v) basic_value& operator=(basic_value&& v)
{ {
if(this == std::addressof(v)) {return *this;}
this->cleanup(); this->cleanup();
this->region_info_ = std::move(v.region_info_); this->region_info_ = std::move(v.region_info_);
this->comments_ = std::move(v.comments_); this->comments_ = std::move(v.comments_);
@@ -281,9 +294,9 @@ class basic_value
// overwrite comments ---------------------------------------------------- // overwrite comments ----------------------------------------------------
basic_value(const basic_value& v, std::vector<std::string> comments) basic_value(const basic_value& v, std::vector<std::string> com)
: type_(v.type()), region_info_(v.region_info_), : type_(v.type()), region_info_(v.region_info_),
comments_(std::move(comments)) comments_(std::move(com))
{ {
switch(v.type()) switch(v.type())
{ {
@@ -301,9 +314,9 @@ class basic_value
} }
} }
basic_value(basic_value&& v, std::vector<std::string> comments) basic_value(basic_value&& v, std::vector<std::string> com)
: type_(v.type()), region_info_(std::move(v.region_info_)), : type_(v.type()), region_info_(std::move(v.region_info_)),
comments_(std::move(comments)) comments_(std::move(com))
{ {
switch(this->type_) // here this->type_ is already initialized switch(this->type_) // here this->type_ is already initialized
{ {
@@ -359,9 +372,9 @@ class basic_value
template<typename C, template<typename C,
template<typename ...> class T, template<typename ...> class T,
template<typename ...> class A> template<typename ...> class A>
basic_value(const basic_value<C, T, A>& v, std::vector<std::string> comments) basic_value(const basic_value<C, T, A>& v, std::vector<std::string> com)
: type_(v.type()), region_info_(v.region_info_), : type_(v.type()), region_info_(v.region_info_),
comments_(std::move(comments)) comments_(std::move(com))
{ {
switch(v.type()) switch(v.type())
{ {
@@ -443,10 +456,10 @@ class basic_value
assigner(this->boolean_, b); assigner(this->boolean_, b);
return *this; return *this;
} }
basic_value(boolean b, std::vector<std::string> comments) basic_value(boolean b, std::vector<std::string> com)
: type_(value_t::boolean), : type_(value_t::boolean),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->boolean_, b); assigner(this->boolean_, b);
} }
@@ -478,10 +491,10 @@ class basic_value
template<typename T, typename std::enable_if<detail::conjunction< template<typename T, typename std::enable_if<detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value, std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value,
std::nullptr_t>::type = nullptr> std::nullptr_t>::type = nullptr>
basic_value(T i, std::vector<std::string> comments) basic_value(T i, std::vector<std::string> com)
: type_(value_t::integer), : type_(value_t::integer),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->integer_, static_cast<integer>(i)); assigner(this->integer_, static_cast<integer>(i));
} }
@@ -511,10 +524,10 @@ class basic_value
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr> std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, std::vector<std::string> comments) basic_value(T f, std::vector<std::string> com)
: type_(value_t::floating), : type_(value_t::floating),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->floating_, f); assigner(this->floating_, f);
} }
@@ -535,10 +548,10 @@ class basic_value
assigner(this->string_, s); assigner(this->string_, s);
return *this; return *this;
} }
basic_value(toml::string s, std::vector<std::string> comments) basic_value(toml::string s, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, std::move(s)); assigner(this->string_, std::move(s));
} }
@@ -563,17 +576,17 @@ class basic_value
{ {
assigner(this->string_, toml::string(std::move(s), kind)); assigner(this->string_, toml::string(std::move(s), kind));
} }
basic_value(std::string s, std::vector<std::string> comments) basic_value(std::string s, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, toml::string(std::move(s))); assigner(this->string_, toml::string(std::move(s)));
} }
basic_value(std::string s, string_t kind, std::vector<std::string> comments) basic_value(std::string s, string_t kind, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, toml::string(std::move(s), kind)); assigner(this->string_, toml::string(std::move(s), kind));
} }
@@ -598,22 +611,22 @@ class basic_value
{ {
assigner(this->string_, toml::string(std::string(s), kind)); assigner(this->string_, toml::string(std::string(s), kind));
} }
basic_value(const char* s, std::vector<std::string> comments) basic_value(const char* s, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, toml::string(std::string(s))); assigner(this->string_, toml::string(std::string(s)));
} }
basic_value(const char* s, string_t kind, std::vector<std::string> comments) basic_value(const char* s, string_t kind, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, toml::string(std::string(s), kind)); assigner(this->string_, toml::string(std::string(s), kind));
} }
#if __cplusplus >= 201703L #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
basic_value(std::string_view s) basic_value(std::string_view s)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})) region_info_(std::make_shared<region_base>(region_base{}))
@@ -628,10 +641,10 @@ class basic_value
assigner(this->string_, toml::string(s)); assigner(this->string_, toml::string(s));
return *this; return *this;
} }
basic_value(std::string_view s, std::vector<std::string> comments) basic_value(std::string_view s, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, toml::string(s)); assigner(this->string_, toml::string(s));
} }
@@ -641,10 +654,10 @@ class basic_value
{ {
assigner(this->string_, toml::string(s, kind)); assigner(this->string_, toml::string(s, kind));
} }
basic_value(std::string_view s, string_t kind, std::vector<std::string> comments) basic_value(std::string_view s, string_t kind, std::vector<std::string> com)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->string_, toml::string(s, kind)); assigner(this->string_, toml::string(s, kind));
} }
@@ -666,10 +679,10 @@ class basic_value
assigner(this->local_date_, ld); assigner(this->local_date_, ld);
return *this; return *this;
} }
basic_value(const local_date& ld, std::vector<std::string> comments) basic_value(const local_date& ld, std::vector<std::string> com)
: type_(value_t::local_date), : type_(value_t::local_date),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->local_date_, ld); assigner(this->local_date_, ld);
} }
@@ -682,10 +695,10 @@ class basic_value
{ {
assigner(this->local_time_, lt); assigner(this->local_time_, lt);
} }
basic_value(const local_time& lt, std::vector<std::string> comments) basic_value(const local_time& lt, std::vector<std::string> com)
: type_(value_t::local_time), : type_(value_t::local_time),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->local_time_, lt); assigner(this->local_time_, lt);
} }
@@ -707,10 +720,10 @@ class basic_value
} }
template<typename Rep, typename Period> template<typename Rep, typename Period>
basic_value(const std::chrono::duration<Rep, Period>& dur, basic_value(const std::chrono::duration<Rep, Period>& dur,
std::vector<std::string> comments) std::vector<std::string> com)
: type_(value_t::local_time), : type_(value_t::local_time),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->local_time_, local_time(dur)); assigner(this->local_time_, local_time(dur));
} }
@@ -732,10 +745,10 @@ class basic_value
{ {
assigner(this->local_datetime_, ldt); assigner(this->local_datetime_, ldt);
} }
basic_value(const local_datetime& ldt, std::vector<std::string> comments) basic_value(const local_datetime& ldt, std::vector<std::string> com)
: type_(value_t::local_datetime), : type_(value_t::local_datetime),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->local_datetime_, ldt); assigner(this->local_datetime_, ldt);
} }
@@ -756,10 +769,10 @@ class basic_value
{ {
assigner(this->offset_datetime_, odt); assigner(this->offset_datetime_, odt);
} }
basic_value(const offset_datetime& odt, std::vector<std::string> comments) basic_value(const offset_datetime& odt, std::vector<std::string> com)
: type_(value_t::offset_datetime), : type_(value_t::offset_datetime),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->offset_datetime_, odt); assigner(this->offset_datetime_, odt);
} }
@@ -778,10 +791,10 @@ class basic_value
assigner(this->offset_datetime_, offset_datetime(tp)); assigner(this->offset_datetime_, offset_datetime(tp));
} }
basic_value(const std::chrono::system_clock::time_point& tp, basic_value(const std::chrono::system_clock::time_point& tp,
std::vector<std::string> comments) std::vector<std::string> com)
: type_(value_t::offset_datetime), : type_(value_t::offset_datetime),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->offset_datetime_, offset_datetime(tp)); assigner(this->offset_datetime_, offset_datetime(tp));
} }
@@ -802,10 +815,10 @@ class basic_value
{ {
assigner(this->array_, ary); assigner(this->array_, ary);
} }
basic_value(const array_type& ary, std::vector<std::string> comments) basic_value(const array_type& ary, std::vector<std::string> com)
: type_(value_t::array), : type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->array_, ary); assigner(this->array_, ary);
} }
@@ -833,10 +846,10 @@ class basic_value
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
std::is_convertible<T, value_type>::value, std::is_convertible<T, value_type>::value,
std::nullptr_t>::type = nullptr> std::nullptr_t>::type = nullptr>
basic_value(std::initializer_list<T> list, std::vector<std::string> comments) basic_value(std::initializer_list<T> list, std::vector<std::string> com)
: type_(value_t::array), : type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
array_type ary(list.begin(), list.end()); array_type ary(list.begin(), list.end());
assigner(this->array_, std::move(ary)); assigner(this->array_, std::move(ary));
@@ -876,10 +889,10 @@ class basic_value
detail::negation<std::is_same<T, array_type>>, detail::negation<std::is_same<T, array_type>>,
detail::is_container<T> detail::is_container<T>
>::value, std::nullptr_t>::type = nullptr> >::value, std::nullptr_t>::type = nullptr>
basic_value(const T& list, std::vector<std::string> comments) basic_value(const T& list, std::vector<std::string> com)
: type_(value_t::array), : type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
static_assert(std::is_convertible<typename T::value_type, value_type>::value, static_assert(std::is_convertible<typename T::value_type, value_type>::value,
"elements of a container should be convertible to toml::value"); "elements of a container should be convertible to toml::value");
@@ -915,10 +928,10 @@ class basic_value
{ {
assigner(this->table_, tab); assigner(this->table_, tab);
} }
basic_value(const table_type& tab, std::vector<std::string> comments) basic_value(const table_type& tab, std::vector<std::string> com)
: type_(value_t::table), : type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
assigner(this->table_, tab); assigner(this->table_, tab);
} }
@@ -943,10 +956,10 @@ class basic_value
} }
basic_value(std::initializer_list<std::pair<key, basic_value>> list, basic_value(std::initializer_list<std::pair<key, basic_value>> list,
std::vector<std::string> comments) std::vector<std::string> com)
: type_(value_t::table), : type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
table_type tab; table_type tab;
for(const auto& elem : list) {tab[elem.first] = elem.second;} for(const auto& elem : list) {tab[elem.first] = elem.second;}
@@ -982,10 +995,10 @@ class basic_value
detail::negation<std::is_same<Map, table_type>>, detail::negation<std::is_same<Map, table_type>>,
detail::is_map<Map> detail::is_map<Map>
>::value, std::nullptr_t>::type = nullptr> >::value, std::nullptr_t>::type = nullptr>
basic_value(const Map& mp, std::vector<std::string> comments) basic_value(const Map& mp, std::vector<std::string> com)
: type_(value_t::table), : type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})), region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments)) comments_(std::move(com))
{ {
table_type tab; table_type tab;
for(const auto& elem : mp) {tab[elem.first] = elem.second;} for(const auto& elem : mp) {tab[elem.first] = elem.second;}
@@ -1017,8 +1030,8 @@ class basic_value
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr> detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
basic_value(const T& ud, std::vector<std::string> comments) basic_value(const T& ud, std::vector<std::string> com)
: basic_value(ud.into_toml(), std::move(comments)) : basic_value(ud.into_toml(), std::move(com))
{} {}
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr> detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
@@ -1033,8 +1046,8 @@ class basic_value
template<typename T, std::size_t S = sizeof(::toml::into<T>)> template<typename T, std::size_t S = sizeof(::toml::into<T>)>
basic_value(const T& ud): basic_value(::toml::into<T>::into_toml(ud)) {} basic_value(const T& ud): basic_value(::toml::into<T>::into_toml(ud)) {}
template<typename T, std::size_t S = sizeof(::toml::into<T>)> template<typename T, std::size_t S = sizeof(::toml::into<T>)>
basic_value(const T& ud, std::vector<std::string> comments) basic_value(const T& ud, std::vector<std::string> com)
: basic_value(::toml::into<T>::into_toml(ud), std::move(comments)) : basic_value(::toml::into<T>::into_toml(ud), std::move(com))
{} {}
template<typename T, std::size_t S = sizeof(::toml::into<T>)> template<typename T, std::size_t S = sizeof(::toml::into<T>)>
basic_value& operator=(const T& ud) basic_value& operator=(const T& ud)
@@ -1047,10 +1060,10 @@ class basic_value
// //
// Those constructors take detail::region that contains parse result. // Those constructors take detail::region that contains parse result.
basic_value(boolean b, detail::region reg) basic_value(boolean b, detail::region reg, std::vector<std::string> cm)
: type_(value_t::boolean), : type_(value_t::boolean),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->boolean_, b); assigner(this->boolean_, b);
} }
@@ -1058,68 +1071,75 @@ class basic_value
detail::conjunction< detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>> std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
>::value, std::nullptr_t>::type = nullptr> >::value, std::nullptr_t>::type = nullptr>
basic_value(T i, detail::region reg) basic_value(T i, detail::region reg, std::vector<std::string> cm)
: type_(value_t::integer), : type_(value_t::integer),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->integer_, static_cast<integer>(i)); assigner(this->integer_, static_cast<integer>(i));
} }
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr> std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, detail::region reg) basic_value(T f, detail::region reg, std::vector<std::string> cm)
: type_(value_t::floating), : type_(value_t::floating),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->floating_, static_cast<floating>(f)); assigner(this->floating_, static_cast<floating>(f));
} }
basic_value(toml::string s, detail::region reg) basic_value(toml::string s, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->string_, std::move(s)); assigner(this->string_, std::move(s));
} }
basic_value(const local_date& ld, detail::region reg) basic_value(const local_date& ld, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_date), : type_(value_t::local_date),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->local_date_, ld); assigner(this->local_date_, ld);
} }
basic_value(const local_time& lt, detail::region reg) basic_value(const local_time& lt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_time), : type_(value_t::local_time),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->local_time_, lt); assigner(this->local_time_, lt);
} }
basic_value(const local_datetime& ldt, detail::region reg) basic_value(const local_datetime& ldt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_datetime), : type_(value_t::local_datetime),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->local_datetime_, ldt); assigner(this->local_datetime_, ldt);
} }
basic_value(const offset_datetime& odt, detail::region reg) basic_value(const offset_datetime& odt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::offset_datetime), : type_(value_t::offset_datetime),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->offset_datetime_, odt); assigner(this->offset_datetime_, odt);
} }
basic_value(const array_type& ary, detail::region reg) basic_value(const array_type& ary, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::array), : type_(value_t::array),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->array_, ary); assigner(this->array_, ary);
} }
basic_value(const table_type& tab, detail::region reg) basic_value(const table_type& tab, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::table), : type_(value_t::table),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->table_, tab); assigner(this->table_, tab);
} }
@@ -1127,8 +1147,10 @@ class basic_value
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T, value_type>::value, detail::is_exact_toml_type<T, value_type>::value,
std::nullptr_t>::type = nullptr> std::nullptr_t>::type = nullptr>
basic_value(std::pair<T, detail::region> parse_result) basic_value(std::pair<T, detail::region> parse_result, std::vector<std::string> com)
: basic_value(std::move(parse_result.first), std::move(parse_result.second)) : basic_value(std::move(parse_result.first),
std::move(parse_result.second),
std::move(com))
{} {}
// type checking and casting ============================================ // type checking and casting ============================================
@@ -1692,9 +1714,9 @@ class basic_value
{ {
switch(this->type_) switch(this->type_)
{ {
case value_t::string : {string_.~string(); return;} case value_t::string : {string_.~string(); return;}
case value_t::array : {array_.~array_storage(); return;} case value_t::array : {array_.~array_storage(); return;}
case value_t::table : {table_.~table_storage(); return;} case value_t::table : {table_.~table_storage(); return;}
default : return; default : return;
} }
} }
@@ -1730,7 +1752,8 @@ class basic_value
}; };
// default toml::value and default array/table. // default toml::value and default array/table.
using value = basic_value<discard_comments, std::unordered_map, std::vector>; // TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp
using value = basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
using array = typename value::array_type; using array = typename value::array_type;
using table = typename value::table_type; using table = typename value::table_type;
+42
View File
@@ -0,0 +1,42 @@
#ifndef TOML11_VERSION_HPP
#define TOML11_VERSION_HPP
// This file checks C++ version.
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
// Since MSVC does not define `__cplusplus` correctly unless you pass
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
// Those enables you to define version manually or to use MSVC specific
// version macro automatically.
//
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
// MSVC ignores the value, maybe because of backward compatibility. Instead,
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
// the C++ standard. First we check the manual version definition, and then
// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
//
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
//
#if defined(TOML11_ENFORCE_CXX11)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
#elif defined(TOML11_ENFORCE_CXX14)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
#elif defined(TOML11_ENFORCE_CXX17)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
#elif defined(TOML11_ENFORCE_CXX20)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
#else
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#endif// TOML11_VERSION_HPP