Compare commits

...

36 Commits

Author SHA1 Message Date
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
19 changed files with 491 additions and 201 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ 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
AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterDefinitionReturnType: None
+1
View File
@@ -9,3 +9,4 @@ build*/
.idea/ .idea/
cmake-build*/ cmake-build*/
CMakeLists.txt.user CMakeLists.txt.user
.vscode/
+15 -42
View File
@@ -7,17 +7,14 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build") 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.17
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,29 +50,14 @@ 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
# Target cmkr
set(CMKR_TARGET cmkr)
set(cmkr_SOURCES "")
list(APPEND cmkr_SOURCES
"src/arguments.cpp" "src/arguments.cpp"
"src/build.cpp" "src/build.cpp"
"src/cmake_generator.cpp" "src/cmake_generator.cpp"
@@ -93,24 +75,12 @@ list(APPEND cmkr_SOURCES
"include/project_parser.hpp" "include/project_parser.hpp"
"cmake/cmkr.cmake" "cmake/cmkr.cmake"
"cmake/version.hpp.in" "cmake/version.hpp.in"
)
list(APPEND cmkr_SOURCES
cmake.toml 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
@@ -129,10 +99,13 @@ target_link_libraries(cmkr PRIVATE
nlohmann_json 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) generate_resources(${CMKR_TARGET})
install( install(
TARGETS TARGETS
+7 -6
View File
@@ -64,9 +64,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 ❤️
+2 -2
View File
@@ -4,14 +4,14 @@ cmkr-include = false
[project] [project]
name = "cmkr" name = "cmkr"
version = "0.2.12" version = "0.2.17"
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"
+22 -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.17" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE) set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake # To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
@@ -58,7 +58,7 @@ 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(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$") if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
@@ -72,10 +72,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 +103,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()
+12 -12
View File
@@ -1,20 +1,20 @@
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")
+10 -4
View File
@@ -27,6 +27,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")
""" """
@@ -43,8 +44,8 @@ You can specify your own conditions and use them in any `condition` field:
```toml ```toml
[conditions] [conditions]
arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8" arch64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4" arch32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
``` ```
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions. This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
@@ -58,6 +59,8 @@ sources = ["src/main.cpp"]
windows.sources = ["src/windows_specific.cpp"] windows.sources = ["src/windows_specific.cpp"]
``` ```
### 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
@@ -144,6 +147,7 @@ alias = "mytarget::mytarget"
type = "static" # executable, shared (DLL), static, interface, object, library, custom type = "static" # executable, shared (DLL), static, interface, object, library, custom
headers = ["src/mytarget.h"] headers = ["src/mytarget.h"]
sources = ["src/mytarget.cpp"] sources = ["src/mytarget.cpp"]
msvc-runtime = "" # dynamic (implicit default), static
# The keys below match the target_xxx CMake commands # The keys below match the target_xxx CMake commands
# Keys prefixed with private- will get PRIVATE visibility # Keys prefixed with private- will get PRIVATE visibility
@@ -200,7 +204,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/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>
+7 -5
View File
@@ -70,8 +70,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 ❤️
+14 -1
View File
@@ -3,6 +3,7 @@
#include <mpark/variant.hpp> #include <mpark/variant.hpp>
#include <string> #include <string>
#include <tsl/ordered_map.h> #include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
#include <vector> #include <vector>
namespace cmkr { namespace cmkr {
@@ -67,7 +68,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
@@ -128,6 +128,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 {
@@ -151,7 +152,17 @@ struct Content {
ConditionVector include_after; ConditionVector include_after;
}; };
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";
@@ -169,6 +180,7 @@ struct Project {
std::string project_version; std::string project_version;
std::string project_description; std::string project_description;
std::vector<std::string> project_languages; std::vector<std::string> project_languages;
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;
@@ -186,6 +198,7 @@ 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 is_root_path(const std::string &path); bool is_root_path(const std::string &path);
+216 -95
View File
@@ -4,6 +4,7 @@
#include <resources/cmkr.hpp> #include <resources/cmkr.hpp>
#include "fs.hpp" #include "fs.hpp"
#include "project_parser.hpp"
#include <cstdio> #include <cstdio>
#include <fstream> #include <fstream>
#include <memory> #include <memory>
@@ -25,7 +26,7 @@ static std::string format(const char *format, tsl::ordered_map<std::string, std:
return s; return s;
} }
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool root_project) { static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool is_root_project) {
std::vector<std::string> temp; std::vector<std::string> temp;
auto extract_suffix = [](const fs::path &base, const fs::path &full) { auto extract_suffix = [](const fs::path &base, const fs::path &full) {
@@ -38,7 +39,7 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
auto stem = name.filename().stem().string(); auto stem = name.filename().stem().string();
auto ext = name.extension(); auto ext = name.extension();
if (root_project && stem == "**" && name == name.filename()) { if (is_root_project && stem == "**" && name == name.filename()) {
throw std::runtime_error("Recursive globbing not allowed in project root: " + name.string()); throw std::runtime_error("Recursive globbing not allowed in project root: " + name.string());
} }
@@ -66,11 +67,11 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
return temp; return temp;
} }
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool root_project) { static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool is_root_project) {
// TODO: add duplicate checking // TODO: add duplicate checking
std::vector<std::string> result; std::vector<std::string> result;
for (const auto &src : sources) { for (const auto &src : sources) {
auto expanded = expand_cmake_path(src, toml_dir, root_project); auto expanded = expand_cmake_path(src, toml_dir, is_root_project);
for (const auto &f : expanded) { for (const auto &f : expanded) {
result.push_back(f); result.push_back(f);
} }
@@ -89,8 +90,24 @@ static void create_file(const fs::path &path, const std::string &contents) {
ofs << contents; ofs << contents;
} }
// CMake target name rules: https://cmake.org/cmake/help/latest/policy/CMP0037.html [A-Za-z0-9_.+\-]
// TOML bare keys: non-empty strings composed only of [A-Za-z0-9_-]
// We replace all non-TOML bare key characters with _
static std::string escape_project_name(const std::string &name) {
std::string escaped;
escaped.reserve(name.length());
for (auto ch : name) {
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-') {
escaped += ch;
} else {
escaped += '_';
}
}
return escaped;
}
void generate_project(const std::string &type) { void generate_project(const std::string &type) {
const auto name = fs::current_path().stem().string(); const auto name = escape_project_name(fs::current_path().stem().string());
if (fs::exists(fs::current_path() / "cmake.toml")) { if (fs::exists(fs::current_path() / "cmake.toml")) {
throw std::runtime_error("Cannot initialize a project when cmake.toml already exists!"); throw std::runtime_error("Cannot initialize a project when cmake.toml already exists!");
} }
@@ -126,13 +143,17 @@ void generate_project(const std::string &type) {
struct CommandEndl { struct CommandEndl {
std::stringstream &ss; std::stringstream &ss;
CommandEndl(std::stringstream &ss) : ss(ss) {} CommandEndl(std::stringstream &ss) : ss(ss) {
void endl() { ss << '\n'; } }
void endl() {
ss << '\n';
}
}; };
struct RawArg { struct RawArg {
RawArg() = default; RawArg() = default;
RawArg(std::string arg) : arg(std::move(arg)) {} RawArg(std::string arg) : arg(std::move(arg)) {
}
std::string arg; std::string arg;
}; };
@@ -148,7 +169,8 @@ struct Command {
std::string post_comment; std::string post_comment;
Command(std::stringstream &ss, int depth, std::string command, std::string post_comment) Command(std::stringstream &ss, int depth, std::string command, std::string post_comment)
: ss(ss), depth(depth), command(std::move(command)), post_comment(std::move(post_comment)) {} : ss(ss), depth(depth), command(std::move(command)), post_comment(std::move(post_comment)) {
}
~Command() noexcept(false) { ~Command() noexcept(false) {
if (!generated) { if (!generated) {
@@ -162,10 +184,11 @@ struct Command {
return "\"\""; return "\"\"";
} }
// Don't quote arguments that don't need quoting // Don't quote arguments that don't need quoting
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos && // https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#unquoted-argument
str.find(';') == std::string::npos) { // NOTE: Normally '/' does not require quoting according to the documentation but this has been the case here
// previously, so for backwards compatibility its still here.
if (str.find_first_of("()#\"\\'> |/;") == str.npos)
return str; return str;
}
std::string result; std::string result;
result += "\""; result += "\"";
for (char ch : str) { for (char ch : str) {
@@ -316,7 +339,8 @@ static std::string tolf(const std::string &str) {
}; };
struct Generator { struct Generator {
Generator(const parser::Project &project) : project(project) {} Generator(const parser::Project &project) : project(project) {
}
Generator(const Generator &) = delete; Generator(const Generator &) = delete;
const parser::Project &project; const parser::Project &project;
@@ -342,7 +366,9 @@ struct Generator {
return CommandEndl(ss); return CommandEndl(ss);
} }
void endl() { ss << '\n'; } void endl() {
ss << '\n';
}
void inject_includes(const std::vector<std::string> &includes) { void inject_includes(const std::vector<std::string> &includes) {
if (!includes.empty()) { if (!includes.empty()) {
@@ -391,9 +417,10 @@ struct Generator {
} }
if (!condition.empty()) { if (!condition.empty()) {
cmd("endif")(); cmd("endif")().endl();
} else if (!itr.second.empty()) {
endl();
} }
endl();
} }
} }
} }
@@ -480,7 +507,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
} }
// Root project doesn't have a parent // Root project doesn't have a parent
auto root_project = parent_project == nullptr; auto is_root_project = parent_project == nullptr;
parser::Project project(parent_project, path, false); parser::Project project(parent_project, path, false);
Generator gen(project); Generator gen(project);
@@ -496,9 +523,27 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
comment("See " + cmkr_url + " for more information"); comment("See " + cmkr_url + " for more information");
endl(); endl();
if (root_project) { if (is_root_project) {
cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl(); cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl();
if (project.project_msvc_runtime != parser::msvc_last) {
comment("Enable support for MSVC_RUNTIME_LIBRARY");
cmd("cmake_policy")("SET", "CMP0091", "NEW");
switch (project.project_msvc_runtime) {
case parser::msvc_dynamic:
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL");
break;
case parser::msvc_static:
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>");
break;
default:
break;
}
endl();
}
// clang-format on
if (!project.allow_in_tree) { if (!project.allow_in_tree) {
// clang-format off // clang-format off
cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR"); cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR");
@@ -507,14 +552,13 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
// clang-format on // clang-format on
} }
comment("Regenerate CMakeLists.txt automatically in the root project");
cmd("set")("CMKR_ROOT_PROJECT", "OFF"); cmd("set")("CMKR_ROOT_PROJECT", "OFF");
// clang-format off // clang-format off
cmd("if")("CMAKE_CURRENT_SOURCE_DIR", "STREQUAL", "CMAKE_SOURCE_DIR"); cmd("if")("CMAKE_CURRENT_SOURCE_DIR", "STREQUAL", "CMAKE_SOURCE_DIR");
cmd("set")("CMKR_ROOT_PROJECT", "ON").endl(); cmd("set")("CMKR_ROOT_PROJECT", "ON").endl();
if (!project.cmkr_include.empty()) { if (!project.cmkr_include.empty()) {
comment("Bootstrap cmkr"); comment("Bootstrap cmkr and automatically regenerate CMakeLists.txt");
cmd("include")(project.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT"); cmd("include")(project.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT");
cmd("if")("CMKR_INCLUDE_RESULT"); cmd("if")("CMKR_INCLUDE_RESULT");
cmd("cmkr")(); cmd("cmkr")();
@@ -522,7 +566,10 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
} }
comment("Enable folder support"); comment("Enable folder support");
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON"); cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON").endl();
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
cmd("endif")().endl(); cmd("endif")().endl();
// clang-format on // clang-format on
@@ -530,15 +577,15 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
if (!project.cmkr_include.empty() && !fs::exists(cmkr_include) && cmkr_include.is_relative()) { if (!project.cmkr_include.empty() && !fs::exists(cmkr_include) && cmkr_include.is_relative()) {
create_file(cmkr_include, resources::cmkr); create_file(cmkr_include, resources::cmkr);
} }
} else {
// clang-format off
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
cmd("if")("CMKR_ROOT_PROJECT");
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
cmd("endif")().endl();
// clang-format on
} }
// clang-format off
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
cmd("if")("CMKR_ROOT_PROJECT");
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
cmd("endif")().endl();
// clang-format on
// TODO: remove support and replace with global compile-features // TODO: remove support and replace with global compile-features
if (!project.cppflags.empty()) { if (!project.cppflags.empty()) {
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \""; ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
@@ -574,6 +621,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
endl(); endl();
} }
// TODO: rename to 'variables'?
if (!project.settings.empty()) { if (!project.settings.empty()) {
comment("Settings"); comment("Settings");
for (const auto &set : project.settings) { for (const auto &set : project.settings) {
@@ -603,6 +651,13 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto version = std::make_pair("VERSION", project.project_version); auto version = std::make_pair("VERSION", project.project_version);
auto description = std::make_pair("DESCRIPTION", project.project_description); auto description = std::make_pair("DESCRIPTION", project.project_description);
cmd("project")(project.project_name, languages, version, description).endl(); cmd("project")(project.project_name, languages, version, description).endl();
for (const auto &language : project.project_languages) {
if (language == "CSharp") {
cmd("include")("CSharpUtilities").endl();
break;
}
}
} }
gen.conditional_includes(project.include_after); gen.conditional_includes(project.include_after);
@@ -633,8 +688,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
cmd("include")("FetchContent"); cmd("include")("FetchContent");
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")..."); cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
cmd("FetchContent_Declare")("vcpkg", "URL", url); cmd("FetchContent_Declare")("vcpkg", "URL", url);
cmd("FetchContent_MakeAvailable")("vcpkg"); // Not using FetchContent_MakeAvailable here in case vcpkg adds CMakeLists.txt
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake"); cmd("FetchContent_GetProperties")("vcpkg");
cmd("if")("NOT", "vcpkg_POPULATED");
cmd("FetchContent_Populate")("vcpkg");
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
cmd("endif")();
cmd("endif")(); cmd("endif")();
endl(); endl();
// clang-format on // clang-format on
@@ -742,7 +801,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto add_subdir = [&](const std::string &dir) { auto add_subdir = [&](const std::string &dir) {
// clang-format off // clang-format off
comment(dir); comment("Subdirectory: " + dir);
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}"); cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
cmd("if")("CMAKE_FOLDER"); cmd("if")("CMAKE_FOLDER");
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir); cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
@@ -752,17 +811,19 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
// clang-format on // clang-format on
cmd("add_subdirectory")(dir); cmd("add_subdirectory")(dir);
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl(); cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}");
}; };
// generate_cmake is called on the subdirectories recursively later // generate_cmake is called on the subdirectories recursively later
if (!project.project_subdirs.empty()) { if (!project.project_subdirs.empty()) {
gen.handle_condition(project.project_subdirs, [&](const std::string &, const std::vector<std::string> &subdirs) { gen.handle_condition(project.project_subdirs, [&](const std::string &, const std::vector<std::string> &subdirs) {
for (const auto &dir : subdirs) { for (size_t i = 0; i < subdirs.size(); i++) {
add_subdir(dir); add_subdir(subdirs[i]);
if (i + 1 < subdirs.size()) {
endl();
}
} }
}); });
endl();
} }
for (const auto &subdir : project.subdirs) { for (const auto &subdir : project.subdirs) {
ConditionScope cs(gen, subdir.condition); ConditionScope cs(gen, subdir.condition);
@@ -771,22 +832,20 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.conditional_cmake(subdir.cmake_before); gen.conditional_cmake(subdir.cmake_before);
add_subdir(subdir.name); add_subdir(subdir.name);
endl();
gen.conditional_includes(subdir.include_after); gen.conditional_includes(subdir.include_after);
gen.conditional_cmake(subdir.cmake_after); gen.conditional_cmake(subdir.cmake_after);
} }
if (!project.targets.empty()) { if (!project.targets.empty()) {
auto project_root = project.root();
for (size_t i = 0; i < project.targets.size(); i++) { for (size_t i = 0; i < project.targets.size(); i++) {
if (i > 0) {
endl();
}
const auto &target = project.targets[i]; const auto &target = project.targets[i];
const parser::Template *tmplate = nullptr; const parser::Template *tmplate = nullptr;
std::unique_ptr<ConditionScope> tmplate_cs{}; std::unique_ptr<ConditionScope> tmplate_cs{};
comment("Target " + target.name); comment("Target: " + target.name);
// Check if this target is using a template. // Check if this target is using a template.
if (target.type == parser::target_template) { if (target.type == parser::target_template) {
@@ -800,56 +859,115 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
ConditionScope cs(gen, target.condition); ConditionScope cs(gen, target.condition);
cmd("set")("CMKR_TARGET", target.name); // Detect if there is cmake included before/after the target
auto has_include_before = false;
auto has_include_after = false;
{
auto has_include = [](const parser::ConditionVector &includes) {
for (const auto &itr : includes) {
for (const auto &jtr : itr.second) {
return true;
}
}
return false;
};
auto has_include_helper = [&](const parser::Target &target) {
if (!target.cmake_before.empty() || has_include(target.include_before)) {
has_include_before = true;
}
if (!target.cmake_after.empty() || has_include(target.include_after)) {
has_include_after = true;
}
};
if (tmplate != nullptr) {
has_include_helper(tmplate->outline);
}
has_include_helper(target);
}
// Generate the include before
if (has_include_before) {
cmd("set")("CMKR_TARGET", target.name);
}
if (tmplate != nullptr) { if (tmplate != nullptr) {
gen.conditional_includes(tmplate->outline.include_before); gen.conditional_includes(tmplate->outline.include_before);
gen.conditional_cmake(tmplate->outline.cmake_before); gen.conditional_cmake(tmplate->outline.cmake_before);
} }
gen.conditional_includes(target.include_before); gen.conditional_includes(target.include_before);
gen.conditional_cmake(target.cmake_before); gen.conditional_cmake(target.cmake_before);
auto sources_var = target.name + "_SOURCES"; // Merge the sources from the template and the target. The sources
// without condition need to be processed first
parser::Condition<tsl::ordered_set<std::string>> msources;
msources[""].clear();
bool added_toml = false; auto merge_sources = [&msources](const parser::ConditionVector &sources) {
cmd("set")(sources_var, RawArg("\"\"")).endl(); for (const auto &itr : sources) {
auto &source_list = msources[itr.first];
if (tmplate != nullptr) { for (const auto &source : itr.second) {
gen.handle_condition(tmplate->outline.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) { source_list.insert(source);
auto sources = expand_cmake_paths(condition_sources, path, root_project);
if (sources.empty()) {
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
} }
cmd("list")("APPEND", sources_var, sources); }
}); };
if (tmplate != nullptr) {
merge_sources(tmplate->outline.sources);
}
merge_sources(target.sources);
// Improve IDE support
if (target.type != parser::target_interface) {
msources[""].insert("cmake.toml");
} }
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) { // If there are only conditional sources we generate a 'set' to
auto sources = expand_cmake_paths(condition_sources, path, root_project); // create an empty source list. The rest is then appended using
// 'list(APPEND ...)'
auto has_sources = false;
for (const auto &itr : msources) {
if (!itr.second.empty()) {
has_sources = true;
break;
}
}
auto sources_var = target.name + "_SOURCES";
auto sources_with_set = true;
if (has_sources && msources[""].empty()) {
sources_with_set = false;
cmd("set")(sources_var, RawArg("\"\"")).endl();
}
gen.handle_condition(msources, [&](const std::string &condition, const tsl::ordered_set<std::string> &source_set) {
std::vector<std::string> condition_sources;
condition_sources.reserve(source_set.size());
for (const auto &source : source_set) {
condition_sources.push_back(source);
}
auto sources = expand_cmake_paths(condition_sources, path, is_root_project);
if (sources.empty()) { if (sources.empty()) {
auto source_key = condition.empty() ? "sources" : (condition + ".sources"); auto source_key = condition.empty() ? "sources" : (condition + ".sources");
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files"); throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
} }
// Do not add cmake.toml twice if (sources_with_set) {
if (!added_toml && std::find(sources.begin(), sources.end(), "cmake.toml") != sources.end()) { // This is a sanity check to make sure the unconditional sources are first
added_toml = true; if (!condition.empty()) {
throw std::runtime_error("Unreachable code, make sure unconditional sources are first");
}
cmd("set")(sources_var, sources);
sources_with_set = false;
} else {
cmd("list")("APPEND", sources_var, sources);
} }
cmd("list")("APPEND", sources_var, sources);
}); });
auto target_type = target.type; auto target_type = target.type;
if (tmplate != nullptr) { if (tmplate != nullptr) {
if (target_type != parser::target_template) {
throw std::runtime_error("Unreachable code, unexpected target type for template");
}
target_type = tmplate->outline.type; target_type = tmplate->outline.type;
} }
if (!added_toml && target_type != parser::target_interface) {
cmd("list")("APPEND", sources_var, std::vector<std::string>{"cmake.toml"}).endl();
}
cmd("set")("CMKR_SOURCES", "${" + sources_var + "}");
std::string add_command; std::string add_command;
std::string target_type_string; std::string target_type_string;
std::string target_scope; std::string target_scope;
@@ -905,34 +1023,20 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}"); cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}");
} else { } else {
cmd(add_command)(target.name, target_type_string).endl(); cmd(add_command)(target.name, target_type_string).endl();
if (has_sources) {
// clang-format off cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE",
cmd("if")(sources_var); "${" + sources_var + "}");
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}"); }
cmd("endif")().endl();
// clang-format on
} }
} else { } else {
cmd(add_command)(target.name, target_type_string).endl(); cmd(add_command)(target.name, target_type_string).endl();
if (has_sources) {
// clang-format off
cmd("if")(sources_var);
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}"); cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
cmd("endif")().endl(); }
// clang-format on
} }
// The first executable target will become the Visual Studio startup project // TODO: support sources from other directories
if (target_type == parser::target_executable) { if (has_sources) {
cmd("get_directory_property")("CMKR_VS_STARTUP_PROJECT", "DIRECTORY", "${PROJECT_SOURCE_DIR}", "DEFINITION", "VS_STARTUP_PROJECT");
// clang-format off
cmd("if")("NOT", "CMKR_VS_STARTUP_PROJECT");
cmd("set_property")("DIRECTORY", "${PROJECT_SOURCE_DIR}", "PROPERTY", "VS_STARTUP_PROJECT", target.name);
cmd("endif")().endl();
// clang-format on
}
if (!target.sources.empty()) {
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + sources_var + "}").endl(); cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + sources_var + "}").endl();
} }
@@ -985,23 +1089,39 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
} }
gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &properties) { gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &properties) {
for (const auto &propItr : properties) {
if (propItr.first == "MSVC_RUNTIME_LIBRARY") {
if (project_root->project_msvc_runtime == parser::msvc_last) {
throw std::runtime_error("You cannot set [target].msvc-runtime without setting the root [project].msvc-runtime");
}
}
}
cmd("set_target_properties")(target.name, "PROPERTIES", properties); cmd("set_target_properties")(target.name, "PROPERTIES", properties);
}); });
} }
// The first executable target will become the Visual Studio startup project
// TODO: this is not working properly
if (target_type == parser::target_executable) {
cmd("get_directory_property")("CMKR_VS_STARTUP_PROJECT", "DIRECTORY", "${PROJECT_SOURCE_DIR}", "DEFINITION", "VS_STARTUP_PROJECT");
// clang-format off
cmd("if")("NOT", "CMKR_VS_STARTUP_PROJECT");
cmd("set_property")("DIRECTORY", "${PROJECT_SOURCE_DIR}", "PROPERTY", "VS_STARTUP_PROJECT", target.name);
cmd("endif")().endl();
// clang-format on
}
// Generate the include after
if (!has_include_before && has_include_after) {
cmd("set")("CMKR_TARGET", target.name);
}
gen.conditional_includes(target.include_after); gen.conditional_includes(target.include_after);
gen.conditional_cmake(target.cmake_after); gen.conditional_cmake(target.cmake_after);
if (tmplate != nullptr) { if (tmplate != nullptr) {
gen.conditional_includes(tmplate->outline.include_after); gen.conditional_includes(tmplate->outline.include_after);
gen.conditional_cmake(tmplate->outline.cmake_after); gen.conditional_cmake(tmplate->outline.cmake_after);
} }
cmd("unset")("CMKR_TARGET");
cmd("unset")("CMKR_SOURCES");
} }
endl();
} }
if (!project.tests.empty()) { if (!project.tests.empty()) {
@@ -1027,7 +1147,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto dirs = std::make_pair("DIRS", inst.dirs); auto dirs = std::make_pair("DIRS", inst.dirs);
std::vector<std::string> files_data; std::vector<std::string> files_data;
if (!inst.files.empty()) { if (!inst.files.empty()) {
files_data = expand_cmake_paths(inst.files, path, root_project); files_data = expand_cmake_paths(inst.files, path, is_root_project);
if (files_data.empty()) { if (files_data.empty()) {
throw std::runtime_error("[[install]] files wildcard did not resolve to any files"); throw std::runtime_error("[[install]] files wildcard did not resolve to any files");
} }
@@ -1040,8 +1160,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
component_name = inst.targets.front(); component_name = inst.targets.front();
} }
auto component = std::make_pair("COMPONENT", component_name); auto component = std::make_pair("COMPONENT", component_name);
auto optional = inst.optional ? "OPTIONAL" : "";
ConditionScope cs(gen, inst.condition); ConditionScope cs(gen, inst.condition);
cmd("install")(targets, dirs, files, configs, destination, component); cmd("install")(targets, dirs, files, configs, destination, component, optional);
} }
} }
+10 -4
View File
@@ -1,15 +1,21 @@
#include "error.hpp" #include "error.hpp"
#include <cassert> #include <cassert>
#include <cstddef>
namespace cmkr { namespace cmkr {
namespace error { namespace error {
Status::Status(Code ec) noexcept : ec_(ec) {} Status::Status(Code ec) noexcept : ec_(ec) {
}
Status::operator int() const noexcept { return static_cast<int>(ec_); } Status::operator int() const noexcept {
return static_cast<int>(ec_);
}
Status::Code Status::code() const noexcept { return ec_; } Status::Code Status::code() const noexcept {
return ec_;
}
} // namespace error } // namespace error
} // namespace cmkr } // namespace cmkr
@@ -20,6 +26,6 @@ static const char *err_string[] = {
}; };
const char *cmkr_error_status(int i) { const char *cmkr_error_status(int i) {
assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string)))); assert(i >= 0 && static_cast<size_t>(i) < (sizeof(err_string) / sizeof(*(err_string))));
return err_string[i]; return err_string[i];
} }
+9 -3
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(
@@ -22,6 +24,10 @@ arguments:
} // namespace help } // namespace help
} // namespace cmkr } // namespace cmkr
const char *cmkr_help_version(void) { return cmkr::help::version(); } const char *cmkr_help_version(void) {
return cmkr::help::version();
}
const char *cmkr_help_message(void) { return cmkr::help::message(); } const char *cmkr_help_message(void) {
return cmkr::help::message();
}
+84 -13
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,6 +19,17 @@ 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_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
@@ -54,8 +63,10 @@ class TomlChecker {
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) {} }
TomlChecker(const TomlBasicValue &v) : m_v(v) {
}
TomlChecker(const TomlChecker &) = delete; TomlChecker(const TomlChecker &) = delete;
TomlChecker(TomlChecker &&) = delete; TomlChecker(TomlChecker &&) = delete;
@@ -109,9 +120,13 @@ 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()) {
@@ -152,7 +167,8 @@ class TomlCheckerRoot {
bool m_checked = false; bool m_checked = false;
public: public:
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {} TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
}
TomlCheckerRoot(const TomlCheckerRoot &) = delete; TomlCheckerRoot(const TomlCheckerRoot &) = delete;
TomlCheckerRoot(TomlCheckerRoot &&) = delete; TomlCheckerRoot(TomlCheckerRoot &&) = delete;
@@ -185,7 +201,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() + "'");
@@ -269,6 +285,21 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
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 std::runtime_error(format_key_error(error, msvc_runtime, project.find("msvc-runtime")));
}
}
} }
if (checker.contains("subdir")) { if (checker.contains("subdir")) {
@@ -480,9 +511,18 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
throw std::runtime_error(format_key_error(error, target.type_name, t.find("type"))); throw std::runtime_error(format_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);
@@ -507,10 +547,32 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
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()) { Condition<std::string> msvc_runtime;
auto &sources = target.sources.nth(0).value(); t.optional("msvc-runtime", msvc_runtime);
const auto &headers = target.headers.nth(0)->second; for (const auto &condItr : msvc_runtime) {
sources.insert(sources.end(), headers.begin(), headers.end()); switch (parse_msvcRuntimeType(condItr.second)) {
case msvc_dynamic:
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
break;
case msvc_static:
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
break;
default: {
std::string error = "Unknown runtime '" + condItr.second + "'\n";
error += "Available types:\n";
for (std::string type_name : msvcRuntimeTypeNames) {
error += " - " + type_name + "\n";
}
error.pop_back(); // Remove last newline
const TomlBasicValue *report;
if (condItr.first.empty()) {
report = &t.find("msvc-runtime");
} else {
report = &t.find(condItr.first).as_table().find("msvc-runtime").value();
}
throw std::runtime_error(format_key_error(error, condItr.second, *report));
}
}
} }
t.optional("condition", target.condition); t.optional("condition", target.condition);
@@ -619,6 +681,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 +690,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;
@@ -652,6 +716,13 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
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 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)) {
+20 -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,19 @@ 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()
+7
View File
@@ -45,3 +45,10 @@ 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"]
+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)!");
}