Skip to content

Commit 5ad6134

Browse files
authored
Merge pull request #27 from build-cpp/manifest
Add support for headers and vcpkg manifest mode
2 parents 4988f14 + 7b20d8c commit 5ad6134

File tree

14 files changed

+25646
-21
lines changed

14 files changed

+25646
-21
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmake.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type = "executable"
2020
sources = ["src/*.cpp", "include/*.hpp"]
2121
include-directories = ["include"]
2222
compile-features = ["cxx_std_11"]
23-
link-libraries = ["toml11", "ghc_filesystem", "mpark_variant", "ordered_map"]
23+
link-libraries = ["toml11", "ghc_filesystem", "mpark_variant", "ordered_map", "nlohmann_json"]
2424

2525
[[install]]
2626
targets = ["cmkr"]

docs/examples/vcpkg.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# Automatically generated from tests/vcpkg/cmake.toml - DO NOT EDIT
3+
layout: default
4+
title: Dependencies from vcpkg
5+
permalink: /examples/vcpkg
6+
parent: Examples
7+
nav_order: 4
8+
---
9+
10+
# Dependencies from vcpkg
11+
12+
Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
13+
14+
```toml
15+
[project]
16+
name = "vcpkg"
17+
description = "Dependencies from vcpkg"
18+
19+
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
20+
# See https://vcpkg.io/en/packages.html for available packages
21+
[vcpkg]
22+
version = "2021.05.12"
23+
packages = ["fmt"]
24+
25+
[find-package]
26+
fmt = {}
27+
28+
[target.example]
29+
type = "executable"
30+
sources = ["src/main.cpp"]
31+
link-libraries = ["fmt::fmt"]
32+
```
33+
34+
The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`.
35+
36+
<sup><sub>This page was automatically generated from [tests/vcpkg/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/vcpkg/cmake.toml).</sub></sup>

include/cmake.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct Package {
3434

3535
struct Vcpkg {
3636
std::string version;
37+
std::string url;
3738
std::vector<std::string> packages;
3839
};
3940

@@ -64,6 +65,7 @@ struct Target {
6465
ConditionVector link_libraries;
6566
ConditionVector link_options;
6667
ConditionVector precompile_headers;
68+
ConditionVector headers;
6769
ConditionVector sources;
6870

6971
std::string alias;

src/cmake.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ CMake::CMake(const std::string &path, bool build) {
181181
target.name = itr.first;
182182
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
183183

184+
get_optional(t, "headers", target.headers);
184185
get_optional(t, "sources", target.sources);
185186
get_optional(t, "compile-definitions", target.compile_definitions);
186187
get_optional(t, "compile-features", target.compile_features);
@@ -191,6 +192,12 @@ CMake::CMake(const std::string &path, bool build) {
191192
get_optional(t, "link-options", target.link_options);
192193
get_optional(t, "precompile-headers", target.precompile_headers);
193194

195+
if (!target.headers.empty()) {
196+
auto &sources = target.sources.nth(0).value();
197+
const auto &headers = target.headers.nth(0)->second;
198+
sources.insert(sources.end(), headers.begin(), headers.end());
199+
}
200+
194201
if (t.contains("alias")) {
195202
target.alias = toml::find(t, "alias").as_string();
196203
}
@@ -250,15 +257,9 @@ CMake::CMake(const std::string &path, bool build) {
250257

251258
if (toml.contains("vcpkg")) {
252259
const auto &v = toml::find(toml, "vcpkg");
253-
vcpkg.version = toml::find(v, "version").as_string();
260+
get_optional(v, "url", vcpkg.url);
261+
get_optional(v, "version", vcpkg.version);
254262
vcpkg.packages = toml::find<decltype(vcpkg.packages)>(v, "packages");
255-
256-
// This allows the user to use a custom pmm version if desired
257-
if (contents.count("pmm") == 0) {
258-
contents["pmm"]["url"] = "https://github.com/vector-of-bool/pmm/archive/refs/tags/1.5.1.tar.gz";
259-
// Hack to not execute pmm's example CMakeLists.txt
260-
contents["pmm"]["SOURCE_SUBDIR"] = "pmm";
261-
}
262263
}
263264

264265
// Reasonable default conditions (you can override these if you desire)

src/gen.cpp

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
#include <cstdio>
99
#include <cstring>
1010
#include <fstream>
11+
#include <iomanip>
1112
#include <new>
13+
#include <nlohmann/json.hpp>
14+
#include <regex>
1215
#include <sstream>
1316
#include <stdexcept>
1417
#include <string>
@@ -160,7 +163,7 @@ struct Command {
160163

161164
~Command() {
162165
if (!generated) {
163-
assert(false && "Incorrect usage of cmd()");
166+
assert(false && "Incorrect usage of cmd(), you probably forgot ()");
164167
}
165168
}
166169

@@ -403,6 +406,31 @@ struct Generator {
403406
}
404407
};
405408

409+
static std::string vcpkg_escape_identifier(const std::string &name) {
410+
// Do a reasonable effort to escape the project name for use with vcpkg
411+
std::string escaped;
412+
for (char ch : name) {
413+
if ((ch & 0x80) != 0) {
414+
throw std::runtime_error("Non-ASCII characters are not allowed in [project].name when using [vcpkg]");
415+
}
416+
417+
if (ch == '_' || ch == ' ') {
418+
ch = '-';
419+
}
420+
421+
escaped += std::tolower(ch);
422+
}
423+
424+
const std::regex reserved("prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default");
425+
const std::regex ok("[a-z0-9]+(-[a-z0-9]+)*");
426+
std::cmatch m;
427+
if (!std::regex_match(escaped.c_str(), m, reserved) && std::regex_match(escaped.c_str(), m, ok)) {
428+
return escaped;
429+
} else {
430+
throw std::runtime_error("The escaped project name '" + escaped + "' is not usable with [vcpkg]");
431+
}
432+
}
433+
406434
int generate_cmake(const char *path, bool root) {
407435
if (!fs::exists(fs::path(path) / "cmake.toml")) {
408436
throw std::runtime_error("No cmake.toml found!");
@@ -419,8 +447,9 @@ int generate_cmake(const char *path, bool root) {
419447
auto inject_includes = [&gen](const std::vector<std::string> &includes) { gen.inject_includes(includes); };
420448
auto inject_cmake = [&gen](const std::string &cmake) { gen.inject_cmake(cmake); };
421449

450+
std::string cmkr_url = "https://github.com/build-cpp/cmkr";
422451
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
423-
comment("See https://github.com/build-cpp/cmkr for more information");
452+
comment("See " + cmkr_url + " for more information");
424453
endl();
425454

426455
if (root) {
@@ -520,15 +549,45 @@ int generate_cmake(const char *path, bool root) {
520549
}
521550
}
522551

523-
if (!cmake.vcpkg.version.empty()) {
524-
assert("pmm is required in fetch-content for vcpkg to work" && cmake.contents.count("pmm") != 0);
525-
comment("Bootstrap vcpkg");
526-
cmd("include")("${pmm_SOURCE_DIR}/pmm.cmake");
527-
tsl::ordered_map<std::string, std::vector<std::string>> vcpkg_args;
528-
vcpkg_args["REVISION"] = {cmake.vcpkg.version};
529-
vcpkg_args["REQUIRES"] = cmake.vcpkg.packages;
530-
auto vcpkg = std::make_pair("VCPKG", vcpkg_args);
531-
cmd("pmm")(vcpkg).endl();
552+
if (!cmake.vcpkg.packages.empty()) {
553+
// Allow the user to specify a url or derive it from the version
554+
auto url = cmake.vcpkg.url;
555+
if (url.empty()) {
556+
if (cmake.vcpkg.version.empty()) {
557+
throw std::runtime_error("You need either [vcpkg].version or [vcpkg].url");
558+
}
559+
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/" + cmake.vcpkg.version + ".tar.gz";
560+
}
561+
562+
// CMake to bootstrap vcpkg and download the packages
563+
// clang-format off
564+
cmd("if")("CMKR_ROOT_PROJECT", "AND", "NOT", "CMKR_DISABLE_VCPKG");
565+
cmd("include")("FetchContent");
566+
cmd("message")("STATUS", "Fetching vcpkg...");
567+
cmd("FetchContent_Declare")("vcpkg", "URL", url);
568+
cmd("FetchContent_MakeAvailable")("vcpkg");
569+
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
570+
cmd("endif")();
571+
// clang-format on
572+
573+
// Generate vcpkg.json
574+
using namespace nlohmann;
575+
json j;
576+
j["$cmkr"] = "This file is automatically generated from cmake.toml - DO NOT EDIT";
577+
j["$cmkr-url"] = cmkr_url;
578+
j["$schema"] = "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json";
579+
j["name"] = vcpkg_escape_identifier(cmake.project_name);
580+
j["version-string"] = cmake.project_version;
581+
j["dependencies"] = cmake.vcpkg.packages;
582+
if (!cmake.project_description.empty()) {
583+
j["description"] = cmake.project_description;
584+
}
585+
586+
std::ofstream ofs("vcpkg.json");
587+
if (!ofs) {
588+
throw std::runtime_error("Failed to create a vcpkg.json manifest file!");
589+
}
590+
ofs << std::setw(2) << j << std::endl;
532591
}
533592

534593
if (!cmake.packages.empty()) {
@@ -683,7 +742,7 @@ int generate_cmake(const char *path, bool root) {
683742
}
684743

685744
if (!target.sources.empty()) {
686-
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + target.name + "_SOURCES}").endl();
745+
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + sources_var + "}").endl();
687746
}
688747

689748
if (!target.alias.empty()) {

tests/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/cmake.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,10 @@ arguments = ["build"]
2020
name = "conditions"
2121
command = "$<TARGET_FILE:cmkr>"
2222
working-directory = "conditions"
23+
arguments = ["build"]
24+
25+
[[test]]
26+
name = "vcpkg"
27+
command = "$<TARGET_FILE:cmkr>"
28+
working-directory = "vcpkg"
2329
arguments = ["build"]

tests/vcpkg/cmake.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it:
2+
3+
[project]
4+
name = "vcpkg"
5+
description = "Dependencies from vcpkg"
6+
7+
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
8+
# See https://vcpkg.io/en/packages.html for available packages
9+
[vcpkg]
10+
version = "2021.05.12"
11+
packages = ["fmt"]
12+
13+
[find-package]
14+
fmt = {}
15+
16+
[target.example]
17+
type = "executable"
18+
sources = ["src/main.cpp"]
19+
link-libraries = ["fmt::fmt"]
20+
21+
# The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`.

tests/vcpkg/src/main.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <fmt/core.h>
2+
3+
int main()
4+
{
5+
fmt::print("Hello, world!\n");
6+
}

0 commit comments

Comments
 (0)