mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a718dfd675 | |||
| 25da3171f0 | |||
| 95907602b4 | |||
| 4b300df8d5 | |||
| 760b2a8511 | |||
| fb9af0213b |
Generated
+4
@@ -3,6 +3,10 @@
|
||||
|
||||
cmake_minimum_required(VERSION 2.8...3.8)
|
||||
|
||||
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)
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ include_guard()
|
||||
|
||||
# Change these defaults to point to your infrastructure if desired
|
||||
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
|
||||
set(CMKR_TAG "archive_3cd84a97" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "archive_264e4ace" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
|
||||
# Set these from the command line to customize for development/debugging purposes
|
||||
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
# Automatically generated from tests/cxx-standard/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Changing C++ standard
|
||||
permalink: /examples/cxx-standard
|
||||
parent: Examples
|
||||
nav_order: 5
|
||||
---
|
||||
|
||||
# Changing C++ standard
|
||||
|
||||
Require a C++11 compiler for the target `example`.
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "cxx-standard"
|
||||
description = "Changing C++ standard"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
```
|
||||
|
||||
This is equivalent to CMake's [target_compile_features](https://cmake.org/cmake/help/latest/command/target_compile_features.html)`(example PRIVATE cxx_std_11)`. For more information on available C/C++ standards and features see [cmake-compile-features(7)](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html).
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/cxx-standard/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/cxx-standard/cmake.toml).</sub></sup>
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
# Automatically generated from tests/globbing/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Globbing sources
|
||||
permalink: /examples/globbing
|
||||
parent: Examples
|
||||
nav_order: 6
|
||||
---
|
||||
|
||||
# Globbing sources
|
||||
|
||||
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "globbing"
|
||||
description = "Globbing sources"
|
||||
|
||||
# Recursively glob in the mylib/ folder
|
||||
[target.mylib]
|
||||
type = "static"
|
||||
alias = "mylib::mylib"
|
||||
sources = ["mylib/**.hpp", "mylib/**.cpp"]
|
||||
include-directories = ["mylib/include"]
|
||||
|
||||
# Single-folder glob in example/src/
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["example/src/*.cpp"]
|
||||
link-libraries = ["mylib::mylib"]
|
||||
```
|
||||
|
||||
As you can see in the example above you can use `**.ext` to glob recursively and `*.ext` to glob non-recursively. This **does not** generate `file(GLOB ...)` commands, but instead globs when cmkr is run. Files are sorted to give deterministic results regardless of the platform used.
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/globbing/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/globbing/cmake.toml).</sub></sup>
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "project_parser.hpp"
|
||||
|
||||
namespace cmkr {
|
||||
namespace gen {
|
||||
|
||||
int generate_project(const char *typ);
|
||||
|
||||
int generate_cmake(const char *path, bool root = true);
|
||||
int generate_cmake(const char *path, const parser::Project *parent_project = nullptr);
|
||||
|
||||
} // namespace gen
|
||||
} // namespace cmkr
|
||||
|
||||
@@ -126,6 +126,7 @@ struct Project {
|
||||
std::string build_dir = "build";
|
||||
std::string generator;
|
||||
std::string config;
|
||||
bool allow_in_tree = false;
|
||||
Condition<std::vector<std::string>> project_subdirs;
|
||||
std::vector<std::string> cppflags;
|
||||
std::vector<std::string> cflags;
|
||||
@@ -151,7 +152,7 @@ struct Project {
|
||||
tsl::ordered_map<std::string, std::string> conditions;
|
||||
std::vector<Subdir> subdirs;
|
||||
|
||||
Project(const std::string &path, bool build);
|
||||
Project(const Project *parent, const std::string &path, bool build);
|
||||
};
|
||||
|
||||
bool is_root_path(const std::string &path);
|
||||
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
#include "build.hpp"
|
||||
#include "project_parser.hpp"
|
||||
#include "error.hpp"
|
||||
#include "cmake_generator.hpp"
|
||||
#include "error.hpp"
|
||||
#include "project_parser.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <sstream>
|
||||
@@ -14,7 +14,7 @@ namespace cmkr {
|
||||
namespace build {
|
||||
|
||||
int run(int argc, char **argv) {
|
||||
parser::Project project(".", true);
|
||||
parser::Project project(nullptr, ".", true);
|
||||
if (argc > 2) {
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
project.build_args.push_back(argv[i]);
|
||||
@@ -50,7 +50,7 @@ int run(int argc, char **argv) {
|
||||
|
||||
int clean() {
|
||||
bool ret = false;
|
||||
parser::Project project(".", true);
|
||||
parser::Project project(nullptr, ".", true);
|
||||
if (fs::exists(project.build_dir)) {
|
||||
ret = fs::remove_all(project.build_dir);
|
||||
fs::create_directory(project.build_dir);
|
||||
@@ -59,7 +59,7 @@ int clean() {
|
||||
}
|
||||
|
||||
int install() {
|
||||
parser::Project project(".", false);
|
||||
parser::Project project(nullptr, ".", false);
|
||||
auto cmd = "cmake --install " + project.build_dir;
|
||||
return ::system(cmd.c_str());
|
||||
}
|
||||
|
||||
+30
-12
@@ -1,7 +1,6 @@
|
||||
#include "cmake_generator.hpp"
|
||||
#include "error.hpp"
|
||||
#include "literals.hpp"
|
||||
#include "project_parser.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <cassert>
|
||||
@@ -412,6 +411,13 @@ struct Generator {
|
||||
}
|
||||
};
|
||||
|
||||
static bool vcpkg_valid_identifier(const std::string &name) {
|
||||
const std::regex reserved("prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default");
|
||||
const std::regex ok("[a-z0-9]+(-[a-z0-9]+)*");
|
||||
std::cmatch m;
|
||||
return !std::regex_match(name.c_str(), m, reserved) && std::regex_match(name.c_str(), m, ok);
|
||||
}
|
||||
|
||||
static std::string vcpkg_escape_identifier(const std::string &name) {
|
||||
// Do a reasonable effort to escape the project name for use with vcpkg
|
||||
std::string escaped;
|
||||
@@ -427,22 +433,18 @@ static std::string vcpkg_escape_identifier(const std::string &name) {
|
||||
escaped += std::tolower(ch);
|
||||
}
|
||||
|
||||
const std::regex reserved("prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default");
|
||||
const std::regex ok("[a-z0-9]+(-[a-z0-9]+)*");
|
||||
std::cmatch m;
|
||||
if (!std::regex_match(escaped.c_str(), m, reserved) && std::regex_match(escaped.c_str(), m, ok)) {
|
||||
return escaped;
|
||||
} else {
|
||||
if (!vcpkg_valid_identifier(escaped)) {
|
||||
throw std::runtime_error("The escaped project name '" + escaped + "' is not usable with [vcpkg]");
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
int generate_cmake(const char *path, bool root) {
|
||||
int generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml found!");
|
||||
}
|
||||
|
||||
parser::Project project(path, false);
|
||||
parser::Project project(parent_project, path, false);
|
||||
Generator gen(project);
|
||||
|
||||
// Helper lambdas for more convenient CMake generation
|
||||
@@ -458,9 +460,18 @@ int generate_cmake(const char *path, bool root) {
|
||||
comment("See " + cmkr_url + " for more information");
|
||||
endl();
|
||||
|
||||
if (root) {
|
||||
// Root project doesn't have a parent
|
||||
if (parent_project == nullptr) {
|
||||
cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl();
|
||||
|
||||
if (!project.allow_in_tree) {
|
||||
// clang-format off
|
||||
cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR");
|
||||
cmd("message")("FATAL_ERROR", "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
comment("Regenerate CMakeLists.txt automatically in the root project");
|
||||
cmd("set")("CMKR_ROOT_PROJECT", "OFF");
|
||||
// clang-format off
|
||||
@@ -565,6 +576,13 @@ int generate_cmake(const char *path, bool root) {
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/" + project.vcpkg.version + ".tar.gz";
|
||||
}
|
||||
|
||||
// Show a nicer error than vcpkg when specifying an invalid package name
|
||||
for (const auto &package : project.vcpkg.packages) {
|
||||
if (!vcpkg_valid_identifier(package)) {
|
||||
throw std::runtime_error("Invalid [vcpkg].packages name '" + package + "' (needs to be lowercase alphanumeric)");
|
||||
}
|
||||
}
|
||||
|
||||
// CMake to bootstrap vcpkg and download the packages
|
||||
// clang-format off
|
||||
cmd("if")("CMKR_ROOT_PROJECT", "AND", "NOT", "CMKR_DISABLE_VCPKG");
|
||||
@@ -901,7 +919,7 @@ int generate_cmake(const char *path, bool root) {
|
||||
}
|
||||
}
|
||||
|
||||
auto generate_subdir = [path](const fs::path &sub) {
|
||||
auto generate_subdir = [path, &project](const fs::path &sub) {
|
||||
// Skip generating for subdirectories that have a cmake.toml with a [project] in it
|
||||
fs::path subpath;
|
||||
for (const auto &p : sub) {
|
||||
@@ -913,7 +931,7 @@ int generate_cmake(const char *path, bool root) {
|
||||
|
||||
subpath = path / sub;
|
||||
if (fs::exists(subpath / "cmake.toml")) {
|
||||
generate_cmake(subpath.string().c_str(), false);
|
||||
generate_cmake(subpath.string().c_str(), &project);
|
||||
}
|
||||
};
|
||||
for (const auto &itr : project.project_subdirs) {
|
||||
|
||||
+14
-6
@@ -61,7 +61,7 @@ static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destin
|
||||
}
|
||||
}
|
||||
|
||||
Project::Project(const std::string &path, bool build) {
|
||||
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("No cmake.toml was found!");
|
||||
@@ -79,6 +79,7 @@ Project::Project(const std::string &path, bool build) {
|
||||
get_optional(cmake, "generator", generator);
|
||||
get_optional(cmake, "config", config);
|
||||
get_optional(cmake, "arguments", gen_args);
|
||||
get_optional(cmake, "allow-in-tree", allow_in_tree);
|
||||
}
|
||||
} else {
|
||||
if (toml.contains("cmake")) {
|
||||
@@ -312,11 +313,18 @@ Project::Project(const std::string &path, bool build) {
|
||||
}
|
||||
|
||||
// Reasonable default conditions (you can override these if you desire)
|
||||
conditions["windows"] = R"cmake(WIN32)cmake";
|
||||
conditions["macos"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")cmake";
|
||||
conditions["unix"] = R"cmake(UNIX)cmake";
|
||||
conditions["bsd"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")cmake";
|
||||
conditions["linux"] = conditions["lunix"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")cmake";
|
||||
if (parent == nullptr) {
|
||||
conditions["windows"] = R"cmake(WIN32)cmake";
|
||||
conditions["macos"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Darwin")cmake";
|
||||
conditions["unix"] = R"cmake(UNIX)cmake";
|
||||
conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake";
|
||||
conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake";
|
||||
conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake";
|
||||
conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake";
|
||||
conditions["msvc"] = R"cmake(MSVC)cmake";
|
||||
} else {
|
||||
conditions = parent->conditions;
|
||||
}
|
||||
|
||||
if (toml.contains("conditions")) {
|
||||
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
|
||||
|
||||
Generated
+20
@@ -58,3 +58,23 @@ add_test(
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
cxx-standard
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/cxx-standard"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
globbing
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/globbing"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
|
||||
+13
-1
@@ -26,4 +26,16 @@ arguments = ["build"]
|
||||
name = "vcpkg"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "vcpkg"
|
||||
arguments = ["build"]
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "cxx-standard"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "cxx-standard"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "globbing"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "globbing"
|
||||
arguments = ["build"]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Require a C++11 compiler for the target `example`.
|
||||
|
||||
[project]
|
||||
name = "cxx-standard"
|
||||
description = "Changing C++ standard"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
|
||||
# This is equivalent to CMake's [target_compile_features](https://cmake.org/cmake/help/latest/command/target_compile_features.html)`(example PRIVATE cxx_std_11)`. For more information on available C/C++ standards and features see [cmake-compile-features(7)](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html).
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
||||
int main()
|
||||
{
|
||||
auto tpl = std::make_tuple(1, 2);
|
||||
printf("Hello from C++11 %d\n", std::get<0>(tpl));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
[project]
|
||||
name = "globbing"
|
||||
description = "Globbing sources"
|
||||
|
||||
# Recursively glob in the mylib/ folder
|
||||
[target.mylib]
|
||||
type = "static"
|
||||
alias = "mylib::mylib"
|
||||
sources = ["mylib/**.hpp", "mylib/**.cpp"]
|
||||
include-directories = ["mylib/include"]
|
||||
|
||||
# Single-folder glob in example/src/
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["example/src/*.cpp"]
|
||||
link-libraries = ["mylib::mylib"]
|
||||
|
||||
# As you can see in the example above you can use `**.ext` to glob recursively and `*.ext` to glob non-recursively. This **does not** generate `file(GLOB ...)` commands, but instead globs when cmkr is run. Files are sorted to give deterministic results regardless of the platform used.
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
int main() {
|
||||
std::cout << mylib::message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mylib
|
||||
{
|
||||
std::string message();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
std::string mylib::message()
|
||||
{
|
||||
return "cmkr is awesome!";
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"dependencies": [
|
||||
"fmt"
|
||||
],
|
||||
"description": "Example of using vcpkg",
|
||||
"description": "Dependencies from vcpkg",
|
||||
"name": "vcpkg",
|
||||
"version-string": ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user