mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c76956450a |
+1
-1
@@ -10,7 +10,7 @@ AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
|
||||
@@ -9,4 +9,3 @@ build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
CMakeLists.txt.user
|
||||
.vscode/
|
||||
Generated
+42
-15
@@ -7,14 +7,17 @@ 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")
|
||||
endif()
|
||||
|
||||
# Regenerate CMakeLists.txt automatically in the root project
|
||||
set(CMKR_ROOT_PROJECT OFF)
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CMKR_ROOT_PROJECT ON)
|
||||
|
||||
# Enable folder support
|
||||
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)
|
||||
endif()
|
||||
|
||||
@@ -22,7 +25,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.17
|
||||
0.2.12
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
@@ -30,7 +33,7 @@ project(cmkr
|
||||
include("cmake/generate_documentation.cmake")
|
||||
include("cmake/generate_resources.cmake")
|
||||
|
||||
# Subdirectory: third_party
|
||||
# third_party
|
||||
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||
if(CMAKE_FOLDER)
|
||||
set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party")
|
||||
@@ -40,7 +43,7 @@ endif()
|
||||
add_subdirectory(third_party)
|
||||
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||
|
||||
# Subdirectory: tests
|
||||
# tests
|
||||
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||
if(CMAKE_FOLDER)
|
||||
set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests")
|
||||
@@ -50,14 +53,29 @@ endif()
|
||||
add_subdirectory(tests)
|
||||
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)
|
||||
|
||||
set(CMKR_TARGET cmkr_generate_documentation)
|
||||
if(cmkr_generate_documentation_SOURCES)
|
||||
target_sources(cmkr_generate_documentation INTERFACE ${cmkr_generate_documentation_SOURCES})
|
||||
endif()
|
||||
|
||||
generate_documentation()
|
||||
|
||||
# Target: cmkr
|
||||
set(cmkr_SOURCES
|
||||
unset(CMKR_TARGET)
|
||||
unset(CMKR_SOURCES)
|
||||
|
||||
# Target cmkr
|
||||
set(CMKR_TARGET cmkr)
|
||||
set(cmkr_SOURCES "")
|
||||
|
||||
list(APPEND cmkr_SOURCES
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
@@ -75,12 +93,24 @@ set(cmkr_SOURCES
|
||||
"include/project_parser.hpp"
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
)
|
||||
|
||||
list(APPEND cmkr_SOURCES
|
||||
cmake.toml
|
||||
)
|
||||
|
||||
set(CMKR_SOURCES ${cmkr_SOURCES})
|
||||
add_executable(cmkr)
|
||||
|
||||
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
|
||||
if(cmkr_SOURCES)
|
||||
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
|
||||
endif()
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmkr)
|
||||
endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES})
|
||||
|
||||
target_compile_features(cmkr PRIVATE
|
||||
@@ -99,14 +129,11 @@ target_link_libraries(cmkr PRIVATE
|
||||
nlohmann_json
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
set(CMKR_TARGET cmkr)
|
||||
generate_resources(${CMKR_TARGET})
|
||||
|
||||
unset(CMKR_TARGET)
|
||||
unset(CMKR_SOURCES)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
cmkr
|
||||
|
||||
@@ -64,10 +64,9 @@ arguments:
|
||||
|
||||
## Credits
|
||||
|
||||
- [gulrak/filesystem](https://github.com/gulrak/filesystem)
|
||||
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
|
||||
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
|
||||
- [mpark/variant](https://github.com/mpark/variant)
|
||||
- [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 ❤️
|
||||
- https://github.com/gulrak/filesystem
|
||||
- https://github.com/Tessil/ordered-map
|
||||
- https://github.com/ToruNiina/toml11
|
||||
- https://github.com/mpark/variant
|
||||
- https://www.svgrepo.com/svg/192268/hammer
|
||||
- https://github.com/can1357 for buying `cmkr.build` ❤️
|
||||
|
||||
+2
-2
@@ -4,14 +4,14 @@ cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.17"
|
||||
version = "0.2.12"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
subdirs = ["third_party", "tests"]
|
||||
include-after = [
|
||||
"cmake/generate_documentation.cmake",
|
||||
"cmake/generate_resources.cmake"
|
||||
]
|
||||
subdirs = ["third_party", "tests"]
|
||||
|
||||
[target.cmkr_generate_documentation]
|
||||
type = "interface"
|
||||
|
||||
+5
-22
@@ -2,7 +2,7 @@ include_guard()
|
||||
|
||||
# Change these defaults to point to your infrastructure if desired
|
||||
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
|
||||
set(CMKR_TAG "v0.2.17" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.12" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
|
||||
|
||||
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
|
||||
@@ -58,7 +58,7 @@ else()
|
||||
endif()
|
||||
|
||||
# Use cached cmkr if found
|
||||
if(DEFINED ENV{CMKR_CACHE})
|
||||
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
|
||||
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
|
||||
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
|
||||
@@ -72,27 +72,10 @@ endif()
|
||||
set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
|
||||
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
|
||||
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
|
||||
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD)
|
||||
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE)
|
||||
if(CMKR_STARTSWITH_BUILD)
|
||||
if(DEFINED ENV{CMKR_CACHE})
|
||||
if(CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
|
||||
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
|
||||
message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
|
||||
if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
|
||||
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
|
||||
@@ -103,7 +86,7 @@ if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
|
||||
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
|
||||
unset(CMKR_EXECUTABLE CACHE)
|
||||
endif()
|
||||
elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE)
|
||||
elseif(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}" AND CMKR_EXECUTABLE MATCHES "^${CMKR_DIRECTORY_PREFIX}")
|
||||
message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
|
||||
unset(CMKR_EXECUTABLE CACHE)
|
||||
endif()
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
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)
|
||||
if(CMKR_GENERATE_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
|
||||
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")
|
||||
|
||||
+4
-10
@@ -27,7 +27,6 @@ name = "myproject"
|
||||
version = "1.0.0"
|
||||
description = "Description of the project"
|
||||
languages = ["C", "CXX"]
|
||||
msvc-runtime = "" # dynamic (implicit default), static
|
||||
cmake-before = """
|
||||
message(STATUS "CMake injected before the project() call")
|
||||
"""
|
||||
@@ -44,8 +43,8 @@ You can specify your own conditions and use them in any `condition` field:
|
||||
|
||||
```toml
|
||||
[conditions]
|
||||
arch64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
arch32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8"
|
||||
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4"
|
||||
```
|
||||
|
||||
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
|
||||
@@ -59,8 +58,6 @@ sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
```
|
||||
|
||||
### Predefined conditions
|
||||
|
||||
The following conditions are predefined (you can override them if you desire):
|
||||
|
||||
```toml
|
||||
@@ -147,7 +144,6 @@ alias = "mytarget::mytarget"
|
||||
type = "static" # executable, shared (DLL), static, interface, object, library, custom
|
||||
headers = ["src/mytarget.h"]
|
||||
sources = ["src/mytarget.cpp"]
|
||||
msvc-runtime = "" # dynamic (implicit default), static
|
||||
|
||||
# The keys below match the target_xxx CMake commands
|
||||
# Keys prefixed with private- will get PRIVATE visibility
|
||||
@@ -204,9 +200,7 @@ working-directory = "mytest-dir"
|
||||
condition = "mycondition"
|
||||
targets = ["mytarget", "mytest"]
|
||||
destination = ["bin"]
|
||||
component = "mycomponent"
|
||||
files = ["content/my.png"]
|
||||
dirs = ["include"]
|
||||
configs = ["Release", "Debug"]
|
||||
optional = false
|
||||
dirs = [""]
|
||||
configs = [""]
|
||||
```
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
# Automatically generated from tests/msvc-runtime/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Static MSVC runtime
|
||||
permalink: /examples/msvc-runtime
|
||||
parent: Examples
|
||||
nav_order: 8
|
||||
---
|
||||
|
||||
# Static MSVC runtime
|
||||
|
||||
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "msvc-runtime"
|
||||
description = "Static MSVC runtime"
|
||||
msvc-runtime = "static"
|
||||
|
||||
# This target will compile with a static runtime
|
||||
[target.static-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
|
||||
# This target overrides the [project].msvc-runtime
|
||||
[target.dynamic-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc-runtime = "dynamic"
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml).</sub></sup>
|
||||
+5
-7
@@ -70,10 +70,8 @@ arguments:
|
||||
|
||||
## Credits
|
||||
|
||||
- [gulrak/filesystem](https://github.com/gulrak/filesystem)
|
||||
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
|
||||
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
|
||||
- [mpark/variant](https://github.com/mpark/variant)
|
||||
- [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 ❤️
|
||||
- https://github.com/gulrak/filesystem
|
||||
- https://github.com/Tessil/ordered-map
|
||||
- https://github.com/ToruNiina/toml11
|
||||
- https://github.com/mpark/variant
|
||||
- https://www.svgrepo.com/svg/192268/hammer
|
||||
|
||||
+10
-14
@@ -3,7 +3,6 @@
|
||||
#include <mpark/variant.hpp>
|
||||
#include <string>
|
||||
#include <tsl/ordered_map.h>
|
||||
#include <tsl/ordered_set.h>
|
||||
#include <vector>
|
||||
|
||||
namespace cmkr {
|
||||
@@ -28,7 +27,7 @@ struct Option {
|
||||
bool val = false;
|
||||
};
|
||||
|
||||
struct Package {
|
||||
struct FindPackage {
|
||||
std::string name;
|
||||
std::string condition;
|
||||
std::string version;
|
||||
@@ -68,6 +67,7 @@ struct Target {
|
||||
TargetType type = target_last;
|
||||
std::string type_name;
|
||||
|
||||
ConditionVector headers;
|
||||
ConditionVector sources;
|
||||
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
|
||||
@@ -128,7 +128,6 @@ struct Install {
|
||||
std::vector<std::string> configs;
|
||||
std::string destination;
|
||||
std::string component;
|
||||
bool optional = false;
|
||||
};
|
||||
|
||||
struct Subdir {
|
||||
@@ -152,17 +151,15 @@ struct Content {
|
||||
ConditionVector include_after;
|
||||
};
|
||||
|
||||
enum MsvcRuntimeType {
|
||||
msvc_dynamic,
|
||||
msvc_static,
|
||||
msvc_last,
|
||||
struct Package {
|
||||
std::string name;
|
||||
std::vector<std::string> targets;
|
||||
std::vector<std::string> headers;
|
||||
std::vector<std::string> dependencies;
|
||||
std::string namespace_name;
|
||||
};
|
||||
|
||||
extern const char *msvcRuntimeTypeNames[msvc_last];
|
||||
|
||||
struct Project {
|
||||
const Project *parent;
|
||||
|
||||
// This is the CMake version required to use all cmkr versions.
|
||||
std::string cmake_version = "3.15";
|
||||
std::string cmkr_include = "cmkr.cmake";
|
||||
@@ -180,15 +177,15 @@ struct Project {
|
||||
std::string project_version;
|
||||
std::string project_description;
|
||||
std::vector<std::string> project_languages;
|
||||
MsvcRuntimeType project_msvc_runtime = msvc_last;
|
||||
Condition<std::string> cmake_before;
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
ConditionVector include_after;
|
||||
std::vector<Setting> settings;
|
||||
std::vector<Option> options;
|
||||
std::vector<Package> packages;
|
||||
std::vector<FindPackage> packages;
|
||||
Vcpkg vcpkg;
|
||||
Package package;
|
||||
std::vector<Content> contents;
|
||||
std::vector<Template> templates;
|
||||
std::vector<Target> targets;
|
||||
@@ -198,7 +195,6 @@ struct Project {
|
||||
std::vector<Subdir> subdirs;
|
||||
|
||||
Project(const Project *parent, const std::string &path, bool build);
|
||||
const Project *root() const;
|
||||
};
|
||||
|
||||
bool is_root_path(const std::string &path);
|
||||
|
||||
+210
-227
@@ -4,7 +4,6 @@
|
||||
#include <resources/cmkr.hpp>
|
||||
|
||||
#include "fs.hpp"
|
||||
#include "project_parser.hpp"
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@@ -26,20 +25,20 @@ static std::string format(const char *format, tsl::ordered_map<std::string, std:
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool is_root_project) {
|
||||
std::vector<std::string> temp;
|
||||
static std::string extract_suffix(const fs::path &base, const fs::path &full) {
|
||||
auto fullpath = full.string();
|
||||
auto base_len = base.string().length();
|
||||
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
|
||||
return delet;
|
||||
}
|
||||
|
||||
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
|
||||
auto fullpath = full.string();
|
||||
auto base_len = base.string().length();
|
||||
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
|
||||
return delet;
|
||||
};
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool root_project) {
|
||||
std::vector<std::string> temp;
|
||||
|
||||
auto stem = name.filename().stem().string();
|
||||
auto ext = name.extension();
|
||||
|
||||
if (is_root_project && stem == "**" && name == name.filename()) {
|
||||
if (root_project && stem == "**" && name == name.filename()) {
|
||||
throw std::runtime_error("Recursive globbing not allowed in project root: " + name.string());
|
||||
}
|
||||
|
||||
@@ -67,11 +66,11 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool is_root_project) {
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool root_project) {
|
||||
// TODO: add duplicate checking
|
||||
std::vector<std::string> result;
|
||||
for (const auto &src : sources) {
|
||||
auto expanded = expand_cmake_path(src, toml_dir, is_root_project);
|
||||
auto expanded = expand_cmake_path(src, toml_dir, root_project);
|
||||
for (const auto &f : expanded) {
|
||||
result.push_back(f);
|
||||
}
|
||||
@@ -90,24 +89,8 @@ static void create_file(const fs::path &path, const std::string &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) {
|
||||
const auto name = escape_project_name(fs::current_path().stem().string());
|
||||
const auto name = fs::current_path().stem().string();
|
||||
if (fs::exists(fs::current_path() / "cmake.toml")) {
|
||||
throw std::runtime_error("Cannot initialize a project when cmake.toml already exists!");
|
||||
}
|
||||
@@ -143,17 +126,13 @@ void generate_project(const std::string &type) {
|
||||
|
||||
struct CommandEndl {
|
||||
std::stringstream &ss;
|
||||
CommandEndl(std::stringstream &ss) : ss(ss) {
|
||||
}
|
||||
void endl() {
|
||||
ss << '\n';
|
||||
}
|
||||
CommandEndl(std::stringstream &ss) : ss(ss) {}
|
||||
void endl() { ss << '\n'; }
|
||||
};
|
||||
|
||||
struct RawArg {
|
||||
RawArg() = default;
|
||||
RawArg(std::string arg) : arg(std::move(arg)) {
|
||||
}
|
||||
RawArg(std::string arg) : arg(std::move(arg)) {}
|
||||
|
||||
std::string arg;
|
||||
};
|
||||
@@ -169,8 +148,7 @@ struct 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) {
|
||||
if (!generated) {
|
||||
@@ -184,11 +162,10 @@ struct Command {
|
||||
return "\"\"";
|
||||
}
|
||||
// Don't quote arguments that don't need quoting
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#unquoted-argument
|
||||
// NOTE: Normally '/' does not require quoting according to the documentation but this has been the case here
|
||||
// previously, so for backwards compatibility its still here.
|
||||
if (str.find_first_of("()#\"\\'> |/;") == str.npos)
|
||||
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos &&
|
||||
str.find(';') == std::string::npos) {
|
||||
return str;
|
||||
}
|
||||
std::string result;
|
||||
result += "\"";
|
||||
for (char ch : str) {
|
||||
@@ -257,9 +234,16 @@ struct Command {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static bool is_empty(const T &v) {
|
||||
return v.empty();
|
||||
}
|
||||
|
||||
static bool is_empty(const char *v) { return *v == '\0'; }
|
||||
|
||||
template <class K, class V>
|
||||
bool print_arg(const std::pair<K, V> &kv) {
|
||||
if (kv.second.empty()) {
|
||||
if (is_empty(kv.second)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -273,7 +257,7 @@ struct Command {
|
||||
}
|
||||
|
||||
bool print_arg(const RawArg &arg) {
|
||||
if (arg.arg.empty()) {
|
||||
if (is_empty(arg.arg)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -339,8 +323,7 @@ static std::string tolf(const std::string &str) {
|
||||
};
|
||||
|
||||
struct Generator {
|
||||
Generator(const parser::Project &project) : project(project) {
|
||||
}
|
||||
Generator(const parser::Project &project) : project(project) {}
|
||||
Generator(const Generator &) = delete;
|
||||
|
||||
const parser::Project &project;
|
||||
@@ -366,9 +349,7 @@ struct Generator {
|
||||
return CommandEndl(ss);
|
||||
}
|
||||
|
||||
void endl() {
|
||||
ss << '\n';
|
||||
}
|
||||
void endl() { ss << '\n'; }
|
||||
|
||||
void inject_includes(const std::vector<std::string> &includes) {
|
||||
if (!includes.empty()) {
|
||||
@@ -417,10 +398,9 @@ struct Generator {
|
||||
}
|
||||
|
||||
if (!condition.empty()) {
|
||||
cmd("endif")().endl();
|
||||
} else if (!itr.second.empty()) {
|
||||
endl();
|
||||
cmd("endif")();
|
||||
}
|
||||
endl();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,7 +487,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
// Root project doesn't have a parent
|
||||
auto is_root_project = parent_project == nullptr;
|
||||
auto root_project = parent_project == nullptr;
|
||||
|
||||
parser::Project project(parent_project, path, false);
|
||||
Generator gen(project);
|
||||
@@ -523,27 +503,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
comment("See " + cmkr_url + " for more information");
|
||||
endl();
|
||||
|
||||
if (is_root_project) {
|
||||
if (root_project) {
|
||||
cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl();
|
||||
|
||||
if (project.project_msvc_runtime != parser::msvc_last) {
|
||||
comment("Enable support for MSVC_RUNTIME_LIBRARY");
|
||||
cmd("cmake_policy")("SET", "CMP0091", "NEW");
|
||||
|
||||
switch (project.project_msvc_runtime) {
|
||||
case parser::msvc_dynamic:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL");
|
||||
break;
|
||||
case parser::msvc_static:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
endl();
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
if (!project.allow_in_tree) {
|
||||
// clang-format off
|
||||
cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR");
|
||||
@@ -552,13 +514,14 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
comment("Regenerate CMakeLists.txt automatically in the root project");
|
||||
cmd("set")("CMKR_ROOT_PROJECT", "OFF");
|
||||
// clang-format off
|
||||
cmd("if")("CMAKE_CURRENT_SOURCE_DIR", "STREQUAL", "CMAKE_SOURCE_DIR");
|
||||
cmd("set")("CMKR_ROOT_PROJECT", "ON").endl();
|
||||
|
||||
if (!project.cmkr_include.empty()) {
|
||||
comment("Bootstrap cmkr and automatically regenerate CMakeLists.txt");
|
||||
comment("Bootstrap cmkr");
|
||||
cmd("include")(project.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT");
|
||||
cmd("if")("CMKR_INCLUDE_RESULT");
|
||||
cmd("cmkr")();
|
||||
@@ -566,10 +529,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
comment("Enable folder support");
|
||||
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("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
|
||||
@@ -577,15 +537,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()) {
|
||||
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
|
||||
if (!project.cppflags.empty()) {
|
||||
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
|
||||
@@ -621,7 +581,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
endl();
|
||||
}
|
||||
|
||||
// TODO: rename to 'variables'?
|
||||
if (!project.settings.empty()) {
|
||||
comment("Settings");
|
||||
for (const auto &set : project.settings) {
|
||||
@@ -651,19 +610,15 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
auto version = std::make_pair("VERSION", project.project_version);
|
||||
auto description = std::make_pair("DESCRIPTION", project.project_description);
|
||||
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_cmake(project.cmake_after);
|
||||
|
||||
if (!project.vcpkg.packages.empty()) {
|
||||
if (!root_project) {
|
||||
throw std::runtime_error("[vcpkg] has to be in the project root");
|
||||
}
|
||||
// Allow the user to specify a url or derive it from the version
|
||||
auto url = project.vcpkg.url;
|
||||
auto version_name = url;
|
||||
@@ -688,12 +643,8 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("include")("FetchContent");
|
||||
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
|
||||
cmd("FetchContent_Declare")("vcpkg", "URL", url);
|
||||
// Not using FetchContent_MakeAvailable here in case vcpkg adds CMakeLists.txt
|
||||
cmd("FetchContent_GetProperties")("vcpkg");
|
||||
cmd("if")("NOT", "vcpkg_POPULATED");
|
||||
cmd("FetchContent_Populate")("vcpkg");
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
cmd("endif")();
|
||||
cmd("FetchContent_MakeAvailable")("vcpkg");
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
cmd("endif")();
|
||||
endl();
|
||||
// clang-format on
|
||||
@@ -801,7 +752,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
|
||||
auto add_subdir = [&](const std::string &dir) {
|
||||
// clang-format off
|
||||
comment("Subdirectory: " + dir);
|
||||
comment(dir);
|
||||
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
|
||||
cmd("if")("CMAKE_FOLDER");
|
||||
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
|
||||
@@ -811,19 +762,17 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
// clang-format on
|
||||
|
||||
cmd("add_subdirectory")(dir);
|
||||
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}");
|
||||
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
|
||||
};
|
||||
|
||||
// generate_cmake is called on the subdirectories recursively later
|
||||
if (!project.project_subdirs.empty()) {
|
||||
gen.handle_condition(project.project_subdirs, [&](const std::string &, const std::vector<std::string> &subdirs) {
|
||||
for (size_t i = 0; i < subdirs.size(); i++) {
|
||||
add_subdir(subdirs[i]);
|
||||
if (i + 1 < subdirs.size()) {
|
||||
endl();
|
||||
}
|
||||
for (const auto &dir : subdirs) {
|
||||
add_subdir(dir);
|
||||
}
|
||||
});
|
||||
endl();
|
||||
}
|
||||
for (const auto &subdir : project.subdirs) {
|
||||
ConditionScope cs(gen, subdir.condition);
|
||||
@@ -832,20 +781,22 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
gen.conditional_cmake(subdir.cmake_before);
|
||||
|
||||
add_subdir(subdir.name);
|
||||
endl();
|
||||
|
||||
gen.conditional_includes(subdir.include_after);
|
||||
gen.conditional_cmake(subdir.cmake_after);
|
||||
}
|
||||
|
||||
if (!project.targets.empty()) {
|
||||
auto project_root = project.root();
|
||||
for (size_t i = 0; i < project.targets.size(); i++) {
|
||||
if (i > 0) {
|
||||
endl();
|
||||
}
|
||||
|
||||
const auto &target = project.targets[i];
|
||||
const parser::Template *tmplate = nullptr;
|
||||
std::unique_ptr<ConditionScope> tmplate_cs{};
|
||||
|
||||
comment("Target: " + target.name);
|
||||
comment("Target " + target.name);
|
||||
|
||||
// Check if this target is using a template.
|
||||
if (target.type == parser::target_template) {
|
||||
@@ -859,115 +810,56 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
|
||||
ConditionScope cs(gen, target.condition);
|
||||
|
||||
// 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);
|
||||
}
|
||||
cmd("set")("CMKR_TARGET", target.name);
|
||||
|
||||
// Generate the include before
|
||||
if (has_include_before) {
|
||||
cmd("set")("CMKR_TARGET", target.name);
|
||||
}
|
||||
if (tmplate != nullptr) {
|
||||
gen.conditional_includes(tmplate->outline.include_before);
|
||||
gen.conditional_cmake(tmplate->outline.cmake_before);
|
||||
}
|
||||
|
||||
gen.conditional_includes(target.include_before);
|
||||
gen.conditional_cmake(target.cmake_before);
|
||||
|
||||
// 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();
|
||||
|
||||
auto merge_sources = [&msources](const parser::ConditionVector &sources) {
|
||||
for (const auto &itr : sources) {
|
||||
auto &source_list = msources[itr.first];
|
||||
for (const auto &source : itr.second) {
|
||||
source_list.insert(source);
|
||||
}
|
||||
}
|
||||
};
|
||||
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");
|
||||
}
|
||||
|
||||
// If there are only conditional sources we generate a 'set' to
|
||||
// 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();
|
||||
|
||||
bool added_toml = false;
|
||||
cmd("set")(sources_var, RawArg("\"\"")).endl();
|
||||
|
||||
if (tmplate != nullptr) {
|
||||
gen.handle_condition(tmplate->outline.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
|
||||
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");
|
||||
}
|
||||
if (sources_with_set) {
|
||||
// This is a sanity check to make sure the unconditional sources are first
|
||||
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);
|
||||
// Do not add cmake.toml twice
|
||||
if (!added_toml && std::find(sources.begin(), sources.end(), "cmake.toml") != sources.end()) {
|
||||
added_toml = true;
|
||||
}
|
||||
cmd("list")("APPEND", sources_var, sources);
|
||||
});
|
||||
|
||||
auto target_type = target.type;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 target_type_string;
|
||||
std::string target_scope;
|
||||
@@ -1023,20 +915,34 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}");
|
||||
} else {
|
||||
cmd(add_command)(target.name, target_type_string).endl();
|
||||
if (has_sources) {
|
||||
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE",
|
||||
"${" + sources_var + "}");
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
cmd("if")(sources_var);
|
||||
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
} else {
|
||||
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("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// TODO: support sources from other directories
|
||||
if (has_sources) {
|
||||
// The first executable target will become the Visual Studio startup project
|
||||
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
|
||||
}
|
||||
|
||||
if (!target.sources.empty()) {
|
||||
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + sources_var + "}").endl();
|
||||
}
|
||||
|
||||
@@ -1059,7 +965,23 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
target_cmd("target_compile_options", t.compile_options, target_scope);
|
||||
target_cmd("target_compile_options", t.private_compile_options, "PRIVATE");
|
||||
|
||||
target_cmd("target_include_directories", t.include_directories, target_scope);
|
||||
cmkr::parser::ConditionVector include_directories;
|
||||
for (const auto &itr : t.include_directories) {
|
||||
auto directories = itr.second;
|
||||
if (target_scope == "PUBLIC" || target_scope == "INTERFACE") {
|
||||
for (auto &directory : directories) {
|
||||
// TODO: properly detect if this is a relative path
|
||||
if (directory[0] != '$') {
|
||||
directory = "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/" + directory + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
include_directories[itr.first] = directories;
|
||||
}
|
||||
// TODO: this needs to be done cleaner, maybe the install command has some magic?
|
||||
include_directories[""].push_back("$<INSTALL_INTERFACE:include>");
|
||||
|
||||
target_cmd("target_include_directories", include_directories, target_scope);
|
||||
target_cmd("target_include_directories", t.private_include_directories, "PRIVATE");
|
||||
|
||||
target_cmd("target_link_directories", t.link_directories, target_scope);
|
||||
@@ -1089,39 +1011,23 @@ 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) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
// 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_cmake(target.cmake_after);
|
||||
|
||||
if (tmplate != nullptr) {
|
||||
gen.conditional_includes(tmplate->outline.include_after);
|
||||
gen.conditional_cmake(tmplate->outline.cmake_after);
|
||||
}
|
||||
|
||||
cmd("unset")("CMKR_TARGET");
|
||||
cmd("unset")("CMKR_SOURCES");
|
||||
}
|
||||
|
||||
endl();
|
||||
}
|
||||
|
||||
if (!project.tests.empty()) {
|
||||
@@ -1147,7 +1053,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
auto dirs = std::make_pair("DIRS", inst.dirs);
|
||||
std::vector<std::string> files_data;
|
||||
if (!inst.files.empty()) {
|
||||
files_data = expand_cmake_paths(inst.files, path, is_root_project);
|
||||
files_data = expand_cmake_paths(inst.files, path, root_project);
|
||||
if (files_data.empty()) {
|
||||
throw std::runtime_error("[[install]] files wildcard did not resolve to any files");
|
||||
}
|
||||
@@ -1160,12 +1066,89 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
component_name = inst.targets.front();
|
||||
}
|
||||
auto component = std::make_pair("COMPONENT", component_name);
|
||||
auto optional = inst.optional ? "OPTIONAL" : "";
|
||||
ConditionScope cs(gen, inst.condition);
|
||||
cmd("install")(targets, dirs, files, configs, destination, component, optional);
|
||||
cmd("install")(targets, dirs, files, configs, destination, component);
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.package.targets.empty()) {
|
||||
//TODO: generate an option to enable/disable installation
|
||||
|
||||
const auto &package = project.package;
|
||||
|
||||
if (!root_project) {
|
||||
throw std::runtime_error("[package] has to be in the project root");
|
||||
}
|
||||
comment("Project packaging");
|
||||
cmd("include")("CMakePackageConfigHelpers").endl();
|
||||
|
||||
comment("Install headers");
|
||||
tsl::ordered_map<std::string, std::vector<std::string>> header_tree;
|
||||
for (const auto &pattern : package.headers) {
|
||||
auto pattern_path = fs::path(pattern);
|
||||
auto pattern_parent = pattern_path.parent_path();
|
||||
auto headers = expand_cmake_path(pattern_path, path, root_project);
|
||||
for (const auto &header : headers) {
|
||||
auto install_path = extract_suffix(pattern_parent, header);
|
||||
auto install_destination = "include/" + fs::path(install_path).parent_path().string();
|
||||
header_tree[install_destination].push_back(header);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &itr : header_tree) {
|
||||
cmd("install")(std::make_pair("FILES", itr.second), std::make_pair("DESTINATION", itr.first));
|
||||
printf("Install destination: %s\n", itr.first.c_str());
|
||||
for (const auto &header : itr.second) {
|
||||
printf(" Header: %s\n", header.c_str());
|
||||
}
|
||||
}
|
||||
endl();
|
||||
|
||||
comment("Install target outputs and generate target exports");
|
||||
auto export_name = package.name + "Targets";
|
||||
cmd("install")(std::make_pair("TARGETS", package.targets), std::make_pair("EXPORT", export_name), "LIBRARY", "ARCHIVE", "RUNTIME");
|
||||
endl();
|
||||
|
||||
comment("Install generated target exports");
|
||||
auto cmake_destination = "lib/cmake/" + package.name;
|
||||
cmd("install")(std::make_pair("EXPORT", export_name), std::make_pair("FILE", export_name + ".cmake"),
|
||||
std::make_pair("NAMESPACE", package.namespace_name), std::make_pair("DESTINATION", cmake_destination));
|
||||
endl();
|
||||
|
||||
auto config_base = "${CMAKE_CURRENT_BINARY_DIR}/" + package.name;
|
||||
std::vector<std::string> generated_scripts;
|
||||
if (!project.project_version.empty()) {
|
||||
comment("Generate package version scripts");
|
||||
auto version_cmake = config_base + "ConfigVersion.cmake";
|
||||
cmd("write_basic_package_version_file")(version_cmake, std::make_pair("VERSION", "${PROJECT_VERSION}"),
|
||||
std::make_pair("COMPATIBILITY", "SameMajorVersion"));
|
||||
endl();
|
||||
|
||||
generated_scripts.push_back(version_cmake);
|
||||
}
|
||||
|
||||
comment("Generate package config scripts");
|
||||
cmd("set")("PACKAGE_NAME", package.name);
|
||||
// TODO: this needs some actual logic
|
||||
cmd("set")("PACKAGE_DEPENDENCIES", package.dependencies);
|
||||
auto config_cmake = config_base + "Config.cmake";
|
||||
auto config_in = config_cmake + ".in";
|
||||
cmd("file")("WRITE", config_in, RawArg(R"("include(CMakeFindDependencyMacro)\n\n")"));
|
||||
cmd("file")("APPEND", config_in, RawArg(R"("string(REGEX MATCHALL \"[^;]+\" SEPARATE_DEPENDENCIES \"@PACKAGE_DEPENDENCIES@\")\n")"));
|
||||
cmd("file")("APPEND", config_in, RawArg(R"("foreach(dependency ${SEPARATE_DEPENDENCIES})\n")"));
|
||||
cmd("file")("APPEND", config_in, RawArg(R"(" string(REPLACE \" \" \";\" args \"\${dependency}\")\n")"));
|
||||
cmd("file")("APPEND", config_in, RawArg(R"(" find_dependency(\${args})\n")"));
|
||||
cmd("file")("APPEND", config_in, RawArg(R"("endforeach()\n\n")"));
|
||||
cmd("file")("APPEND", config_in, RawArg(R"("include(\"\${CMAKE_CURRENT_LIST_DIR}/@PACKAGE_NAME@Targets.cmake\")\n")"));
|
||||
cmd("configure_file")(config_in, config_cmake, "@ONLY");
|
||||
generated_scripts.push_back(config_cmake);
|
||||
endl();
|
||||
|
||||
comment("Install generated package scripts");
|
||||
cmd("install")(std::make_pair("FILES", generated_scripts), std::make_pair("DESTINATION", cmake_destination));
|
||||
endl();
|
||||
}
|
||||
|
||||
// Generate CMakeLists.txt
|
||||
auto list_path = fs::path(path) / "CMakeLists.txt";
|
||||
|
||||
|
||||
+4
-10
@@ -1,21 +1,15 @@
|
||||
#include "error.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
namespace cmkr {
|
||||
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 cmkr
|
||||
@@ -26,6 +20,6 @@ static const char *err_string[] = {
|
||||
};
|
||||
|
||||
const char *cmkr_error_status(int i) {
|
||||
assert(i >= 0 && static_cast<size_t>(i) < (sizeof(err_string) / sizeof(*(err_string))));
|
||||
assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string))));
|
||||
return err_string[i];
|
||||
}
|
||||
|
||||
+3
-9
@@ -4,9 +4,7 @@
|
||||
namespace cmkr {
|
||||
namespace help {
|
||||
|
||||
const char *version() noexcept {
|
||||
return "cmkr version " CMKR_VERSION;
|
||||
}
|
||||
const char *version() noexcept { return "cmkr version " CMKR_VERSION; }
|
||||
|
||||
const char *message() noexcept {
|
||||
return R"lit(
|
||||
@@ -24,10 +22,6 @@ arguments:
|
||||
} // namespace help
|
||||
} // 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(); }
|
||||
+40
-87
@@ -4,6 +4,8 @@
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
#include <tsl/ordered_set.h>
|
||||
|
||||
namespace cmkr {
|
||||
namespace parser {
|
||||
@@ -19,17 +21,6 @@ static TargetType parse_targetType(const std::string &name) {
|
||||
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>;
|
||||
|
||||
static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
|
||||
@@ -63,15 +54,14 @@ class TomlChecker {
|
||||
tsl::ordered_set<toml::key> m_conditionVisited;
|
||||
|
||||
public:
|
||||
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, const toml::key &ky) : m_v(toml::find(v, ky)) {}
|
||||
TomlChecker(const TomlBasicValue &v) : m_v(v) {}
|
||||
TomlChecker(const TomlChecker &) = delete;
|
||||
TomlChecker(TomlChecker &&) = delete;
|
||||
|
||||
template <typename T>
|
||||
void optional(const toml::key &ky, Condition<T> &destination) {
|
||||
bool optional(const toml::key &ky, Condition<T> &destination) {
|
||||
auto found = false;
|
||||
// TODO: this algorithm in O(n) over the amount of keys, kinda bad
|
||||
const auto &table = m_v.as_table();
|
||||
for (const auto &itr : table) {
|
||||
@@ -80,9 +70,11 @@ class TomlChecker {
|
||||
if (value.is_table()) {
|
||||
if (value.contains(ky)) {
|
||||
destination[key] = toml::find<T>(value, ky);
|
||||
found = true;
|
||||
}
|
||||
} else if (key == ky) {
|
||||
destination[""] = toml::find<T>(m_v, ky);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,15 +85,19 @@ class TomlChecker {
|
||||
}
|
||||
}
|
||||
visit(ky);
|
||||
return found;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void optional(const toml::key &ky, T &destination) {
|
||||
bool optional(const toml::key &ky, T &destination) {
|
||||
auto found = false;
|
||||
// TODO: this currently doesn't allow you to get an optional map<string, X>
|
||||
if (m_v.contains(ky)) {
|
||||
destination = toml::find<T>(m_v, ky);
|
||||
found = true;
|
||||
}
|
||||
visit(ky);
|
||||
return found;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -120,13 +116,9 @@ class TomlChecker {
|
||||
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 {
|
||||
for (const auto &itr : m_v.as_table()) {
|
||||
@@ -167,8 +159,7 @@ class TomlCheckerRoot {
|
||||
bool m_checked = false;
|
||||
|
||||
public:
|
||||
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {
|
||||
}
|
||||
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {}
|
||||
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
|
||||
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
|
||||
|
||||
@@ -201,7 +192,7 @@ class TomlCheckerRoot {
|
||||
}
|
||||
};
|
||||
|
||||
Project::Project(const Project *parent, const std::string &path, bool build) : parent(parent) {
|
||||
Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
const auto toml_path = fs::path(path) / "cmake.toml";
|
||||
if (!fs::exists(toml_path)) {
|
||||
throw std::runtime_error("File not found '" + toml_path.string() + "'");
|
||||
@@ -285,21 +276,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
project.optional("include-before", include_before);
|
||||
project.optional("include-after", include_after);
|
||||
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")) {
|
||||
@@ -370,7 +346,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
|
||||
for (const auto &itr : pkgs) {
|
||||
Package p;
|
||||
FindPackage p;
|
||||
p.name = itr.first;
|
||||
const auto &value = itr.second;
|
||||
if (itr.second.is_string()) {
|
||||
@@ -511,18 +487,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
throw std::runtime_error(format_key_error(error, target.type_name, t.find("type")));
|
||||
}
|
||||
|
||||
t.optional("headers", target.headers);
|
||||
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("private-compile-definitions", target.private_compile_definitions);
|
||||
|
||||
@@ -547,32 +514,10 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
t.optional("precompile-headers", target.precompile_headers);
|
||||
t.optional("private-precompile-headers", target.private_precompile_headers);
|
||||
|
||||
Condition<std::string> msvc_runtime;
|
||||
t.optional("msvc-runtime", msvc_runtime);
|
||||
for (const auto &condItr : msvc_runtime) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (!target.headers.empty()) {
|
||||
auto &sources = target.sources.nth(0).value();
|
||||
const auto &headers = target.headers.nth(0)->second;
|
||||
sources.insert(sources.end(), headers.begin(), headers.end());
|
||||
}
|
||||
|
||||
t.optional("condition", target.condition);
|
||||
@@ -681,7 +626,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
i.optional("configs", inst.configs);
|
||||
i.required("destination", inst.destination);
|
||||
i.optional("component", inst.component);
|
||||
i.optional("optional", inst.optional);
|
||||
installs.push_back(inst);
|
||||
}
|
||||
}
|
||||
@@ -690,7 +634,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
auto &v = checker.create(toml, "vcpkg");
|
||||
v.optional("url", vcpkg.url);
|
||||
v.optional("version", vcpkg.version);
|
||||
|
||||
for (const auto &p : v.find("packages").as_array()) {
|
||||
Vcpkg::Package package;
|
||||
const auto &package_str = p.as_string().str;
|
||||
@@ -713,14 +656,24 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
}
|
||||
}
|
||||
|
||||
checker.check(conditions, true);
|
||||
}
|
||||
if(checker.contains("package")) {
|
||||
auto& p = checker.create(toml, "package");
|
||||
if(!p.optional("name", package.name)) {
|
||||
package.name = project_name;
|
||||
}
|
||||
p.required("targets", package.targets);
|
||||
// TODO: should this be optional?
|
||||
p.required("headers", package.headers);
|
||||
// TODO: how to detect this should default to all dependencies vs no dependencies?
|
||||
p.optional("dependencies", package.dependencies);
|
||||
if(!p.optional("namespace", package.namespace_name)) {
|
||||
package.namespace_name = project_name + "::";
|
||||
} else {
|
||||
// TODO: check if the namespace ends with "::"?
|
||||
}
|
||||
}
|
||||
|
||||
const Project *Project::root() const {
|
||||
auto root = this;
|
||||
while (root->parent != nullptr)
|
||||
root = root->parent;
|
||||
return root;
|
||||
checker.check(conditions, true);
|
||||
}
|
||||
|
||||
bool is_root_path(const std::string &path) {
|
||||
|
||||
Generated
+8
-20
@@ -14,7 +14,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/interface"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -34,7 +34,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/fetch-content"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/conditions"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/vcpkg"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/cxx-standard"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -74,7 +74,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/globbing"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -84,19 +84,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/templates"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
if(MSVC) # msvc
|
||||
add_test(
|
||||
NAME
|
||||
msvc-runtime
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/msvc-runtime"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
+2
-3
@@ -47,8 +47,7 @@ command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
condition = "msvc"
|
||||
name = "msvc-runtime"
|
||||
working-directory = "msvc-runtime"
|
||||
name = "package"
|
||||
working-directory = "package"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
@@ -1,15 +0,0 @@
|
||||
[project]
|
||||
name = "msvc-runtime"
|
||||
description = "Static MSVC runtime"
|
||||
msvc-runtime = "static"
|
||||
|
||||
# This target will compile with a static runtime
|
||||
[target.static-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
|
||||
# This target overrides the [project].msvc-runtime
|
||||
[target.dynamic-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc-runtime = "dynamic"
|
||||
@@ -1,5 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr(msvc-static)!");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[project]
|
||||
name = "Utility"
|
||||
version = "1.2"
|
||||
|
||||
[target.Utility]
|
||||
type = "static"
|
||||
alias = "Utility::Utility" # autogenerate this alias as ${PROJECT_NAME}::TargetName
|
||||
sources = [
|
||||
"src/Utility/*.cpp",
|
||||
"include/Utility/*.hpp",
|
||||
]
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
[package]
|
||||
name = "Utility" # defaults to ${PROJECT_NAME}
|
||||
targets = ["Utility"] # required
|
||||
headers = ["include/**.hpp", "include/**.h"] # required
|
||||
dependencies = [] # defaults to all the required packages
|
||||
namespace = "Utility::" # defaults to ${PROJECT_NAME}::
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace String
|
||||
{
|
||||
bool startsWith(const std::string& str, const std::string& prefix);
|
||||
std::string reverse(const std::string& str);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#include <Utility/String.hpp>
|
||||
|
||||
namespace String
|
||||
{
|
||||
bool startsWith(const std::string& str, const std::string& prefix)
|
||||
{
|
||||
return str.find(prefix) == 0;
|
||||
}
|
||||
|
||||
std::string reverse(const std::string& str)
|
||||
{
|
||||
auto result = str;
|
||||
std::reverse(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user