diff --git a/docs/examples/vcpkg.md b/docs/examples/vcpkg.md new file mode 100644 index 0000000..237d397 --- /dev/null +++ b/docs/examples/vcpkg.md @@ -0,0 +1,36 @@ +--- +# Automatically generated from tests/vcpkg/cmake.toml - DO NOT EDIT +layout: default +title: Dependencies from vcpkg +permalink: /examples/vcpkg +parent: Examples +nav_order: 4 +--- + +# Dependencies from vcpkg + +Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it: + +```toml +[project] +name = "vcpkg" +description = "Dependencies from vcpkg" + +# See https://github.com/microsoft/vcpkg/releases for vcpkg versions +# See https://vcpkg.io/en/packages.html for available packages +[vcpkg] +version = "2021.05.12" +packages = ["fmt"] + +[find-package] +fmt = {} + +[target.example] +type = "executable" +sources = ["src/main.cpp"] +link-libraries = ["fmt::fmt"] +``` + +The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`. + +This page was automatically generated from [tests/vcpkg/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/vcpkg/cmake.toml). diff --git a/include/cmake.hpp b/include/cmake.hpp index a4299ff..4de5984 100644 --- a/include/cmake.hpp +++ b/include/cmake.hpp @@ -33,6 +33,7 @@ struct Package { }; struct Vcpkg { + std::string version; std::string url; std::vector packages; }; diff --git a/src/cmake.cpp b/src/cmake.cpp index aa7c0e0..6fe3b1c 100644 --- a/src/cmake.cpp +++ b/src/cmake.cpp @@ -257,7 +257,8 @@ CMake::CMake(const std::string &path, bool build) { if (toml.contains("vcpkg")) { const auto &v = toml::find(toml, "vcpkg"); - vcpkg.url = toml::find(v, "url").as_string(); + get_optional(v, "url", vcpkg.url); + get_optional(v, "version", vcpkg.version); vcpkg.packages = toml::find(v, "packages"); } diff --git a/src/gen.cpp b/src/gen.cpp index 911a460..cea8b84 100644 --- a/src/gen.cpp +++ b/src/gen.cpp @@ -163,7 +163,7 @@ struct Command { ~Command() { if (!generated) { - assert(false && "Incorrect usage of cmd()"); + assert(false && "Incorrect usage of cmd(), you probably forgot ()"); } } @@ -297,7 +297,7 @@ struct Command { } template - CommandEndl operator()(Ts &&... values) { + CommandEndl operator()(Ts &&...values) { generated = true; ss << indent(depth) << command << '('; (void)std::initializer_list{print_arg(values)...}; @@ -406,6 +406,31 @@ struct Generator { } }; +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; + for (char ch : name) { + if ((ch & 0x80) != 0) { + throw std::runtime_error("Non-ASCII characters are not allowed in [project].name when using [vcpkg]"); + } + + if (ch == '_' || ch == ' ') { + ch = '-'; + } + + 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 { + throw std::runtime_error("The escaped project name '" + escaped + "' is not usable with [vcpkg]"); + } +} + int generate_cmake(const char *path, bool root) { if (!fs::exists(fs::path(path) / "cmake.toml")) { throw std::runtime_error("No cmake.toml found!"); @@ -422,8 +447,9 @@ int generate_cmake(const char *path, bool root) { auto inject_includes = [&gen](const std::vector &includes) { gen.inject_includes(includes); }; auto inject_cmake = [&gen](const std::string &cmake) { gen.inject_cmake(cmake); }; + std::string cmkr_url = "https://github.com/build-cpp/cmkr"; comment("This file is automatically generated from cmake.toml - DO NOT EDIT"); - comment("See https://github.com/build-cpp/cmkr for more information"); + comment("See " + cmkr_url + " for more information"); endl(); if (root) { @@ -523,30 +549,40 @@ int generate_cmake(const char *path, bool root) { } } - if (!cmake.vcpkg.packages.empty() && !cmake.vcpkg.url.empty()) { - auto vcpkg_escape_identifier = [](const std::string &name) -> std::string { - const std::regex ok("[a-z0-9]+(-[a-z0-9]+)*"); - const std::regex reserved("prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default"); - std::cmatch m; - if (!std::regex_match(name.c_str(), m, reserved) && std::regex_match(name.c_str(), m, ok)) { - return name; - } else { - // should probably throw! - return "project-name"; + if (!cmake.vcpkg.packages.empty()) { + // Allow the user to specify a url or derive it from the version + auto url = cmake.vcpkg.url; + if (url.empty()) { + if (cmake.vcpkg.version.empty()) { + throw std::runtime_error("You need either [vcpkg].version or [vcpkg].url"); } - }; + url = "https://github.com/microsoft/vcpkg/archive/refs/tags/" + cmake.vcpkg.version + ".tar.gz"; + } + + // CMake to bootstrap vcpkg and download the packages + // clang-format off + cmd("if")("CMKR_ROOT_PROJECT", "AND", "NOT", "CMKR_DISABLE_VCPKG"); + cmd("include")("FetchContent"); + cmd("message")("STATUS", "Fetching vcpkg..."); + cmd("FetchContent_Declare")("vcpkg", "URL", url); + cmd("FetchContent_MakeAvailable")("vcpkg"); + cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake"); + cmd("endif")(); + // clang-format on + + // Generate vcpkg.json using namespace nlohmann; json j; + j["$cmkr"] = "This file is automatically generated from cmake.toml - DO NOT EDIT"; + j["$cmkr-url"] = cmkr_url; j["$schema"] = "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json"; j["name"] = vcpkg_escape_identifier(cmake.project_name); - if (!cmake.project_version.empty()) - j["version"] = cmake.project_version; + j["version-string"] = cmake.project_version; j["dependencies"] = cmake.vcpkg.packages; - cmd("include")("FetchContent"); - cmd("message")("STATUS", "Fetching vcpkg..."); - cmd("FetchContent_Declare")("vcpkg", "URL", cmake.vcpkg.url); - cmd("FetchContent_MakeAvailable")("vcpkg"); - cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake"); + if (!cmake.project_description.empty()) { + j["description"] = cmake.project_description; + } + std::ofstream ofs("vcpkg.json"); if (!ofs) { throw std::runtime_error("Failed to create a vcpkg.json manifest file!"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 19efc1f..8565573 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -48,3 +48,13 @@ add_test( build ) +add_test( + NAME + vcpkg + WORKING_DIRECTORY + "${CMAKE_CURRENT_LIST_DIR}/vcpkg" + COMMAND + $ + build +) + diff --git a/tests/cmake.toml b/tests/cmake.toml index b708184..ebc12b2 100644 --- a/tests/cmake.toml +++ b/tests/cmake.toml @@ -20,4 +20,10 @@ arguments = ["build"] name = "conditions" command = "$" working-directory = "conditions" +arguments = ["build"] + +[[test]] +name = "vcpkg" +command = "$" +working-directory = "vcpkg" arguments = ["build"] \ No newline at end of file diff --git a/tests/vcpkg/cmake.toml b/tests/vcpkg/cmake.toml new file mode 100644 index 0000000..9ef39bf --- /dev/null +++ b/tests/vcpkg/cmake.toml @@ -0,0 +1,21 @@ +# Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) using [vcpkg](https://vcpkg.io/) and links an `example` target to it: + +[project] +name = "vcpkg" +description = "Dependencies from vcpkg" + +# See https://github.com/microsoft/vcpkg/releases for vcpkg versions +# See https://vcpkg.io/en/packages.html for available packages +[vcpkg] +version = "2021.05.12" +packages = ["fmt"] + +[find-package] +fmt = {} + +[target.example] +type = "executable" +sources = ["src/main.cpp"] +link-libraries = ["fmt::fmt"] + +# The bootstrapping of vcpkg is fully automated and no user interaction is necessary. You can disable vcpkg by setting `CMKR_DISABLE_VCPKG=ON`. \ No newline at end of file diff --git a/tests/vcpkg/src/main.cpp b/tests/vcpkg/src/main.cpp new file mode 100644 index 0000000..4a2fe7e --- /dev/null +++ b/tests/vcpkg/src/main.cpp @@ -0,0 +1,6 @@ +#include + +int main() +{ + fmt::print("Hello, world!\n"); +} \ No newline at end of file diff --git a/tests/vcpkg/vcpkg.json b/tests/vcpkg/vcpkg.json new file mode 100644 index 0000000..2ed5f1a --- /dev/null +++ b/tests/vcpkg/vcpkg.json @@ -0,0 +1,11 @@ +{ + "$cmkr": "This file is automatically generated from cmake.toml - DO NOT EDIT", + "$cmkr-url": "https://github.com/build-cpp/cmkr", + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", + "dependencies": [ + "fmt" + ], + "description": "Example of using vcpkg", + "name": "vcpkg", + "version-string": "" +}