diff options
| author | nicole mazzuca <mazzucan@outlook.com> | 2020-06-30 10:40:18 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-30 10:40:18 -0700 |
| commit | 1d8f0acc9c3085d18152a3f639077a28109196b6 (patch) | |
| tree | 1daa4780e0d5f51844217dc61dbf275433153abf /toolsrc/src | |
| parent | 67ab6130b64cce6701c5a95ca3b9bdc4d949cb8a (diff) | |
| download | vcpkg-1d8f0acc9c3085d18152a3f639077a28109196b6.tar.gz vcpkg-1d8f0acc9c3085d18152a3f639077a28109196b6.zip | |
[vcpkg manifest] Manifest Implementation (#11757)
==== Changes Related to manifests ====
* Add the `manifests` feature flag
* This only says whether we look for a `vcpkg.json` in the cwd, not
whether we support parsing manifests (for ports, for example)
* Changes to the manifests RFC
* `"authors"` -> `"maintainers"`
* `--x-classic-mode` -> `-manifests` \in `vcpkg_feature_flags`
* reserve `"core"` in addition to `"default"`, since that's already
reserved for features
* Add a small helper note about what identifiers must look like
* `<license-string>`: SPDX v3.8 -> v3.9
* `"feature"."description"` is allowed to be an array of strings as well
* `"version"` -> `"version-string"` for forward-compat with versions
RFC
* Add the `--feature-flags` option
* Add the ability to turn off feature flags via passing
`-<feature-flag>` to `VCPKG_FEATURE_FLAGS` or `--feature-flags`
* Add CMake toolchain support for manifests
* Requires either:
* a feature flag of `manifests` in either `Env{VCPKG_FEATURE_FLAGS}`
or `VCPKG_FEATURE_FLAGS`
* Passing the `VCPKG_ENABLE_MANIFESTS` option
* The toolchain will install your packages to
`${VCPKG_MANIFEST_DIR}/vcpkg_installed`.
* Add MSBuild `vcpkg integrate install` support for manifests
* Requires `VcpkgEnableManifest` to be true
* `vcpkg create` creates a port that has a `vcpkg.json` instead of a
`CONTROL`
* argparse, abseil, 3fd, and avisynthplus ports switched to manifest
from CONTROL
* Add support for `--x-manifest-root`, as well as code for finding it if
not passed
* Add support for parsing manifests!
* Add a filesystem lock!
==== Important Changes which are somewhat unrelated to manifests ====
* Rename `logicexpression.{h,cpp}` to `platform-expression.{h,cpp}`
* Add `PlatformExpression` type which takes the place of the old logic
expression
* Split the parsing of platform expressions from checking whether
they're true or not
* Eagerly parse PlatformExpressions as opposed to leaving them as
strings
* Add checking for feature flag consistency
* i.e., if `-binarycaching` is passed, you shouldn't be passing
`--binarysource`
* Add the `Json::Reader` type which, with the help of user-defined
visitors, converts JSON to your internal type
* VcpkgArgParser: place the switch names into a constant as opposed to
using magic constants
* In general update the parsing code so that this ^ works
* Add `Port-Version` fields to CONTROL files
* This replaces the existing practice of
`Version: <my-version>-<port-version>`
==== Smaller changes ====
* small drive-by cleanups to some CMake
* `${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}` ->
`${CURRENT_INSTALLED_DIR}`
* Remove `-analyze` when compiling with clang-cl, since that's not a
supported flag (vcpkg's build system)
* Add a message about which compiler is detected by vcpkg's build
system machinery
* Fix `Expected::then`
* Convert `""` to `{}` for `std::string` and `fs::path`, to avoid a
`strlen` (additionally, `.empty()` instead of `== ""`, and `.clear()`)
* Add `Strings::strto` which converts strings to numeric types
* Support built-in arrays and `StringView` for `Strings::join`
* Add `operator<` and friends to `StringView`
* Add `substr` to `StringView`
* SourceParagraphParser gets some new errors
Diffstat (limited to 'toolsrc/src')
49 files changed, 3158 insertions, 1027 deletions
diff --git a/toolsrc/src/vcpkg-fuzz/main.cpp b/toolsrc/src/vcpkg-fuzz/main.cpp index 881577654..bbff0d98d 100644 --- a/toolsrc/src/vcpkg-fuzz/main.cpp +++ b/toolsrc/src/vcpkg-fuzz/main.cpp @@ -2,6 +2,9 @@ #include <vcpkg/base/json.h> #include <vcpkg/base/stringview.h> #include <vcpkg/base/system.print.h> +#include <vcpkg/base/unicode.h> + +#include <vcpkg/platform-expression.h> #include <iostream> #include <sstream> @@ -17,6 +20,7 @@ namespace None, Utf8Decoder, JsonParser, + PlatformExpr, }; struct FuzzArgs @@ -57,10 +61,14 @@ namespace { kind = FuzzKind::Utf8Decoder; } + else if (value == "platform-expr") + { + kind = FuzzKind::PlatformExpr; + } else { System::print2(System::Color::error, "Invalid kind: ", value, "\n"); - System::print2(System::Color::error, " Expected one of: utf-8, json\n\n"); + System::print2(System::Color::error, " Expected one of: utf-8, json, platform-expr\n\n"); print_help_and_exit(true); } } @@ -120,6 +128,44 @@ Options: return std::move(ss).str(); } + [[noreturn]] void fuzz_json_and_exit(StringView text) + { + auto res = Json::parse(text); + if (!res) + { + Checks::exit_with_message(VCPKG_LINE_INFO, res.error()->format()); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } + + [[noreturn]] void fuzz_utf8_and_exit(StringView text) + { + auto res = Unicode::Utf8Decoder(text.begin(), text.end()); + for (auto ch : res) + { + (void)ch; + } + + Checks::exit_success(VCPKG_LINE_INFO); + } + + [[noreturn]] void fuzz_platform_expr_and_exit(StringView text) + { + auto res1 = PlatformExpression::parse_platform_expression(text, PlatformExpression::MultipleBinaryOperators::Deny); + auto res2 = PlatformExpression::parse_platform_expression(text, PlatformExpression::MultipleBinaryOperators::Allow); + + if (!res1) + { + Checks::exit_with_message(VCPKG_LINE_INFO, res1.error()); + } + if (!res2) + { + Checks::exit_with_message(VCPKG_LINE_INFO, res2.error()); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } } int main(int argc, char** argv) @@ -132,13 +178,11 @@ int main(int argc, char** argv) } auto text = read_all_of_stdin(); - auto res = Json::parse(text); - if (!res) - { - System::print2(System::Color::error, res.error()->format()); - } - else + switch (args.kind) { - System::print2(System::Color::success, "success!"); + case FuzzKind::JsonParser: fuzz_json_and_exit(text); + case FuzzKind::Utf8Decoder: fuzz_utf8_and_exit(text); + case FuzzKind::PlatformExpr: fuzz_platform_expr_and_exit(text); + default: Checks::exit_fail(VCPKG_LINE_INFO); } } diff --git a/toolsrc/src/vcpkg-test/arguments.cpp b/toolsrc/src/vcpkg-test/arguments.cpp index 448a5035c..3ce4c5a9b 100644 --- a/toolsrc/src/vcpkg-test/arguments.cpp +++ b/toolsrc/src/vcpkg-test/arguments.cpp @@ -32,13 +32,13 @@ TEST_CASE ("VcpkgCmdArguments from lowercase argument sequence", "[arguments]") REQUIRE(v.print_metrics); REQUIRE(*v.print_metrics.get()); - REQUIRE(v.overlay_ports->size() == 2); - REQUIRE(v.overlay_ports->at(0) == "C:\\ports1"); - REQUIRE(v.overlay_ports->at(1) == "C:\\ports2"); + REQUIRE(v.overlay_ports.size() == 2); + REQUIRE(v.overlay_ports.at(0) == "C:\\ports1"); + REQUIRE(v.overlay_ports.at(1) == "C:\\ports2"); - REQUIRE(v.overlay_triplets->size() == 2); - REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA"); - REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB"); + REQUIRE(v.overlay_triplets.size() == 2); + REQUIRE(v.overlay_triplets.at(0) == "C:\\tripletsA"); + REQUIRE(v.overlay_triplets.at(1) == "C:\\tripletsB"); } TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]") @@ -64,13 +64,13 @@ TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]") REQUIRE(v.print_metrics); REQUIRE(*v.print_metrics.get()); - REQUIRE(v.overlay_ports->size() == 2); - REQUIRE(v.overlay_ports->at(0) == "C:\\ports1"); - REQUIRE(v.overlay_ports->at(1) == "C:\\ports2"); + REQUIRE(v.overlay_ports.size() == 2); + REQUIRE(v.overlay_ports.at(0) == "C:\\ports1"); + REQUIRE(v.overlay_ports.at(1) == "C:\\ports2"); - REQUIRE(v.overlay_triplets->size() == 2); - REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA"); - REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB"); + REQUIRE(v.overlay_triplets.size() == 2); + REQUIRE(v.overlay_triplets.at(0) == "C:\\tripletsA"); + REQUIRE(v.overlay_triplets.at(1) == "C:\\tripletsB"); } TEST_CASE ("VcpkgCmdArguments from argument sequence with valued options", "[arguments]") diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp index c9eb0df35..4a4b1ac06 100644 --- a/toolsrc/src/vcpkg-test/dependencies.cpp +++ b/toolsrc/src/vcpkg-test/dependencies.cpp @@ -14,8 +14,10 @@ TEST_CASE ("parse depends", "[dependencies]") REQUIRE(w); auto& v = *w.get(); REQUIRE(v.size() == 1); - REQUIRE(v.at(0).depend.name == "liba"); - REQUIRE(v.at(0).qualifier == "windows"); + REQUIRE(v.at(0).name == "liba"); + REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); + REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}})); + REQUIRE(!v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}})); } TEST_CASE ("filter depends", "[dependencies]") @@ -48,14 +50,17 @@ TEST_CASE ("parse feature depends", "[dependencies]") auto& v = *u_.get(); REQUIRE(v.size() == 2); auto&& a0 = v.at(0); - REQUIRE(a0.depend.name == "libwebp"); - REQUIRE(a0.depend.features.size() == 9); - REQUIRE(a0.qualifier.empty()); + REQUIRE(a0.name == "libwebp"); + REQUIRE(a0.features.size() == 9); + REQUIRE(a0.platform.is_empty()); auto&& a1 = v.at(1); - REQUIRE(a1.depend.name == "libwebp"); - REQUIRE(a1.depend.features.size() == 2); - REQUIRE(a1.qualifier == "!osx"); + REQUIRE(a1.name == "libwebp"); + REQUIRE(a1.features.size() == 2); + REQUIRE(!a1.platform.is_empty()); + REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); + REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}})); + REQUIRE_FALSE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}})); } TEST_CASE ("qualified dependency", "[dependencies]") diff --git a/toolsrc/src/vcpkg-test/json.cpp b/toolsrc/src/vcpkg-test/json.cpp index a957618cb..86335d468 100644 --- a/toolsrc/src/vcpkg-test/json.cpp +++ b/toolsrc/src/vcpkg-test/json.cpp @@ -28,8 +28,8 @@ static std::string mystringify(const Value& val) { return Json::stringify(val, J TEST_CASE ("JSON stringify weird strings", "[json]") { vcpkg::StringView str = U8_STR("😀 😁 😂 🤣 😃 😄 😅 😆 😉"); - REQUIRE(mystringify(Value::string(str)) == ('"' + str.to_string() + '"')); - REQUIRE(mystringify(Value::string("\xED\xA0\x80")) == "\"\\ud800\""); // unpaired surrogate + REQUIRE(mystringify(Value::string(str)) == ('"' + str.to_string() + "\"\n")); + REQUIRE(mystringify(Value::string("\xED\xA0\x80")) == "\"\\ud800\"\n"); // unpaired surrogate } TEST_CASE ("JSON parse keywords", "[json]") diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp new file mode 100644 index 000000000..a6c661301 --- /dev/null +++ b/toolsrc/src/vcpkg-test/manifests.cpp @@ -0,0 +1,235 @@ +#include <catch2/catch.hpp> +#include <vcpkg-test/util.h> + +#include <vcpkg/base/json.h> +#include <vcpkg/base/util.h> +#include <vcpkg/paragraphs.h> +#include <vcpkg/sourceparagraph.h> + +using namespace vcpkg; +using namespace vcpkg::Paragraphs; +using namespace vcpkg::Test; + +static Json::Object parse_json_object(StringView sv) +{ + auto json = Json::parse(sv); + // we're not testing json parsing here, so just fail on errors + if (auto r = json.get()) + { + return std::move(r->first.object()); + } + else + { + Checks::exit_with_message(VCPKG_LINE_INFO, json.error()->format()); + } +} + +static Parse::ParseExpected<SourceControlFile> test_parse_manifest(StringView sv, bool expect_fail = false) +{ + auto object = parse_json_object(sv); + auto res = SourceControlFile::parse_manifest_file(fs::u8path("<test manifest>"), object); + if (!res.has_value() && !expect_fail) + { + print_error_message(res.error()); + } + return res; +} + +TEST_CASE ("manifest construct minimum", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "1.2.8" + })json"); + + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->name == "zlib"); + REQUIRE(pgh.core_paragraph->version == "1.2.8"); + REQUIRE(pgh.core_paragraph->maintainers.empty()); + REQUIRE(pgh.core_paragraph->description.empty()); + REQUIRE(pgh.core_paragraph->dependencies.empty()); +} + +TEST_CASE ("manifest construct maximum", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "s", + "version-string": "v", + "maintainers": ["m"], + "description": "d", + "dependencies": ["bd"], + "default-features": ["df"], + "features": [ + { + "name": "iroh", + "description": "zuko's uncle", + "dependencies": [ + { + "name": "tea" + }, + "firebending", + { + "name": "order.white-lotus", + "features": [ "the-ancient-ways" ], + "platform": "!(windows & arm)" + } + ] + }, + { + "name": "zuko", + "description": ["son of the fire lord", "firebending 師父"] + } + ] + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->name == "s"); + REQUIRE(pgh.core_paragraph->version == "v"); + REQUIRE(pgh.core_paragraph->maintainers.size() == 1); + REQUIRE(pgh.core_paragraph->maintainers[0] == "m"); + REQUIRE(pgh.core_paragraph->description.size() == 1); + REQUIRE(pgh.core_paragraph->description[0] == "d"); + REQUIRE(pgh.core_paragraph->dependencies.size() == 1); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "bd"); + REQUIRE(pgh.core_paragraph->default_features.size() == 1); + REQUIRE(pgh.core_paragraph->default_features[0] == "df"); + + REQUIRE(pgh.feature_paragraphs.size() == 2); + + REQUIRE(pgh.feature_paragraphs[0]->name == "iroh"); + REQUIRE(pgh.feature_paragraphs[0]->description.size() == 1); + REQUIRE(pgh.feature_paragraphs[0]->description[0] == "zuko's uncle"); + REQUIRE(pgh.feature_paragraphs[0]->dependencies.size() == 3); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[0].name == "tea"); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].name == "firebending"); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].name == "order.white-lotus"); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].features.size() == 1); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].features[0] == "the-ancient-ways"); + REQUIRE_FALSE(pgh.feature_paragraphs[0]->dependencies[2].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}, {"VCPKG_TARGET_ARCHITECTURE", "arm"}})); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}, {"VCPKG_TARGET_ARCHITECTURE", "x86"}})); + REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}, {"VCPKG_TARGET_ARCHITECTURE", "x86"}})); + + REQUIRE(pgh.feature_paragraphs[1]->name == "zuko"); + REQUIRE(pgh.feature_paragraphs[1]->description.size() == 2); + REQUIRE(pgh.feature_paragraphs[1]->description[0] == "son of the fire lord"); + REQUIRE(pgh.feature_paragraphs[1]->description[1] == "firebending 師父"); +} + +TEST_CASE ("SourceParagraph manifest two dependencies", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "1.2.8", + "dependencies": ["z", "openssl"] + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->dependencies.size() == 2); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "z"); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl"); +} + +TEST_CASE ("SourceParagraph manifest three dependencies", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "1.2.8", + "dependencies": ["z", "openssl", "xyz"] + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->dependencies.size() == 3); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "z"); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl"); + REQUIRE(pgh.core_paragraph->dependencies[2].name == "xyz"); +} + +TEST_CASE ("SourceParagraph manifest construct qualified dependencies", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "1.2.8", + "dependencies": [ + { + "name": "liba", + "platform": "windows" + }, + { + "name": "libb", + "platform": "uwp" + } + ] + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->name == "zlib"); + REQUIRE(pgh.core_paragraph->version == "1.2.8"); + REQUIRE(pgh.core_paragraph->maintainers.empty()); + REQUIRE(pgh.core_paragraph->description.empty()); + REQUIRE(pgh.core_paragraph->dependencies.size() == 2); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "liba"); + REQUIRE(pgh.core_paragraph->dependencies[0].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "libb"); + REQUIRE(pgh.core_paragraph->dependencies[1].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}})); +} + +TEST_CASE ("SourceParagraph manifest default features", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "a", + "version-string": "1.0", + "default-features": ["a1"] + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->default_features.size() == 1); + REQUIRE(pgh.core_paragraph->default_features[0] == "a1"); +} + +TEST_CASE ("SourceParagraph manifest description paragraph", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "a", + "version-string": "1.0", + "description": ["line 1", "line 2", "line 3"] + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->description.size() == 3); + REQUIRE(pgh.core_paragraph->description[0] == "line 1"); + REQUIRE(pgh.core_paragraph->description[1] == "line 2"); + REQUIRE(pgh.core_paragraph->description[2] == "line 3"); +} + +TEST_CASE ("SourceParagraph manifest supports", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "a", + "version-string": "1.0", + "supports": "!(windows | osx)" + })json"); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}})); + REQUIRE_FALSE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); + REQUIRE_FALSE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}})); +} + +TEST_CASE ("SourceParagraph manifest empty supports", "[manifests]") +{ + auto m_pgh = test_parse_manifest(R"json({ + "name": "a", + "version-string": "1.0", + "supports": "" + })json", true); + REQUIRE_FALSE(m_pgh.has_value()); +} diff --git a/toolsrc/src/vcpkg-test/paragraph.cpp b/toolsrc/src/vcpkg-test/paragraph.cpp index 678064f1c..6c2103d5f 100644 --- a/toolsrc/src/vcpkg-test/paragraph.cpp +++ b/toolsrc/src/vcpkg-test/paragraph.cpp @@ -8,28 +8,28 @@ namespace Strings = vcpkg::Strings; using vcpkg::Parse::Paragraph; -namespace { - -auto test_parse_control_file(const std::vector<std::unordered_map<std::string, std::string>>& v) +namespace { - std::vector<Paragraph> pghs; - for (auto&& p : v) + auto test_parse_control_file(const std::vector<std::unordered_map<std::string, std::string>>& v) { - pghs.emplace_back(); - for (auto&& kv : p) - pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{})); + std::vector<Paragraph> pghs; + for (auto&& p : v) + { + pghs.emplace_back(); + for (auto&& kv : p) + pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{})); + } + return vcpkg::SourceControlFile::parse_control_file("", std::move(pghs)); } - return vcpkg::SourceControlFile::parse_control_file("", std::move(pghs)); -} -auto test_make_binary_paragraph(const std::unordered_map<std::string, std::string>& v) -{ - Paragraph pgh; - for (auto&& kv : v) - pgh.emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{})); + auto test_make_binary_paragraph(const std::unordered_map<std::string, std::string>& v) + { + Paragraph pgh; + for (auto&& kv : v) + pgh.emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{})); - return vcpkg::BinaryParagraph(std::move(pgh)); -} + return vcpkg::BinaryParagraph(std::move(pgh)); + } } @@ -45,9 +45,9 @@ TEST_CASE ("SourceParagraph construct minimum", "[paragraph]") REQUIRE(pgh.core_paragraph->name == "zlib"); REQUIRE(pgh.core_paragraph->version == "1.2.8"); - REQUIRE(pgh.core_paragraph->maintainer == ""); - REQUIRE(pgh.core_paragraph->description == ""); - REQUIRE(pgh.core_paragraph->depends.size() == 0); + REQUIRE(pgh.core_paragraph->maintainers.empty()); + REQUIRE(pgh.core_paragraph->description.empty()); + REQUIRE(pgh.core_paragraph->dependencies.size() == 0); } TEST_CASE ("SourceParagraph construct maximum", "[paragraph]") @@ -65,15 +65,17 @@ TEST_CASE ("SourceParagraph construct maximum", "[paragraph]") REQUIRE(pgh.core_paragraph->name == "s"); REQUIRE(pgh.core_paragraph->version == "v"); - REQUIRE(pgh.core_paragraph->maintainer == "m"); - REQUIRE(pgh.core_paragraph->description == "d"); - REQUIRE(pgh.core_paragraph->depends.size() == 1); - REQUIRE(pgh.core_paragraph->depends[0].depend.name == "bd"); + REQUIRE(pgh.core_paragraph->maintainers.size() == 1); + REQUIRE(pgh.core_paragraph->maintainers[0] == "m"); + REQUIRE(pgh.core_paragraph->description.size() == 1); + REQUIRE(pgh.core_paragraph->description[0] == "d"); + REQUIRE(pgh.core_paragraph->dependencies.size() == 1); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "bd"); REQUIRE(pgh.core_paragraph->default_features.size() == 1); REQUIRE(pgh.core_paragraph->default_features[0] == "df"); } -TEST_CASE ("SourceParagraph two depends", "[paragraph]") +TEST_CASE ("SourceParagraph two dependencies", "[paragraph]") { auto m_pgh = test_parse_control_file({{ {"Source", "zlib"}, @@ -83,12 +85,12 @@ TEST_CASE ("SourceParagraph two depends", "[paragraph]") REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); - REQUIRE(pgh.core_paragraph->depends.size() == 2); - REQUIRE(pgh.core_paragraph->depends[0].depend.name == "z"); - REQUIRE(pgh.core_paragraph->depends[1].depend.name == "openssl"); + REQUIRE(pgh.core_paragraph->dependencies.size() == 2); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "z"); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl"); } -TEST_CASE ("SourceParagraph three depends", "[paragraph]") +TEST_CASE ("SourceParagraph three dependencies", "[paragraph]") { auto m_pgh = test_parse_control_file({{ {"Source", "zlib"}, @@ -98,13 +100,13 @@ TEST_CASE ("SourceParagraph three depends", "[paragraph]") REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); - REQUIRE(pgh.core_paragraph->depends.size() == 3); - REQUIRE(pgh.core_paragraph->depends[0].depend.name == "z"); - REQUIRE(pgh.core_paragraph->depends[1].depend.name == "openssl"); - REQUIRE(pgh.core_paragraph->depends[2].depend.name == "xyz"); + REQUIRE(pgh.core_paragraph->dependencies.size() == 3); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "z"); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl"); + REQUIRE(pgh.core_paragraph->dependencies[2].name == "xyz"); } -TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]") +TEST_CASE ("SourceParagraph construct qualified dependencies", "[paragraph]") { auto m_pgh = test_parse_control_file({{ {"Source", "zlib"}, @@ -116,13 +118,13 @@ TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]") REQUIRE(pgh.core_paragraph->name == "zlib"); REQUIRE(pgh.core_paragraph->version == "1.2.8"); - REQUIRE(pgh.core_paragraph->maintainer == ""); - REQUIRE(pgh.core_paragraph->description == ""); - REQUIRE(pgh.core_paragraph->depends.size() == 2); - REQUIRE(pgh.core_paragraph->depends[0].depend.name == "liba"); - REQUIRE(pgh.core_paragraph->depends[0].qualifier == "windows"); - REQUIRE(pgh.core_paragraph->depends[1].depend.name == "libb"); - REQUIRE(pgh.core_paragraph->depends[1].qualifier == "uwp"); + REQUIRE(pgh.core_paragraph->maintainers.empty()); + REQUIRE(pgh.core_paragraph->description.empty()); + REQUIRE(pgh.core_paragraph->dependencies.size() == 2); + REQUIRE(pgh.core_paragraph->dependencies[0].name == "liba"); + REQUIRE(pgh.core_paragraph->dependencies[0].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "libb"); + REQUIRE(pgh.core_paragraph->dependencies[1].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}})); } TEST_CASE ("SourceParagraph default features", "[paragraph]") @@ -150,10 +152,10 @@ TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]") REQUIRE(pgh.spec.name() == "zlib"); REQUIRE(pgh.version == "1.2.8"); - REQUIRE(pgh.maintainer == ""); - REQUIRE(pgh.description == ""); + REQUIRE(pgh.maintainers.empty()); + REQUIRE(pgh.description.empty()); REQUIRE(pgh.spec.triplet().canonical_name() == "x86-windows"); - REQUIRE(pgh.depends.size() == 0); + REQUIRE(pgh.dependencies.size() == 0); } TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]") @@ -170,13 +172,15 @@ TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]") REQUIRE(pgh.spec.name() == "s"); REQUIRE(pgh.version == "v"); - REQUIRE(pgh.maintainer == "m"); - REQUIRE(pgh.description == "d"); - REQUIRE(pgh.depends.size() == 1); - REQUIRE(pgh.depends[0] == "bd"); + REQUIRE(pgh.maintainers.size() == 1); + REQUIRE(pgh.maintainers[0] == "m"); + REQUIRE(pgh.description.size() == 1); + REQUIRE(pgh.description[0] == "d"); + REQUIRE(pgh.dependencies.size() == 1); + REQUIRE(pgh.dependencies[0] == "bd"); } -TEST_CASE ("BinaryParagraph three depends", "[paragraph]") +TEST_CASE ("BinaryParagraph three dependencies", "[paragraph]") { auto pgh = test_make_binary_paragraph({ {"Package", "zlib"}, @@ -186,10 +190,10 @@ TEST_CASE ("BinaryParagraph three depends", "[paragraph]") {"Depends", "a, b, c"}, }); - REQUIRE(pgh.depends.size() == 3); - REQUIRE(pgh.depends[0] == "a"); - REQUIRE(pgh.depends[1] == "b"); - REQUIRE(pgh.depends[2] == "c"); + REQUIRE(pgh.dependencies.size() == 3); + REQUIRE(pgh.dependencies[0] == "a"); + REQUIRE(pgh.dependencies[1] == "b"); + REQUIRE(pgh.dependencies[2] == "c"); } TEST_CASE ("BinaryParagraph abi", "[paragraph]") @@ -202,7 +206,7 @@ TEST_CASE ("BinaryParagraph abi", "[paragraph]") {"Abi", "abcd123"}, }); - REQUIRE(pgh.depends.size() == 0); + REQUIRE(pgh.dependencies.size() == 0); REQUIRE(pgh.abi == "abcd123"); } @@ -216,7 +220,7 @@ TEST_CASE ("BinaryParagraph default features", "[paragraph]") {"Default-Features", "a1"}, }); - REQUIRE(pgh.depends.size() == 0); + REQUIRE(pgh.dependencies.size() == 0); REQUIRE(pgh.default_features.size() == 1); REQUIRE(pgh.default_features[0] == "a1"); } @@ -300,8 +304,8 @@ TEST_CASE ("parse paragraphs empty fields", "[paragraph]") REQUIRE(pghs.size() == 1); REQUIRE(pghs[0].size() == 2); - REQUIRE(pghs[0]["f1"].first == ""); - REQUIRE(pghs[0]["f2"].first == ""); + REQUIRE(pghs[0]["f1"].first.empty()); + REQUIRE(pghs[0]["f2"].first.empty()); REQUIRE(pghs[0].size() == 2); } @@ -408,7 +412,7 @@ TEST_CASE ("BinaryParagraph serialize max", "[paragraph]") REQUIRE(pghs[0]["Version"].first == "1.2.8"); REQUIRE(pghs[0]["Architecture"].first == "x86-windows"); REQUIRE(pghs[0]["Multi-Arch"].first == "same"); - REQUIRE(pghs[0]["Description"].first == "first line\n second line"); + REQUIRE(pghs[0]["Description"].first == "first line\n second line"); REQUIRE(pghs[0]["Depends"].first == "dep"); REQUIRE(pghs[0]["Type"].first == "Port"); } diff --git a/toolsrc/src/vcpkg-test/plan.cpp b/toolsrc/src/vcpkg-test/plan.cpp index a39c2b4a4..b93ec54ce 100644 --- a/toolsrc/src/vcpkg-test/plan.cpp +++ b/toolsrc/src/vcpkg-test/plan.cpp @@ -37,7 +37,7 @@ static void features_check(Dependencies::InstallPlanAction& plan, for (auto&& feature_name : expected_features) { // TODO: see if this can be simplified - if (feature_name == "core" || feature_name == "") + if (feature_name == "core" || feature_name.empty()) { REQUIRE((Util::find(feature_list, "core") != feature_list.end() || Util::find(feature_list, "") != feature_list.end())); diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index 859fe99c5..9cd964067 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -72,6 +72,8 @@ static void inner(vcpkg::Files::Filesystem& fs, const VcpkgCmdArguments& args) } const VcpkgPaths paths(fs, args); + paths.track_feature_flag_metrics(); + fs.current_path(paths.root, VCPKG_LINE_INFO); if (args.command == "install" || args.command == "remove" || args.command == "export" || args.command == "update") { @@ -231,10 +233,11 @@ int main(const int argc, const char* const* const argv) VcpkgCmdArguments args = VcpkgCmdArguments::create_from_command_line(fs, argc, argv); args.imbue_from_environment(); + args.check_feature_flag_consistency(); + if (const auto p = args.disable_metrics.get()) Metrics::g_metrics.lock()->set_disabled(*p); if (const auto p = args.print_metrics.get()) Metrics::g_metrics.lock()->set_print_metrics(*p); if (const auto p = args.send_metrics.get()) Metrics::g_metrics.lock()->set_send_metrics(*p); - if (const auto p = args.disable_metrics.get()) Metrics::g_metrics.lock()->set_disabled(*p); if (const auto p = args.debug.get()) Debug::g_debugging = *p; if (args.send_metrics.has_value() && !Metrics::g_metrics.lock()->metrics_enabled()) @@ -248,6 +251,8 @@ int main(const int argc, const char* const* const argv) "Warning: passed either --printmetrics or --no-printmetrics, but metrics are disabled.\n"); } + args.track_feature_flag_metrics(); + if (Debug::g_debugging) { inner(fs, args); diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 74ad046ae..1ec9a19e4 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -10,6 +10,7 @@ #if !defined(_WIN32) #include <fcntl.h> +#include <sys/file.h> #include <sys/stat.h> #endif @@ -839,6 +840,97 @@ namespace vcpkg::Files fs::stdfs::current_path(path, ec); } + virtual fs::SystemHandle try_take_exclusive_file_lock(const fs::path& path, std::error_code& ec) override + { + fs::SystemHandle res; + + const auto system_file_name = path.native(); +#if defined(WIN32) + constexpr static auto busy_error = ERROR_BUSY; + const auto system_try_take_file_lock = [&] { + auto handle = CreateFileW( + system_file_name.c_str(), + GENERIC_READ, + 0 /* no sharing */, + nullptr /* no security attributes */, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr /* no template file */); + if (handle == INVALID_HANDLE_VALUE) + { + const auto err = GetLastError(); + if (err != ERROR_SHARING_VIOLATION) + { + ec.assign(err, std::system_category()); + } + return false; + } + + res.system_handle = reinterpret_cast<intptr_t>(handle); + return true; + }; +#else // ^^^ WIN32 / !WIN32 vvv + constexpr static auto busy_error = EBUSY; + int fd = open(system_file_name.c_str(), 0); + if (fd < 0) + { + ec.assign(errno, std::system_category()); + return res; + } + const auto system_try_take_file_lock = [&] { + if (flock(fd, LOCK_EX | LOCK_NB) != 0) + { + if (errno != EWOULDBLOCK) + { + ec.assign(errno, std::system_category()); + } + return false; + } + + res.system_handle = fd; + return true; + }; +#endif + + if (system_try_take_file_lock() || ec) + { + return res; + } + + System::printf("Waiting to take filesystem lock on %s...\n", path.u8string()); + auto wait = std::chrono::milliseconds(100); + // waits, at most, a second and a half. + while (wait < std::chrono::milliseconds(1000)) + { + std::this_thread::sleep_for(wait); + if (system_try_take_file_lock() || ec) + { + return res; + } + wait *= 2; + } + +#if !defined(WIN32) + close(fd); +#endif + ec.assign(busy_error, std::system_category()); + return res; + } + virtual void unlock_file_lock(fs::SystemHandle handle, std::error_code& ec) override + { +#if defined(WIN32) + if (CloseHandle(reinterpret_cast<HANDLE>(handle.system_handle)) == 0) + { + ec.assign(GetLastError(), std::system_category()); + } +#else + if (flock(handle.system_handle, LOCK_UN) != 0 || close(handle.system_handle) != 0) + { + ec.assign(errno, std::system_category()); + } +#endif + } + virtual std::vector<fs::path> find_from_PATH(const std::string& name) const override { #if defined(_WIN32) diff --git a/toolsrc/src/vcpkg/base/json.cpp b/toolsrc/src/vcpkg/base/json.cpp index 3d6bfc1d2..0972915eb 100644 --- a/toolsrc/src/vcpkg/base/json.cpp +++ b/toolsrc/src/vcpkg/base/json.cpp @@ -144,27 +144,35 @@ namespace vcpkg::Json return underlying_->string; } - const Array& Value::array() const noexcept + const Array& Value::array() const& noexcept { vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_array()); return underlying_->array; } - Array& Value::array() noexcept + Array& Value::array() & noexcept { vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_array()); return underlying_->array; } + Array&& Value::array() && noexcept + { + return std::move(this->array()); + } - const Object& Value::object() const noexcept + const Object& Value::object() const& noexcept { vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_object()); return underlying_->object; } - Object& Value::object() noexcept + Object& Value::object() & noexcept { vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_object()); return underlying_->object; } + Object&& Value::object() && noexcept + { + return std::move(this->object()); + } Value::Value() noexcept = default; Value::Value(Value&&) noexcept = default; @@ -601,35 +609,39 @@ namespace vcpkg::Json } } -#ifdef _MSC_VER -#define SCANF sscanf_s -#else -#define SCANF sscanf -#endif - - // TODO: switch to `from_chars` once we are able to remove support for old compilers if (floating) { - double res; - if (SCANF(number_to_parse.c_str(), "%lf", &res) != 1) + auto opt = Strings::strto<double>(number_to_parse); + if (auto res = opt.get()) + { + if (std::abs(*res) < INFINITY) + { + return Value::number(*res); + } + else + { + add_error(Strings::format("Floating point constant too big: %s", number_to_parse)); + } + } + else { add_error(Strings::format("Invalid floating point constant: %s", number_to_parse)); - return Value(); } - return Value::number(res); } else { - int64_t res; - if (SCANF(number_to_parse.c_str(), "%" SCNd64, &res) != 1) + auto opt = Strings::strto<int64_t>(number_to_parse); + if (auto res = opt.get()) + { + return Value::integer(*res); + } + else { add_error(Strings::format("Invalid integer constant: %s", number_to_parse)); - return Value(); } - return Value::integer(res); } -#undef SCANF + return Value(); } Value parse_keyword() noexcept @@ -960,12 +972,15 @@ namespace vcpkg::Json for (auto code_point : Unicode::Utf8Decoder(sv.begin(), sv.end())) { // a. If C is listed in the "Code Point" column of Table 66, then - const auto match = std::find_if(begin(escape_sequences), end(escape_sequences), [code_point](const std::pair<char32_t, const char*>& attempt) { - return attempt.first == code_point; - }); + const auto match = std::find_if(begin(escape_sequences), + end(escape_sequences), + [code_point](const std::pair<char32_t, const char*>& attempt) { + return attempt.first == code_point; + }); // i. Set product to the string-concatenation of product and the escape sequence for C as // specified in the "Escape Sequence" column of the corresponding row. - if (match != end(escape_sequences)) { + if (match != end(escape_sequences)) + { buffer.append(match->second); continue; } @@ -1086,18 +1101,21 @@ namespace vcpkg::Json { std::string res; Stringifier{style, res}.stringify(value, 0); + res.push_back('\n'); return res; } std::string stringify(const Object& obj, JsonStyle style) { std::string res; Stringifier{style, res}.stringify_object(obj, 0); + res.push_back('\n'); return res; } std::string stringify(const Array& arr, JsonStyle style) { std::string res; Stringifier{style, res}.stringify_array(arr, 0); + res.push_back('\n'); return res; } // } auto stringify() diff --git a/toolsrc/src/vcpkg/base/parse.cpp b/toolsrc/src/vcpkg/base/parse.cpp index 0d2c5f8fc..8e6b767d7 100644 --- a/toolsrc/src/vcpkg/base/parse.cpp +++ b/toolsrc/src/vcpkg/base/parse.cpp @@ -144,6 +144,13 @@ namespace vcpkg::Parse optional_field(fieldname, {out, ignore}); return out; } + std::string ParagraphParser::required_field(const std::string& fieldname) + { + std::string out; + TextRowCol ignore; + optional_field(fieldname, {out, ignore}); + return out; + } std::unique_ptr<ParseControlErrorInfo> ParagraphParser::error_info(const std::string& name) const { @@ -151,8 +158,9 @@ namespace vcpkg::Parse { auto err = std::make_unique<ParseControlErrorInfo>(); err->name = name; - err->extra_fields = Util::extract_keys(fields); - err->missing_fields = std::move(missing_fields); + err->extra_fields["CONTROL"] = Util::extract_keys(fields); + err->missing_fields["CONTROL"] = std::move(missing_fields); + err->expected_types = std::move(expected_types); return err; } return nullptr; @@ -214,7 +222,7 @@ namespace vcpkg::Parse parser.add_error("triplet specifier not allowed in this context", loc); return nullopt; } - return Dependency{{pqs.name, pqs.features.value_or({})}, pqs.qualifier.value_or({})}; + return Dependency{pqs.name, pqs.features.value_or({}), pqs.platform.value_or({})}; }); }); if (!opt) return {parser.get_error()->format(), expected_right_tag}; diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index e267d6864..44fc3ebd1 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -10,10 +10,9 @@ namespace vcpkg::Strings::details static bool is_space(const char c) { return std::isspace(static_cast<unsigned char>(c)) != 0; } // Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower() - static char tolower_char(const char c) { return (c < 'A' || c > 'Z') ? c : c - 'A' + 'a'; } static char toupper_char(const char c) { return (c < 'a' || c > 'z') ? c : c - 'a' + 'A'; } - static bool icase_eq(char a, char b) { return tolower_char(a) == tolower_char(b); } + static bool icase_eq(char a, char b) { return tolower_char{}(a) == tolower_char{}(b); } #if defined(_WIN32) static _locale_t& c_locale() @@ -102,7 +101,7 @@ bool Strings::case_insensitive_ascii_equals(StringView left, StringView right) std::string Strings::ascii_to_lowercase(std::string&& s) { - std::transform(s.begin(), s.end(), s.begin(), &details::tolower_char); + Strings::ascii_to_lowercase(s.begin(), s.end()); return std::move(s); } @@ -157,7 +156,7 @@ void Strings::trim_all_and_remove_whitespace_strings(std::vector<std::string>* s Util::erase_remove_if(*strings, [](const std::string& s) { return s.empty(); }); } -std::vector<std::string> Strings::split(const std::string& s, const char delimiter) +std::vector<std::string> Strings::split(StringView s, const char delimiter) { std::vector<std::string> output; auto first = s.begin(); diff --git a/toolsrc/src/vcpkg/base/stringview.cpp b/toolsrc/src/vcpkg/base/stringview.cpp index 6b159db48..326b0219c 100644 --- a/toolsrc/src/vcpkg/base/stringview.cpp +++ b/toolsrc/src/vcpkg/base/stringview.cpp @@ -71,7 +71,7 @@ namespace vcpkg return result.front(); } - StringView::StringView(const std::string& s) : m_ptr(s.data()), m_size(s.size()) {} + StringView::StringView(const std::string& s) : m_ptr(s.data()), m_size(s.size()) { } std::string StringView::to_string() const { return std::string(m_ptr, m_size); } void StringView::to_string(std::string& s) const { s.append(m_ptr, m_size); } @@ -82,4 +82,13 @@ namespace vcpkg } bool operator!=(StringView lhs, StringView rhs) noexcept { return !(lhs == rhs); } + + bool operator<(StringView lhs, StringView rhs) noexcept + { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + bool operator>(StringView lhs, StringView rhs) noexcept { return rhs < lhs; } + bool operator<=(StringView lhs, StringView rhs) noexcept { return !(rhs < lhs); } + bool operator>=(StringView lhs, StringView rhs) noexcept { return !(lhs < rhs); } } diff --git a/toolsrc/src/vcpkg/binarycaching.cpp b/toolsrc/src/vcpkg/binarycaching.cpp index 13dd920c6..5bebc4fb8 100644 --- a/toolsrc/src/vcpkg/binarycaching.cpp +++ b/toolsrc/src/vcpkg/binarycaching.cpp @@ -1048,7 +1048,7 @@ std::string vcpkg::generate_nuspec(const VcpkgPaths& paths, auto& version = scf.core_paragraph->version;
std::string description =
Strings::concat("NOT FOR DIRECT USE. Automatically generated cache package.\n\n",
- scf.core_paragraph->description,
+ Strings::join("\n ", scf.core_paragraph->description),
"\n\nVersion: ",
version,
"\nTriplet/Compiler hash: ",
diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp index 305ae2ae9..0b0819fd7 100644 --- a/toolsrc/src/vcpkg/binaryparagraph.cpp +++ b/toolsrc/src/vcpkg/binaryparagraph.cpp @@ -6,6 +6,7 @@ #include <vcpkg/binaryparagraph.h> #include <vcpkg/paragraphparser.h> +#include <vcpkg/paragraphs.h> namespace vcpkg { @@ -13,6 +14,7 @@ namespace vcpkg { static const std::string PACKAGE = "Package"; static const std::string VERSION = "Version"; + static const std::string PORT_VERSION = "Port-Version"; static const std::string ARCHITECTURE = "Architecture"; static const std::string MULTI_ARCH = "Multi-Arch"; } @@ -24,7 +26,7 @@ namespace vcpkg static const std::string DESCRIPTION = "Description"; static const std::string MAINTAINER = "Maintainer"; static const std::string DEPENDS = "Depends"; - static const std::string DEFAULTFEATURES = "Default-Features"; + static const std::string DEFAULT_FEATURES = "Default-Features"; static const std::string TYPE = "Type"; } @@ -48,15 +50,38 @@ namespace vcpkg this->version = parser.optional_field(Fields::VERSION); this->feature = parser.optional_field(Fields::FEATURE); - this->description = parser.optional_field(Fields::DESCRIPTION); - this->maintainer = parser.optional_field(Fields::MAINTAINER); + auto pv_str = parser.optional_field(Fields::PORT_VERSION); + this->port_version = 0; + if (!pv_str.empty()) + { + auto pv_opt = Strings::strto<int>(pv_str); + if (auto pv = pv_opt.get()) + { + this->port_version = *pv; + } + else + { + parser.add_type_error(Fields::PORT_VERSION, "a non-negative integer"); + } + } + + this->description = Strings::split(parser.optional_field(Fields::DESCRIPTION), '\n'); + for (auto& desc : this->description) + { + desc = Strings::trim(std::move(desc)); + } + this->maintainers = Strings::split(parser.optional_field(Fields::MAINTAINER), '\n'); + for (auto& maintainer : this->maintainers) + { + maintainer = Strings::trim(std::move(maintainer)); + } this->abi = parser.optional_field(Fields::ABI); std::string multi_arch; parser.required_field(Fields::MULTI_ARCH, multi_arch); - this->depends = Util::fmap( + this->dependencies = Util::fmap( parse_qualified_specifier_list(parser.optional_field(Fields::DEPENDS)).value_or_exit(VCPKG_LINE_INFO), [](const ParsedQualifiedSpecifier& dep) { // for compatibility with previous vcpkg versions, we discard all irrelevant information @@ -64,7 +89,7 @@ namespace vcpkg }); if (!this->is_feature()) { - this->default_features = parse_default_features_list(parser.optional_field(Fields::DEFAULTFEATURES)) + this->default_features = parse_default_features_list(parser.optional_field(Fields::DEFAULT_FEATURES)) .value_or_exit(VCPKG_LINE_INFO); } @@ -87,16 +112,17 @@ namespace vcpkg const std::vector<FeatureSpec>& deps) : spec(spgh.name, triplet) , version(spgh.version) + , port_version(spgh.port_version) , description(spgh.description) - , maintainer(spgh.maintainer) + , maintainers(spgh.maintainers) , feature() , default_features(spgh.default_features) - , depends() + , dependencies() , abi(abi_tag) , type(spgh.type) { - this->depends = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); }); - Util::sort_unique_erase(this->depends); + this->dependencies = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); }); + Util::sort_unique_erase(this->dependencies); } BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, @@ -105,16 +131,17 @@ namespace vcpkg const std ::vector<FeatureSpec>& deps) : spec(spgh.name, triplet) , version() + , port_version() , description(fpgh.description) - , maintainer() + , maintainers() , feature(fpgh.name) , default_features() - , depends() + , dependencies() , abi() , type(spgh.type) { - this->depends = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); }); - Util::sort_unique_erase(this->depends); + this->dependencies = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); }); + Util::sort_unique_erase(this->dependencies); } std::string BinaryParagraph::displayname() const @@ -131,29 +158,157 @@ namespace vcpkg return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.triplet()); } + bool operator==(const BinaryParagraph& lhs, const BinaryParagraph& rhs) + { + if (lhs.spec != rhs.spec) return false; + if (lhs.version != rhs.version) return false; + if (lhs.port_version != rhs.port_version) return false; + if (lhs.description != rhs.description) return false; + if (lhs.maintainers != rhs.maintainers) return false; + if (lhs.feature != rhs.feature) return false; + if (lhs.default_features != rhs.default_features) return false; + if (lhs.dependencies != rhs.dependencies) return false; + if (lhs.abi != rhs.abi) return false; + if (lhs.type != rhs.type) return false; + + return true; + } + + bool operator!=(const BinaryParagraph& lhs, const BinaryParagraph& rhs) { return !(lhs == rhs); } + + static void serialize_string(StringView name, const std::string& field, std::string& out_str) + { + if (field.empty()) + { + return; + } + + out_str.append(name.begin(), name.end()).append(": ").append(field).push_back('\n'); + } + static void serialize_array(StringView name, + const std::vector<std::string>& array, + std::string& out_str, + const char* joiner = ", ") + { + if (array.empty()) + { + return; + } + + out_str.append(name.begin(), name.end()).append(": "); + out_str.append(Strings::join(joiner, array)); + out_str.push_back('\n'); + } + static void serialize_paragraph(StringView name, const std::vector<std::string>& array, std::string& out_str) + { + serialize_array(name, array, out_str, "\n "); + } + void serialize(const BinaryParagraph& pgh, std::string& out_str) { - out_str.append("Package: ").append(pgh.spec.name()).push_back('\n'); - if (!pgh.version.empty()) - out_str.append("Version: ").append(pgh.version).push_back('\n'); - else if (pgh.is_feature()) - out_str.append("Feature: ").append(pgh.feature).push_back('\n'); - if (!pgh.depends.empty()) + const size_t initial_end = out_str.size(); + + serialize_string(Fields::PACKAGE, pgh.spec.name(), out_str); + + serialize_string(Fields::VERSION, pgh.version, out_str); + if (pgh.port_version != 0) + { + out_str.append(Fields::PORT_VERSION).append(": ").append(std::to_string(pgh.port_version)).push_back('\n'); + } + + if (pgh.is_feature()) + { + serialize_string(Fields::FEATURE, pgh.feature, out_str); + } + + if (!pgh.dependencies.empty()) { - out_str.append("Depends: "); - out_str.append(Strings::join(", ", pgh.depends)); - out_str.push_back('\n'); + serialize_array(Fields::DEPENDS, pgh.dependencies, out_str); } - out_str.append("Architecture: ").append(pgh.spec.triplet().to_string()).push_back('\n'); - out_str.append("Multi-Arch: same\n"); + serialize_string(Fields::ARCHITECTURE, pgh.spec.triplet().to_string(), out_str); + serialize_string(Fields::MULTI_ARCH, "same", out_str); - if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n'); - if (!pgh.abi.empty()) out_str.append("Abi: ").append(pgh.abi).push_back('\n'); - if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n'); + serialize_paragraph(Fields::MAINTAINER, pgh.maintainers, out_str); - out_str.append("Type: ").append(Type::to_string(pgh.type)).push_back('\n'); - if (!pgh.default_features.empty()) - out_str.append("Default-Features: ").append(Strings::join(", ", pgh.default_features)).push_back('\n'); + serialize_string(Fields::ABI, pgh.abi, out_str); + + serialize_paragraph(Fields::DESCRIPTION, pgh.description, out_str); + + serialize_string(Fields::TYPE, Type::to_string(pgh.type), out_str); + serialize_array(Fields::DEFAULT_FEATURES, pgh.default_features, out_str); + + // sanity check the serialized data + const auto my_paragraph = out_str.substr(initial_end); + auto parsed_paragraph = Paragraphs::parse_single_paragraph( + out_str.substr(initial_end), "vcpkg::serialize(const BinaryParagraph&, std::string&)"); + if (!parsed_paragraph.has_value()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, + R"([sanity check] Failed to parse a serialized binary paragraph. +Please open an issue at https://github.com/microsoft/vcpkg, with the following output: + Error: %s + +=== Serialized BinaryParagraph === +%s + )", + parsed_paragraph.error(), + my_paragraph); + } + + auto binary_paragraph = BinaryParagraph(*parsed_paragraph.get()); + if (binary_paragraph != pgh) + { + const auto& join_str = R"(", ")"; + Checks::exit_with_message( + VCPKG_LINE_INFO, + R"([sanity check] The serialized binary paragraph was different from the original binary paragraph. +Please open an issue at https://github.com/microsoft/vcpkg, with the following output: + +=== Original BinaryParagraph === +spec: "%s" +version: "%s" +port_version: %d +description: ["%s"] +maintainers: ["%s"] +feature: "%s" +default_features: ["%s"] +dependencies: ["%s"] +abi: "%s" +type: %s + +=== Serialized BinaryParagraph === +spec: "%s" +version: "%s" +port_version: %d +description: ["%s"] +maintainers: ["%s"] +feature: "%s" +default_features: ["%s"] +dependencies: ["%s"] +abi: "%s" +type: %s +)", + pgh.spec.to_string(), + pgh.version, + pgh.port_version, + Strings::join(join_str, pgh.description), + Strings::join(join_str, pgh.maintainers), + pgh.feature, + Strings::join(join_str, pgh.default_features), + Strings::join(join_str, pgh.dependencies), + pgh.abi, + Type::to_string(pgh.type), + binary_paragraph.spec.to_string(), + binary_paragraph.version, + binary_paragraph.port_version, + Strings::join(join_str, binary_paragraph.description), + Strings::join(join_str, binary_paragraph.maintainers), + binary_paragraph.feature, + Strings::join(join_str, binary_paragraph.default_features), + Strings::join(join_str, binary_paragraph.dependencies), + binary_paragraph.abi, + Type::to_string(binary_paragraph.type)); + } } } diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index 7a74f3e83..5e2f8778b 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -153,14 +153,14 @@ namespace vcpkg::Build std::string first_arg = args.command_arguments.at(0);
auto binaryprovider =
- create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
+ create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
const FullPackageSpec spec = Input::check_and_get_full_package_spec(
std::move(first_arg), default_triplet, COMMAND_STRUCTURE.example_text);
Input::check_triplet(spec.package_spec.triplet(), paths);
- PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PathsPortFileProvider provider(paths, args.overlay_ports);
const auto port_name = spec.package_spec.name();
const auto* scfl = provider.get_control_file(port_name).get();
diff --git a/toolsrc/src/vcpkg/buildenvironment.cpp b/toolsrc/src/vcpkg/buildenvironment.cpp index ea3be7bc8..be61ba37c 100644 --- a/toolsrc/src/vcpkg/buildenvironment.cpp +++ b/toolsrc/src/vcpkg/buildenvironment.cpp @@ -14,6 +14,7 @@ namespace vcpkg local_variables.emplace_back("BUILDTREES_DIR", paths.buildtrees);
local_variables.emplace_back("_VCPKG_INSTALLED_DIR", paths.installed);
local_variables.emplace_back("DOWNLOADS", paths.downloads);
+ local_variables.emplace_back("VCPKG_MANIFEST_INSTALL", "OFF");
return System::make_basic_cmake_cmd(paths.get_tool_exe(Tools::CMAKE), cmake_script, local_variables);
}
}
diff --git a/toolsrc/src/vcpkg/commands.buildexternal.cpp b/toolsrc/src/vcpkg/commands.buildexternal.cpp index b25b5e4bf..5609e28f8 100644 --- a/toolsrc/src/vcpkg/commands.buildexternal.cpp +++ b/toolsrc/src/vcpkg/commands.buildexternal.cpp @@ -22,16 +22,16 @@ namespace vcpkg::Commands::BuildExternal const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); auto binaryprovider = - create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO); + create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO); const FullPackageSpec spec = Input::check_and_get_full_package_spec( std::string(args.command_arguments.at(0)), default_triplet, COMMAND_STRUCTURE.example_text); Input::check_triplet(spec.package_spec.triplet(), paths); - auto overlays = args.overlay_ports ? *args.overlay_ports : std::vector<std::string>(); + auto overlays = args.overlay_ports; overlays.insert(overlays.begin(), args.command_arguments.at(1)); - PortFileProvider::PathsPortFileProvider provider(paths, &overlays); + PortFileProvider::PathsPortFileProvider provider(paths, overlays); auto maybe_scfl = provider.get_control_file(spec.package_spec.name()); Checks::check_exit( diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp index efc7bbc15..df69d2e12 100644 --- a/toolsrc/src/vcpkg/commands.ci.cpp +++ b/toolsrc/src/vcpkg/commands.ci.cpp @@ -14,7 +14,7 @@ #include <vcpkg/help.h> #include <vcpkg/input.h> #include <vcpkg/install.h> -#include <vcpkg/logicexpression.h> +#include <vcpkg/platform-expression.h> #include <vcpkg/packagespec.h> #include <vcpkg/vcpkglib.h> @@ -171,7 +171,7 @@ namespace vcpkg::Commands::CI } std::string traits_block; - if (test.abi_tag != "") + if (!test.abi_tag.empty()) { traits_block += Strings::format(R"(<trait name="abi_tag" value="%s" />)", test.abi_tag); } @@ -226,26 +226,11 @@ namespace vcpkg::Commands::CI const InstallPlanAction* install_plan) { auto&& scfl = install_plan->source_control_file_location.value_or_exit(VCPKG_LINE_INFO); - const std::string& supports_expression = scfl.source_control_file->core_paragraph->supports_expression; - if (supports_expression.empty()) - { - return true; // default to 'supported' - } + const auto& supports_expression = scfl.source_control_file->core_paragraph->supports_expression; + PlatformExpression::Context context = + var_provider.get_tag_vars(install_plan->spec).value_or_exit(VCPKG_LINE_INFO); - ExpressionContext context = {var_provider.get_tag_vars(install_plan->spec).value_or_exit(VCPKG_LINE_INFO), - install_plan->spec.triplet().canonical_name()}; - auto maybe_result = evaluate_expression(supports_expression, context); - if (auto b = maybe_result.get()) - return *b; - else - { - System::print2(System::Color::error, - "Error: while processing ", - install_plan->spec.to_string(), - "\n", - maybe_result.error()); - Checks::exit_fail(VCPKG_LINE_INFO); - } + return supports_expression.evaluate(context); } static std::unique_ptr<UnknownCIPortsResults> find_unknown_ports_for_ci( @@ -272,13 +257,13 @@ namespace vcpkg::Commands::CI }; std::vector<PackageSpec> packages_with_qualified_deps; - auto has_qualifier = [](Dependency const& dep) { return !dep.qualifier.empty(); }; + auto has_qualifier = [](Dependency const& dep) { return !dep.platform.is_empty(); }; for (auto&& spec : specs) { auto&& scfl = provider.get_control_file(spec.package_spec.name()).value_or_exit(VCPKG_LINE_INFO); - if (Util::any_of(scfl.source_control_file->core_paragraph->depends, has_qualifier) || + if (Util::any_of(scfl.source_control_file->core_paragraph->dependencies, has_qualifier) || Util::any_of(scfl.source_control_file->feature_paragraphs, - [&](auto&& pgh) { return Util::any_of(pgh->depends, has_qualifier); })) + [&](auto&& pgh) { return Util::any_of(pgh->dependencies, has_qualifier); })) { packages_with_qualified_deps.push_back(spec.package_spec); } @@ -385,7 +370,7 @@ namespace vcpkg::Commands::CI if (args.binary_caching_enabled()) { binaryproviderStorage = - create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO); + create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO); } IBinaryProvider& binaryprovider = binaryproviderStorage ? *binaryproviderStorage : null_binary_provider(); @@ -412,7 +397,7 @@ namespace vcpkg::Commands::CI StatusParagraphs status_db = database_load_check(paths); - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); auto& var_provider = *var_provider_storage; diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp index f09f5e4f8..be6d55304 100644 --- a/toolsrc/src/vcpkg/commands.cpp +++ b/toolsrc/src/vcpkg/commands.cpp @@ -42,7 +42,6 @@ namespace vcpkg::Commands {"update", &Update::perform_and_exit}, {"edit", &Edit::perform_and_exit}, {"create", &Create::perform_and_exit}, - {"import", &Import::perform_and_exit}, {"cache", &Cache::perform_and_exit}, {"portsdiff", &PortsDiff::perform_and_exit}, {"autocomplete", &Autocomplete::perform_and_exit}, @@ -51,6 +50,7 @@ namespace vcpkg::Commands {"x-ci-clean", &CIClean::perform_and_exit}, {"x-history", &PortHistory::perform_and_exit}, {"x-vsinstances", &X_VSInstances::perform_and_exit}, + {"x-format-manifest", &FormatManifest::perform_and_exit}, }; return t; } @@ -89,8 +89,7 @@ namespace vcpkg::Commands::Fetch namespace vcpkg::Commands::Hash { const CommandStructure COMMAND_STRUCTURE = { - Strings::format("The argument should be a file path\n%s", - create_example_string("hash boost_1_62_0.tar.bz2")), + Strings::format("The argument should be a file path\n%s", create_example_string("hash boost_1_62_0.tar.bz2")), 1, 2, {}, diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp index 32f467d41..c78c694f6 100644 --- a/toolsrc/src/vcpkg/commands.dependinfo.cpp +++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp @@ -251,7 +251,7 @@ namespace vcpkg::Commands::DependInfo Input::check_triplet(spec.package_spec.triplet(), paths);
}
- PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PathsPortFileProvider provider(paths, args.overlay_ports);
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;
diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp index b361d6ff1..8cf264d17 100644 --- a/toolsrc/src/vcpkg/commands.env.cpp +++ b/toolsrc/src/vcpkg/commands.env.cpp @@ -39,7 +39,7 @@ namespace vcpkg::Commands::Env const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); auto& var_provider = *var_provider_storage; diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp index db0987512..ede524039 100644 --- a/toolsrc/src/vcpkg/commands.exportifw.cpp +++ b/toolsrc/src/vcpkg/commands.exportifw.cpp @@ -94,7 +94,7 @@ namespace vcpkg::Export::IFW package_xml_file_path.generic_u8string()); auto deps = Strings::join( - ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); + ",", binary_paragraph.dependencies, [](const std::string& dep) { return "packages." + dep + ":"; }); if (!deps.empty()) deps = "\n <Dependencies>" + deps + "</Dependencies>"; @@ -175,7 +175,7 @@ namespace vcpkg::Export::IFW </Package> )###", action.spec.name(), - safe_rich_from_plain_text(binary_paragraph.description), + safe_rich_from_plain_text(Strings::join("\n", binary_paragraph.description)), binary_paragraph.version, create_release_date()), VCPKG_LINE_INFO); diff --git a/toolsrc/src/vcpkg/commands.format-manifest.cpp b/toolsrc/src/vcpkg/commands.format-manifest.cpp new file mode 100644 index 000000000..62e03dbcf --- /dev/null +++ b/toolsrc/src/vcpkg/commands.format-manifest.cpp @@ -0,0 +1,100 @@ +#include "pch.h" + +#include <vcpkg/base/checks.h> +#include <vcpkg/base/files.h> +#include <vcpkg/base/json.h> +#include <vcpkg/base/system.debug.h> +#include <vcpkg/commands.h> +#include <vcpkg/portfileprovider.h> + +namespace vcpkg::Commands::FormatManifest +{ + + static constexpr StringLiteral OPTION_ALL = "--all"; + + const CommandSwitch FORMAT_SWITCHES[] = { + { OPTION_ALL, "Format all ports' manifest files." } + }; + + const CommandStructure COMMAND_STRUCTURE = { + create_example_string(R"###(x-format-manifest --all)###"), + 0, + SIZE_MAX, + { + FORMAT_SWITCHES, + {}, + {} + }, + nullptr, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + auto parsed_args = args.parse_arguments(COMMAND_STRUCTURE); + + std::vector<fs::path> files_to_format; + + auto& fs = paths.get_filesystem(); + bool has_error = false; + + if (Util::Sets::contains(parsed_args.switches, OPTION_ALL)) + { + for (const auto& dir : fs::directory_iterator(paths.ports)) + { + auto manifest_path = dir.path() / fs::u8path("vcpkg.json"); + if (fs.exists(manifest_path)) + { + files_to_format.push_back(std::move(manifest_path)); + } + } + } + + for (const auto& arg : args.command_arguments) + { + auto path = fs::u8path(arg); + if (path.is_relative()) + { + path = paths.original_cwd / path; + } + files_to_format.push_back(std::move(path)); + } + + for (const auto& path : files_to_format) + { + std::error_code ec; + Debug::print("Formatting ", path.u8string(), "\n"); + auto parsed_json_opt = Json::parse_file(fs, path, ec); + if (ec) + { + System::printf(System::Color::error, "Failed to read %s: %s\n", path.u8string(), ec.message()); + has_error = true; + } + + if (auto pr = parsed_json_opt.get()) + { + fs.write_contents(path, Json::stringify(pr->first, Json::JsonStyle{}), ec); + } + else + { + System::printf(System::Color::error, "Failed to parse %s: %s\n", path.u8string(), parsed_json_opt.error()->format()); + has_error = true; + } + + if (ec) + { + System::printf(System::Color::error, "Failed to write %s: %s\n", path.u8string(), ec.message()); + has_error = true; + } + } + + if (has_error) + { + Checks::exit_fail(VCPKG_LINE_INFO); + } + else + { + System::print2("Succeeded in formatting the manifest files.\n"); + Checks::exit_success(VCPKG_LINE_INFO); + } + } +} diff --git a/toolsrc/src/vcpkg/commands.import.cpp b/toolsrc/src/vcpkg/commands.import.cpp deleted file mode 100644 index 9d8e4b4b1..000000000 --- a/toolsrc/src/vcpkg/commands.import.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "pch.h" - -#include <vcpkg/base/files.h> -#include <vcpkg/commands.h> -#include <vcpkg/help.h> -#include <vcpkg/paragraphs.h> -#include <vcpkg/statusparagraph.h> - -namespace vcpkg::Commands::Import -{ - struct Binaries - { - std::vector<fs::path> dlls; - std::vector<fs::path> libs; - }; - - static void check_is_directory(const LineInfo& line_info, const Files::Filesystem& fs, const fs::path& dirpath) - { - Checks::check_exit(line_info, fs.is_directory(dirpath), "The path %s is not a directory", dirpath.string()); - } - - static Binaries find_binaries_in_dir(const Files::Filesystem& fs, const fs::path& path) - { - auto files = fs.get_files_recursive(path); - - check_is_directory(VCPKG_LINE_INFO, fs, path); - - Binaries binaries; - for (auto&& file : files) - { - if (fs.is_directory(file)) continue; - const auto ext = file.extension(); - if (ext == ".dll") - binaries.dlls.push_back(std::move(file)); - else if (ext == ".lib") - binaries.libs.push_back(std::move(file)); - } - return binaries; - } - - static void copy_files_into_directory(Files::Filesystem& fs, - const std::vector<fs::path>& files, - const fs::path& destination_folder) - { - std::error_code ec; - fs.create_directory(destination_folder, ec); - - for (auto const& src_path : files) - { - const fs::path dest_path = destination_folder / src_path.filename(); - fs.copy(src_path, dest_path, fs::copy_options::overwrite_existing); - } - } - - static void place_library_files_in(Files::Filesystem& fs, - const fs::path& include_directory, - const fs::path& project_directory, - const fs::path& destination_path) - { - check_is_directory(VCPKG_LINE_INFO, fs, include_directory); - check_is_directory(VCPKG_LINE_INFO, fs, project_directory); - check_is_directory(VCPKG_LINE_INFO, fs, destination_path); - const Binaries debug_binaries = find_binaries_in_dir(fs, project_directory / "Debug"); - const Binaries release_binaries = find_binaries_in_dir(fs, project_directory / "Release"); - - const fs::path destination_include_directory = destination_path / "include"; - fs.copy(include_directory, - destination_include_directory, - fs::copy_options::recursive | fs::copy_options::overwrite_existing); - - copy_files_into_directory(fs, release_binaries.dlls, destination_path / "bin"); - copy_files_into_directory(fs, release_binaries.libs, destination_path / "lib"); - - std::error_code ec; - fs.create_directory(destination_path / "debug", ec); - copy_files_into_directory(fs, debug_binaries.dlls, destination_path / "debug" / "bin"); - copy_files_into_directory(fs, debug_binaries.libs, destination_path / "debug" / "lib"); - } - - static void do_import(const VcpkgPaths& paths, - const fs::path& include_directory, - const fs::path& project_directory, - const BinaryParagraph& control_file_data) - { - auto& fs = paths.get_filesystem(); - const fs::path library_destination_path = paths.package_dir(control_file_data.spec); - std::error_code ec; - fs.create_directory(library_destination_path, ec); - place_library_files_in(paths.get_filesystem(), include_directory, project_directory, library_destination_path); - - const fs::path control_file_path = library_destination_path / "CONTROL"; - fs.write_contents(control_file_path, Strings::serialize(control_file_data), VCPKG_LINE_INFO); - } - - const CommandStructure COMMAND_STRUCTURE = { - create_example_string(R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"), - 3, - 3, - {}, - nullptr, - }; - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - Util::unused(args.parse_arguments(COMMAND_STRUCTURE)); - - const fs::path control_file_path(args.command_arguments[0]); - const fs::path include_directory(args.command_arguments[1]); - const fs::path project_directory(args.command_arguments[2]); - - const auto pghs = Paragraphs::get_single_paragraph(paths.get_filesystem(), control_file_path); - Checks::check_exit(VCPKG_LINE_INFO, - pghs.get() != nullptr, - "Invalid control file %s for package", - control_file_path.generic_u8string(), - "\n", - pghs.error()); - - StatusParagraph spgh; - spgh.package = BinaryParagraph(*pghs.get()); - auto& control_file_data = spgh.package; - - do_import(paths, include_directory, project_directory, control_file_data); - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp index c837dc395..e95b02623 100644 --- a/toolsrc/src/vcpkg/commands.list.cpp +++ b/toolsrc/src/vcpkg/commands.list.cpp @@ -14,14 +14,22 @@ namespace vcpkg::Commands::List { if (full_desc) { - System::printf("%-50s %-16s %s\n", pgh.package.displayname(), pgh.package.version, pgh.package.description); + System::printf("%-50s %-16s %s\n", + pgh.package.displayname(), + pgh.package.version, + Strings::join("\n ", pgh.package.description)); } else { + std::string description; + if (!pgh.package.description.empty()) + { + description = pgh.package.description[0]; + } System::printf("%-50s %-16s %s\n", vcpkg::shorten_text(pgh.package.displayname(), 50), vcpkg::shorten_text(pgh.package.version, 16), - vcpkg::shorten_text(pgh.package.description, 51)); + vcpkg::shorten_text(description, 51)); } } diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp index ebaa3ce43..c234c3ab9 100644 --- a/toolsrc/src/vcpkg/commands.search.cpp +++ b/toolsrc/src/vcpkg/commands.search.cpp @@ -20,15 +20,22 @@ namespace vcpkg::Commands::Search { if (full_desc) { - System::printf( - "%-20s %-16s %s\n", source_paragraph.name, source_paragraph.version, source_paragraph.description); + System::printf("%-20s %-16s %s\n", + source_paragraph.name, + source_paragraph.version, + Strings::join("\n ", source_paragraph.description)); } else { + std::string description; + if (!source_paragraph.description.empty()) + { + description = source_paragraph.description[0]; + } System::printf("%-20s %-16s %s\n", vcpkg::shorten_text(source_paragraph.name, 20), vcpkg::shorten_text(source_paragraph.version, 16), - vcpkg::shorten_text(source_paragraph.description, 81)); + vcpkg::shorten_text(description, 81)); } } @@ -37,13 +44,17 @@ namespace vcpkg::Commands::Search auto full_feature_name = Strings::concat(name, "[", feature_paragraph.name, "]"); if (full_desc) { - System::printf("%-37s %s\n", full_feature_name, feature_paragraph.description); + System::printf("%-37s %s\n", full_feature_name, Strings::join("\n ", feature_paragraph.description)); } else { - System::printf("%-37s %s\n", - vcpkg::shorten_text(full_feature_name, 37), - vcpkg::shorten_text(feature_paragraph.description, 81)); + std::string description; + if (!feature_paragraph.description.empty()) + { + description = feature_paragraph.description[0]; + } + System::printf( + "%-37s %s\n", vcpkg::shorten_text(full_feature_name, 37), vcpkg::shorten_text(description, 81)); } } @@ -66,7 +77,7 @@ namespace vcpkg::Commands::Search const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); const bool full_description = Util::Sets::contains(options.switches, OPTION_FULLDESC); - PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PathsPortFileProvider provider(paths, args.overlay_ports); auto source_paragraphs = Util::fmap(provider.load_all_control_files(), [](auto&& port) -> const SourceControlFile* { return port->source_control_file.get(); }); @@ -84,24 +95,40 @@ namespace vcpkg::Commands::Search } else { - const auto& icontains = Strings::case_insensitive_ascii_contains; - // At this point there is 1 argument auto&& args_zero = args.command_arguments[0]; + const auto contained_in = [&args_zero](const auto& s) { + return Strings::case_insensitive_ascii_contains(s, args_zero); + }; for (const auto& source_control_file : source_paragraphs) { auto&& sp = *source_control_file->core_paragraph; - const bool contains_name = icontains(sp.name, args_zero); - if (contains_name || icontains(sp.description, args_zero)) + bool found_match = contained_in(sp.name); + if (!found_match) + { + found_match = std::any_of(sp.description.begin(), sp.description.end(), contained_in); + } + + if (found_match) { do_print(sp, full_description); } for (auto&& feature_paragraph : source_control_file->feature_paragraphs) { - if (contains_name || icontains(feature_paragraph->name, args_zero) || - icontains(feature_paragraph->description, args_zero)) + bool found_match_for_feature = found_match; + if (!found_match_for_feature) + { + found_match_for_feature = contained_in(feature_paragraph->name); + } + if (!found_match_for_feature) + { + found_match_for_feature = std::any_of( + feature_paragraph->description.begin(), feature_paragraph->description.end(), contained_in); + } + + if (found_match_for_feature) { do_print(sp.name, *feature_paragraph, full_description); } diff --git a/toolsrc/src/vcpkg/commands.setinstalled.cpp b/toolsrc/src/vcpkg/commands.setinstalled.cpp index 5a473b4ee..f52e4942b 100644 --- a/toolsrc/src/vcpkg/commands.setinstalled.cpp +++ b/toolsrc/src/vcpkg/commands.setinstalled.cpp @@ -27,52 +27,27 @@ namespace vcpkg::Commands::SetInstalled nullptr, }; - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) + void perform_and_exit_ex(const VcpkgCmdArguments& args, + const VcpkgPaths& paths, + const PortFileProvider::PathsPortFileProvider& provider, + IBinaryProvider& binary_provider, + const CMakeVars::CMakeVarProvider& cmake_vars, + const std::vector<FullPackageSpec>& specs, + const Build::BuildPackageOptions& install_plan_options, + DryRun dry_run) { - // input sanitization - const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); - - const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) { - return Input::check_and_get_full_package_spec( - std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text); - }); - - for (auto&& spec : specs) - { - Input::check_triplet(spec.package_spec.triplet(), paths); - } - - auto binaryprovider = - create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO); - - const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN); - - const Build::BuildPackageOptions install_plan_options = { - Build::UseHeadVersion::NO, - Build::AllowDownloads::YES, - Build::OnlyDownloads::NO, - Build::CleanBuildtrees::YES, - Build::CleanPackages::YES, - Build::CleanDownloads::YES, - Build::DownloadTool::BUILT_IN, - Build::FailOnTombstone::NO, - }; - - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); - auto cmake_vars = CMakeVars::make_triplet_cmake_var_provider(paths); - // We have a set of user-requested specs. // We need to know all the specs which are required to fulfill dependencies for those specs. // Therefore, we see what we would install into an empty installed tree, so we can use the existing code. - auto action_plan = Dependencies::create_feature_install_plan(provider, *cmake_vars, specs, {}); + auto action_plan = Dependencies::create_feature_install_plan(provider, cmake_vars, specs, {}); for (auto&& action : action_plan.install_actions) { action.build_options = install_plan_options; } - cmake_vars->load_tag_vars(action_plan, provider); - Build::compute_all_abis(paths, action_plan, *cmake_vars, {}); + cmake_vars.load_tag_vars(action_plan, provider); + Build::compute_all_abis(paths, action_plan, cmake_vars, {}); std::set<std::string> all_abis; @@ -116,7 +91,7 @@ namespace vcpkg::Commands::SetInstalled Dependencies::print_plan(action_plan, true, paths.ports); - if (dry_run) + if (dry_run == DryRun::Yes) { Checks::exit_success(VCPKG_LINE_INFO); } @@ -125,12 +100,55 @@ namespace vcpkg::Commands::SetInstalled Install::KeepGoing::NO, paths, status_db, - args.binary_caching_enabled() ? *binaryprovider : null_binary_provider(), - *cmake_vars); + args.binary_caching_enabled() ? binary_provider : null_binary_provider(), + cmake_vars); System::print2("\nTotal elapsed time: ", summary.total_elapsed_time, "\n\n"); Checks::exit_success(VCPKG_LINE_INFO); } + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) + { + // input sanitization + const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); + + const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_full_package_spec( + std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text); + }); + + for (auto&& spec : specs) + { + Input::check_triplet(spec.package_spec.triplet(), paths); + } + + auto binary_provider = + create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO); + + const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN); + + const Build::BuildPackageOptions install_plan_options = { + Build::UseHeadVersion::NO, + Build::AllowDownloads::YES, + Build::OnlyDownloads::NO, + Build::CleanBuildtrees::YES, + Build::CleanPackages::YES, + Build::CleanDownloads::YES, + Build::DownloadTool::BUILT_IN, + Build::FailOnTombstone::NO, + }; + + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); + auto cmake_vars = CMakeVars::make_triplet_cmake_var_provider(paths); + + perform_and_exit_ex(args, + paths, + provider, + *binary_provider, + *cmake_vars, + specs, + install_plan_options, + dry_run ? DryRun::Yes : DryRun::No); + } } diff --git a/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp index 919f07e91..89466ab60 100644 --- a/toolsrc/src/vcpkg/commands.upgrade.cpp +++ b/toolsrc/src/vcpkg/commands.upgrade.cpp @@ -43,12 +43,12 @@ namespace vcpkg::Commands::Upgrade const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING)); auto binaryprovider = - create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO); + create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO); StatusParagraphs status_db = database_load_check(paths); // Load ports from ports dirs - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); auto& var_provider = *var_provider_storage; diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp index 53db3b6d7..ee28343b6 100644 --- a/toolsrc/src/vcpkg/dependencies.cpp +++ b/toolsrc/src/vcpkg/dependencies.cpp @@ -53,7 +53,7 @@ namespace vcpkg::Dependencies { } - Cluster(const PackageSpec& spec, const SourceControlFileLocation& scfl) : m_spec(spec), m_scfl(scfl) {} + Cluster(const PackageSpec& spec, const SourceControlFileLocation& scfl) : m_spec(spec), m_scfl(scfl) { } bool has_feature_installed(const std::string& feature) const { @@ -124,12 +124,11 @@ namespace vcpkg::Dependencies bool requires_qualified_resolution = false; for (const Dependency& dep : *qualified_deps) { - if (dep.qualifier.empty()) + if (dep.platform.is_empty()) { - Util::Vectors::append( - &dep_list, - FullPackageSpec({dep.depend.name, m_spec.triplet()}, dep.depend.features) - .to_feature_specs({"default"}, {"default"})); + Util::Vectors::append(&dep_list, + FullPackageSpec({dep.name, m_spec.triplet()}, dep.features) + .to_feature_specs({"default"}, {"default"})); } else { @@ -684,7 +683,7 @@ namespace vcpkg::Dependencies const std::vector<Dependency>* paragraph_depends = nullptr; if (spec.feature() == "core") { - paragraph_depends = &clust.m_scfl.source_control_file->core_paragraph->depends; + paragraph_depends = &clust.m_scfl.source_control_file->core_paragraph->dependencies; } else if (spec.feature() == "default") { @@ -697,12 +696,12 @@ namespace vcpkg::Dependencies "Package %s does not have a %s feature", spec.name(), spec.feature()); - paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).depends; + paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).dependencies; } // And it has at least one qualified dependency if (paragraph_depends && - Util::any_of(*paragraph_depends, [](auto&& dep) { return !dep.qualifier.empty(); })) + Util::any_of(*paragraph_depends, [](auto&& dep) { return !dep.platform.is_empty(); })) { // Add it to the next batch run qualified_dependencies.emplace_back(spec); @@ -795,7 +794,7 @@ namespace vcpkg::Dependencies { struct BaseEdgeProvider : Graphs::AdjacencyProvider<PackageSpec, const Cluster*> { - BaseEdgeProvider(const ClusterGraph& parent) : m_parent(parent) {} + BaseEdgeProvider(const ClusterGraph& parent) : m_parent(parent) { } std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); } const Cluster* load_vertex_data(const PackageSpec& spec) const override diff --git a/toolsrc/src/vcpkg/export.chocolatey.cpp b/toolsrc/src/vcpkg/export.chocolatey.cpp index 8d98a4d3f..26af98c61 100644 --- a/toolsrc/src/vcpkg/export.chocolatey.cpp +++ b/toolsrc/src/vcpkg/export.chocolatey.cpp @@ -19,7 +19,7 @@ namespace vcpkg::Export::Chocolatey static constexpr auto CONTENT_TEMPLATE = R"(<dependency id="@PACKAGE_ID@" version="[@PACKAGE_VERSION@]" />)";
std::string nuspec_dependencies;
- for (const std::string& depend : binary_paragraph.depends)
+ for (const std::string& depend : binary_paragraph.dependencies)
{
auto found = packages_version.find(depend);
if (found == packages_version.end())
@@ -68,8 +68,8 @@ namespace vcpkg::Export::Chocolatey Strings::replace_all(std::move(nuspec_file_content), "@PACKAGE_VERSION@", package_version->second);
nuspec_file_content = Strings::replace_all(
std::move(nuspec_file_content), "@PACKAGE_MAINTAINER@", chocolatey_options.maybe_maintainer.value_or(""));
- nuspec_file_content =
- Strings::replace_all(std::move(nuspec_file_content), "@PACKAGE_DESCRIPTION@", binary_paragraph.description);
+ nuspec_file_content = Strings::replace_all(
+ std::move(nuspec_file_content), "@PACKAGE_DESCRIPTION@", Strings::join("\n", binary_paragraph.description));
nuspec_file_content =
Strings::replace_all(std::move(nuspec_file_content), "@EXPORTED_ROOT_DIR@", exported_root_dir);
nuspec_file_content = Strings::replace_all(std::move(nuspec_file_content),
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp index 205fcd31e..138c03b3f 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -589,13 +589,17 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) { + if (paths.manifest_mode_enabled()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "vcpkg export does not support manifest mode, in order to allow for future design considerations. You may use export in classic mode by running vcpkg outside of a manifest-based project."); + } const StatusParagraphs status_db = database_load_check(paths); const auto opts = handle_export_command_arguments(args, default_triplet, status_db); for (auto&& spec : opts.specs) Input::check_triplet(spec.triplet(), paths); // Load ports from ports dirs - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); // create the plan std::vector<ExportPlanAction> export_plan = Dependencies::create_export_plan(opts.specs, status_db); diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 30c997498..5750469ad 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -84,9 +84,10 @@ namespace vcpkg::Install const std::string filename = file.filename().generic_u8string(); if (fs::is_regular_file(status) && (Strings::case_insensitive_ascii_equals(filename, "CONTROL") || + Strings::case_insensitive_ascii_equals(filename, "vcpkg.json") || Strings::case_insensitive_ascii_equals(filename, "BUILD_INFO"))) { - // Do not copy the control file + // Do not copy the control file or manifest file continue; } @@ -530,6 +531,14 @@ namespace vcpkg::Install &get_all_port_names, }; + const CommandStructure MANIFEST_COMMAND_STRUCTURE = { + create_example_string("install --triplet x64-windows"), + 0, + 0, + {INSTALL_SWITCHES, INSTALL_SETTINGS}, + nullptr, + }; + static void print_cmake_information(const BinaryParagraph& bpgh, const VcpkgPaths& paths) { static const std::regex cmake_library_regex(R"(\badd_library\(([^\$\s\)]+)\s)", @@ -645,20 +654,10 @@ namespace vcpkg::Install void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) { // input sanitization - const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); + const ParsedArguments options = args.parse_arguments(paths.manifest_mode_enabled() ? MANIFEST_COMMAND_STRUCTURE : COMMAND_STRUCTURE); auto binaryprovider = - create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO); - - const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) { - return Input::check_and_get_full_package_spec( - std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text); - }); - - for (auto&& spec : specs) - { - Input::check_triplet(spec.package_spec.triplet(), paths); - } + create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO); const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN); const bool use_head_version = Util::Sets::contains(options.switches, (OPTION_USE_HEAD_VERSION)); @@ -672,10 +671,6 @@ namespace vcpkg::Install auto& fs = paths.get_filesystem(); - // create the plan - System::print2("Computing installation plan...\n"); - StatusParagraphs status_db = database_load_check(paths); - Build::DownloadTool download_tool = Build::DownloadTool::BUILT_IN; if (use_aria2) download_tool = Build::DownloadTool::ARIA2; @@ -690,11 +685,54 @@ namespace vcpkg::Install Build::FailOnTombstone::NO, }; - //// Load ports from ports dirs - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); auto& var_provider = *var_provider_storage; + if (paths.manifest_mode_enabled()) + { + std::error_code ec; + const auto path_to_manifest = paths.manifest_root_dir / "vcpkg.json"; + auto res = Paragraphs::try_load_manifest(paths.get_filesystem(), "user manifest", path_to_manifest, ec); + + if (ec) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "Failed to load manifest file (%s): %s\n", + path_to_manifest.u8string(), ec.message()); + } + + std::vector<FullPackageSpec> specs; + if (auto val = res.get()) + { + for (auto& dep : (*val)->core_paragraph->dependencies) + { + specs.push_back(Input::check_and_get_full_package_spec( + std::move(dep.name), default_triplet, COMMAND_STRUCTURE.example_text)); + } + } + else + { + print_error_message(res.error()); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + Commands::SetInstalled::perform_and_exit_ex(args, paths, provider, *binaryprovider, var_provider, specs, install_plan_options, dry_run ? Commands::DryRun::Yes : Commands::DryRun::No); + } + + const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_full_package_spec( + std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text); + }); + + for (auto&& spec : specs) + { + Input::check_triplet(spec.package_spec.triplet(), paths); + } + + // create the plan + System::print2("Computing installation plan...\n"); + StatusParagraphs status_db = database_load_check(paths); + // Note: action_plan will hold raw pointers to SourceControlFileLocations from this map auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, status_db); diff --git a/toolsrc/src/vcpkg/logicexpression.cpp b/toolsrc/src/vcpkg/logicexpression.cpp deleted file mode 100644 index 5f3a25e8e..000000000 --- a/toolsrc/src/vcpkg/logicexpression.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "pch.h" - -#include <vcpkg/base/parse.h> -#include <vcpkg/base/strings.h> -#include <vcpkg/base/system.print.h> -#include <vcpkg/logicexpression.h> - -#include <string> -#include <vector> - -namespace vcpkg -{ - using vcpkg::Parse::ParseError; - - enum class Identifier - { - invalid, // not a recognized identifier - x64, - x86, - arm, - arm64, - - windows, - linux, - osx, - uwp, - android, - emscripten, - wasm32, - - static_link, - }; - - // logic expression supports the following : - // primary-expression: - // ( logic-expression ) - // identifier - // identifier: - // alpha-numeric string of characters - // logic-expression: <- this is the entry point - // not-expression - // not-expression | logic-expression - // not-expression & logic-expression - // not-expression: - // ! primary-expression - // primary-expression - // - // | and & have equal precidence and cannot be used together at the same nesting level - // for example a|b&c is not allowd but (a|b)&c and a|(b&c) are allowed. - class ExpressionParser : public Parse::ParserBase - { - public: - ExpressionParser(const std::string& str, const ExpressionContext& context) : - Parse::ParserBase(str, "CONTROL"), evaluation_context(context) - { - { - auto override_vars = evaluation_context.cmake_context.find("VCPKG_DEP_INFO_OVERRIDE_VARS"); - if (override_vars != evaluation_context.cmake_context.end()) - { - auto cmake_list = Strings::split(override_vars->second, ';'); - for (auto& override_id : cmake_list) - { - if (!override_id.empty()) - { - if (override_id[0] == '!') - { - context_override.insert({override_id.substr(1), false}); - } - else - { - context_override.insert({override_id, true}); - } - } - } - } - } - skip_whitespace(); - - final_result = logic_expression(); - - if (!at_eof()) - { - add_error("invalid logic expression, unexpected character"); - } - } - - bool get_result() const { return final_result; } - - private: - const ExpressionContext& evaluation_context; - std::map<std::string, bool> context_override; - - bool final_result; - - static bool is_identifier_char(char32_t ch) - { - return is_upper_alpha(ch) || is_lower_alpha(ch) || is_ascii_digit(ch) || ch == '-'; - } - - // Legacy evaluation only searches for substrings. Use this only for diagnostic purposes. - bool evaluate_identifier_legacy(const std::string name) const - { - return evaluation_context.legacy_context.find(name) != std::string::npos; - } - - static Identifier string2identifier(const std::string& name) - { - static const std::map<std::string, Identifier> id_map = { - {"x64", Identifier::x64}, - {"x86", Identifier::x86}, - {"arm", Identifier::arm}, - {"arm64", Identifier::arm64}, - {"windows", Identifier::windows}, - {"linux", Identifier::linux}, - {"osx", Identifier::osx}, - {"uwp", Identifier::uwp}, - {"android", Identifier::android}, - {"emscripten", Identifier::emscripten}, - {"wasm32", Identifier::wasm32}, - {"static", Identifier::static_link}, - }; - - auto id_pair = id_map.find(name); - - if (id_pair == id_map.end()) - { - return Identifier::invalid; - } - - return id_pair->second; - } - - bool true_if_exists_and_equal(const std::string& variable_name, const std::string& value) - { - auto iter = evaluation_context.cmake_context.find(variable_name); - if (iter == evaluation_context.cmake_context.end()) - { - return false; - } - return iter->second == value; - } - - // If an identifier is on the explicit override list, return the override value - // Otherwise fall back to the built in logic to evaluate - // All unrecognized identifiers are an error - bool evaluate_identifier_cmake(const std::string name, const SourceLoc& loc) - { - auto id = string2identifier(name); - - switch (id) - { - case Identifier::invalid: - // Point out in the diagnostic that they should add to the override list because that is what - // most users should do, however it is also valid to update the built in identifiers to recognize - // the name. - add_error("Unrecognized identifer name. Add to override list in triplet file.", loc); - break; - - case Identifier::x64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x64"); - case Identifier::x86: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x86"); - case Identifier::arm: - // For backwards compatability arm is also true for arm64. - // This is because it previously was only checking for a substring. - return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm") || - true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64"); - case Identifier::arm64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64"); - case Identifier::windows: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "") || true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"); - case Identifier::linux: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Linux"); - case Identifier::osx: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Darwin"); - case Identifier::uwp: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"); - case Identifier::android: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Android"); - case Identifier::emscripten: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Emscripten"); - case Identifier::wasm32: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "wasm32"); - case Identifier::static_link: return true_if_exists_and_equal("VCPKG_LIBRARY_LINKAGE", "static"); - } - - return evaluation_context.legacy_context.find(name) != std::string::npos; - } - - bool evaluate_identifier(const std::string name, const SourceLoc& loc) - { - if (!context_override.empty()) - { - auto override_id = context_override.find(name); - if (override_id != context_override.end()) - { - return override_id->second; - } - // Fall through to use the cmake logic if the id does not have an override - } - - bool legacy = evaluate_identifier_legacy(name); - bool cmake = evaluate_identifier_cmake(name, loc); - if (legacy != cmake) - { - // Legacy evaluation only used the name of the triplet, now we use the actual - // cmake variables. This has the potential to break custom triplets. - // For now just print a message, this will need to change once we start introducing - // new variables that did not exist previously (such as host-*) - System::print2("Warning: qualifier has changed meaning recently:\n ", name, '\n'); - } - return cmake; - } - - // identifier: - // alpha-numeric string of characters - bool identifier_expression() - { - auto start_loc = cur_loc(); - std::string name = match_zero_or_more(is_identifier_char).to_string(); - - if (name.empty()) - { - add_error("unexpected character in logic expression"); - return false; - } - - bool result = evaluate_identifier(name, start_loc); - skip_whitespace(); - return result; - } - - // not-expression: - // ! primary-expression - // primary-expression - bool not_expression() - { - if (cur() == '!') - { - next(); - skip_whitespace(); - return !primary_expression(); - } - - return primary_expression(); - } - - template<char oper, char other, bool operation(bool, bool)> - bool logic_expression_helper(bool seed) - { - do - { - // Support chains of the operator to avoid breaking backwards compatability - while (next() == oper) - { - }; - skip_whitespace(); - seed = operation(not_expression(), seed); - - } while (cur() == oper); - - if (cur() == other) - { - add_error("mixing & and | is not allowed, use () to specify order of operations"); - } - - skip_whitespace(); - return seed; - } - static bool and_helper(bool left, bool right) { return left && right; } - static bool or_helper(bool left, bool right) { return left || right; } - - // logic-expression: <- entry point - // not-expression - // not-expression | logic-expression - // not-expression & logic-expression - bool logic_expression() - { - auto result = not_expression(); - - switch (cur()) - { - case '|': - { - return logic_expression_helper<'|', '&', or_helper>(result); - } - case '&': - { - return logic_expression_helper<'&', '|', and_helper>(result); - } - default: return result; - } - } - - // primary-expression: - // ( logic-expression ) - // identifier - bool primary_expression() - { - if (cur() == '(') - { - next(); - skip_whitespace(); - bool result = logic_expression(); - if (cur() != ')') - { - add_error("missing closing )"); - return result; - } - next(); - skip_whitespace(); - return result; - } - - return identifier_expression(); - } - }; - - ExpectedT<bool, std::string> evaluate_expression(const std::string& expression, const ExpressionContext& context) - { - ExpressionParser parser(expression, context); - - if (auto err = parser.get_error()) - { - return err->format(); - } - - return parser.get_result(); - } -} diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp index e2661d98a..7920cd7a5 100644 --- a/toolsrc/src/vcpkg/metrics.cpp +++ b/toolsrc/src/vcpkg/metrics.cpp @@ -153,6 +153,8 @@ namespace vcpkg::Metrics Json::Array buildtime_names; Json::Array buildtime_times; + Json::Object feature_flags; + void track_property(const std::string& name, const std::string& value) { properties.insert_or_replace(name, Json::Value::string(value)); @@ -168,6 +170,10 @@ namespace vcpkg::Metrics buildtime_names.push_back(Json::Value::string(name)); buildtime_times.push_back(Json::Value::number(value)); } + void track_feature(const std::string& name, bool value) + { + feature_flags.insert(name, Json::Value::boolean(value)); + } std::string format_event_data_template() const { @@ -226,6 +232,7 @@ namespace vcpkg::Metrics base_data.insert("name", Json::Value::string("commandline_test7")); base_data.insert("properties", Json::Value::object(std::move(props_plus_buildtimes))); base_data.insert("measurements", Json::Value::object(measurements.clone())); + base_data.insert("feature-flags", Json::Value::object(feature_flags.clone())); } return Json::stringify(arr, vcpkg::Json::JsonStyle()); @@ -356,6 +363,15 @@ namespace vcpkg::Metrics g_metricmessage.track_property(name, value); } + void Metrics::track_feature(const std::string& name, bool value) + { + if (!metrics_enabled()) + { + return; + } + g_metricmessage.track_feature(name, value); + } + void Metrics::upload(const std::string& payload) { if (!metrics_enabled()) diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp index 0f3bfd880..f82806879 100644 --- a/toolsrc/src/vcpkg/packagespec.cpp +++ b/toolsrc/src/vcpkg/packagespec.cpp @@ -64,7 +64,8 @@ namespace vcpkg { return parse_qualified_specifier(spec_as_string) .then([&](ParsedQualifiedSpecifier&& p) -> ExpectedS<FullPackageSpec> { - if (p.qualifier) return "Error: qualifier not allowed in this context: " + spec_as_string + "\n"; + if (p.platform) + return "Error: platform specifier not allowed in this context: " + spec_as_string + "\n"; auto triplet = p.triplet ? Triplet::from_canonical_name(std::move(*p.triplet.get())) : default_triplet; return FullPackageSpec({p.name, triplet}, p.features.value_or({})); }); @@ -97,7 +98,7 @@ namespace vcpkg { return parse_qualified_specifier(name).then([&](ParsedQualifiedSpecifier&& pqs) -> ExpectedS<Features> { if (pqs.triplet) return "Error: triplet not allowed in this context: " + name + "\n"; - if (pqs.qualifier) return "Error: qualifier not allowed in this context: " + name + "\n"; + if (pqs.platform) return "Error: platform specifier not allowed in this context: " + name + "\n"; return Features{pqs.name, pqs.features.value_or({})}; }); } @@ -107,8 +108,10 @@ namespace vcpkg return Parse::ParserBase::is_lower_alpha(ch) || Parse::ParserBase::is_ascii_digit(ch) || ch == '-'; } - static bool is_feature_name_char(char32_t ch) { - // TODO: we do not intend underscores to be valid, however there is currently a feature using them (libwebp[vwebp_sdl]). + static bool is_feature_name_char(char32_t ch) + { + // TODO: we do not intend underscores to be valid, however there is currently a feature using them + // (libwebp[vwebp_sdl]). // TODO: we need to rename this feature, then remove underscores from this list. return is_package_name_char(ch) || ch == '_'; } @@ -127,11 +130,15 @@ namespace vcpkg using Parse::ParserBase; auto ret = parser.match_zero_or_more(is_feature_name_char).to_string(); auto ch = parser.cur(); - if (ParserBase::is_upper_alpha(ch) || ch == '_') + + // ignores the feature name vwebp_sdl as a back-compat thing + const bool has_underscore = std::find(ret.begin(), ret.end(), '_') != ret.end() && ret != "vwebp_sdl"; + if (has_underscore || ParserBase::is_upper_alpha(ch)) { parser.add_error("invalid character in feature name (must be lowercase, digits, '-')"); return nullopt; } + if (ret.empty()) { parser.add_error("expected feature name (must be lowercase, digits, '-')"); @@ -224,6 +231,7 @@ namespace vcpkg if (ch == '(') { auto loc = parser.cur_loc(); + std::string platform_string; int depth = 1; while (depth > 0 && (ch = parser.next()) != 0) { @@ -232,12 +240,19 @@ namespace vcpkg } if (depth > 0) { - parser.add_error("unmatched open braces in qualifier", loc); + parser.add_error("unmatched open braces in platform specifier", loc); return nullopt; } - ret.qualifier = std::string( - (++loc.it).pointer_to_current(), - parser.it().pointer_to_current()); + platform_string.append((++loc.it).pointer_to_current(), parser.it().pointer_to_current()); + auto platform_opt = PlatformExpression::parse_platform_expression(platform_string, PlatformExpression::MultipleBinaryOperators::Allow); + if (auto platform = platform_opt.get()) + { + ret.platform = std::move(*platform); + } + else + { + parser.add_error(platform_opt.error(), loc); + } parser.next(); } // This makes the behavior of the parser more consistent -- otherwise, it will skip tabs and spaces only if diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp index 7032620a1..458950eea 100644 --- a/toolsrc/src/vcpkg/paragraphs.cpp +++ b/toolsrc/src/vcpkg/paragraphs.cpp @@ -67,7 +67,7 @@ namespace vcpkg::Paragraphs } public: - PghParser(StringView text, StringView origin) : Parse::ParserBase(text, origin) {} + PghParser(StringView text, StringView origin) : Parse::ParserBase(text, origin) { } ExpectedS<std::vector<Paragraph>> get_paragraphs() { @@ -86,7 +86,7 @@ namespace vcpkg::Paragraphs } }; - static ExpectedS<Paragraph> parse_single_paragraph(const std::string& str, const std::string& origin) + ExpectedS<Paragraph> parse_single_paragraph(const std::string& str, const std::string& origin) { auto pghs = PghParser(str, origin).get_paragraphs(); @@ -128,16 +128,67 @@ namespace vcpkg::Paragraphs return PghParser(str, origin).get_paragraphs(); } + bool is_port_directory(const Files::Filesystem& fs, const fs::path& path) + { + return fs.exists(path / fs::u8path("CONTROL")) || fs.exists(path / fs::u8path("vcpkg.json")); + } + + ParseExpected<SourceControlFile> try_load_manifest(const Files::Filesystem& fs, const std::string& port_name, const fs::path& path_to_manifest, std::error_code& ec) + { + auto error_info = std::make_unique<ParseControlErrorInfo>(); + auto res = Json::parse_file(fs, path_to_manifest, ec); + if (ec) return error_info; + + if (auto val = res.get()) + { + if (val->first.is_object()) + { + return SourceControlFile::parse_manifest_file(path_to_manifest, val->first.object()); + } + else + { + error_info->name = port_name; + error_info->error = "Manifest files must have a top-level object"; + return error_info; + } + } + else + { + error_info->name = port_name; + error_info->error = res.error()->format(); + return error_info; + } + } + ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path) { - const auto path_to_control = path / "CONTROL"; + const auto path_to_manifest = path / fs::u8path("vcpkg.json"); + const auto path_to_control = path / fs::u8path("CONTROL"); + if (fs.exists(path_to_manifest)) { + vcpkg::Checks::check_exit( + VCPKG_LINE_INFO, + !fs.exists(path_to_control), + "Found both manifest and CONTROL file in port %s; please rename one or the other", + path.u8string()); + + std::error_code ec; + auto res = try_load_manifest(fs, path.filename().u8string(), path_to_manifest, ec); + if (ec) + { + auto error_info = std::make_unique<ParseControlErrorInfo>(); + error_info->name = path.filename().u8string(); + error_info->error = Strings::format("Failed to load manifest file for port: %s\n", path_to_manifest.u8string(), ec.message()); + } + + return res; + } ExpectedS<std::vector<Paragraph>> pghs = get_paragraphs(fs, path_to_control); if (auto vector_pghs = pghs.get()) { return SourceControlFile::parse_control_file(path_to_control, std::move(*vector_pghs)); } auto error_info = std::make_unique<ParseControlErrorInfo>(); - error_info->name = path.filename().generic_u8string(); + error_info->name = path.filename().u8string(); error_info->error = pghs.error(); return error_info; } diff --git a/toolsrc/src/vcpkg/platform-expression.cpp b/toolsrc/src/vcpkg/platform-expression.cpp new file mode 100644 index 000000000..35cfd4cbc --- /dev/null +++ b/toolsrc/src/vcpkg/platform-expression.cpp @@ -0,0 +1,471 @@ +#include "pch.h" + +#include <vcpkg/base/parse.h> +#include <vcpkg/base/strings.h> +#include <vcpkg/base/system.print.h> +#include <vcpkg/platform-expression.h> + +#include <string> +#include <variant> +#include <vector> + +namespace vcpkg::PlatformExpression +{ + using vcpkg::Parse::ParseError; + + enum class Identifier + { + invalid = -1, // not a recognized identifier + x86, + x64, + arm, + arm64, + wasm32, + + windows, + linux, + osx, + uwp, + android, + emscripten, + + static_link, + }; + + static Identifier string2identifier(StringView name) + { + static const std::map<StringView, Identifier> id_map = { + {"x86", Identifier::x86}, + {"x64", Identifier::x64}, + {"arm", Identifier::arm}, + {"arm64", Identifier::arm64}, + {"wasm32", Identifier::wasm32}, + {"windows", Identifier::windows}, + {"linux", Identifier::linux}, + {"osx", Identifier::osx}, + {"uwp", Identifier::uwp}, + {"android", Identifier::android}, + {"emscripten", Identifier::emscripten}, + {"static", Identifier::static_link}, + }; + + auto id_pair = id_map.find(name); + + if (id_pair == id_map.end()) + { + return Identifier::invalid; + } + + return id_pair->second; + } + + namespace detail + { + struct ExprIdentifier + { + std::string identifier; + }; + struct ExprNot + { + std::unique_ptr<ExprImpl> expr; + }; + struct ExprAnd + { + std::vector<ExprImpl> exprs; + }; + struct ExprOr + { + std::vector<ExprImpl> exprs; + }; + + struct ExprImpl + { + std::variant<ExprIdentifier, ExprNot, ExprAnd, ExprOr> underlying; + + explicit ExprImpl(ExprIdentifier e) : underlying(std::move(e)) { } + explicit ExprImpl(ExprNot e) : underlying(std::move(e)) { } + explicit ExprImpl(ExprAnd e) : underlying(std::move(e)) { } + explicit ExprImpl(ExprOr e) : underlying(std::move(e)) { } + + ExprImpl clone() const + { + struct Visitor + { + ExprImpl operator()(const ExprIdentifier& e) { return ExprImpl(e); } + ExprImpl operator()(const ExprNot& e) + { + return ExprImpl(ExprNot{std::make_unique<ExprImpl>(e.expr->clone())}); + } + ExprImpl operator()(const ExprAnd& e) + { + ExprAnd res; + for (const auto& expr : e.exprs) + { + res.exprs.push_back(expr.clone()); + } + return ExprImpl(std::move(res)); + } + ExprImpl operator()(const ExprOr& e) + { + ExprOr res; + for (const auto& expr : e.exprs) + { + res.exprs.push_back(expr.clone()); + } + return ExprImpl(std::move(res)); + } + }; + return std::visit(Visitor{}, underlying); + } + }; + + class ExpressionParser : public Parse::ParserBase + { + public: + ExpressionParser(StringView str, MultipleBinaryOperators multiple_binary_operators) : Parse::ParserBase(str, "CONTROL"), multiple_binary_operators(multiple_binary_operators) { } + + MultipleBinaryOperators multiple_binary_operators; + + bool allow_multiple_binary_operators() const + { + return multiple_binary_operators == MultipleBinaryOperators::Allow; + } + + PlatformExpression::Expr parse() + { + skip_whitespace(); + + auto res = expr(); + + if (!at_eof()) + { + add_error("invalid logic expression, unexpected character"); + } + + return Expr(std::make_unique<ExprImpl>(std::move(res))); + } + + private: + // <platform-expression.and> + // <platform-expression.not> + // <platform-expression.and> & <platform-expression.not> + // <platform-expression.or> + // <platform-expression.not> + // <platform-expression.or> | <platform-expression.not> + + static bool is_identifier_char(char32_t ch) + { + return is_lower_alpha(ch) || is_ascii_digit(ch); + } + + // <platform-expression>: + // <platform-expression.not> + // <platform-expression.and> + // <platform-expression.or> + ExprImpl expr() + { + auto result = expr_not(); + + switch (cur()) + { + case '|': + { + ExprOr e; + e.exprs.push_back(std::move(result)); + return expr_binary<'|', '&'>(std::move(e)); + } + case '&': + { + ExprAnd e; + e.exprs.push_back(std::move(result)); + return expr_binary<'&', '|'>(std::move(e)); + } + default: return result; + } + } + + // <platform-expression.simple>: + // ( <platform-expression> ) + // <platform-expression.identifier> + ExprImpl expr_simple() + { + if (cur() == '(') + { + next(); + skip_whitespace(); + auto result = expr(); + if (cur() != ')') + { + add_error("missing closing )"); + return result; + } + next(); + skip_whitespace(); + return result; + } + + return expr_identifier(); + } + + // <platform-expression.identifier>: + // A lowercase alpha-numeric string + ExprImpl expr_identifier() + { + std::string name = match_zero_or_more(is_identifier_char).to_string(); + + if (name.empty()) + { + add_error("unexpected character in logic expression"); + } + + skip_whitespace(); + return ExprImpl{ExprIdentifier{name}}; + } + + // <platform-expression.not>: + // <platform-expression.simple> + // ! <platform-expression.simple> + ExprImpl expr_not() + { + if (cur() == '!') + { + next(); + skip_whitespace(); + return ExprImpl(ExprNot{std::make_unique<ExprImpl>(expr_simple())}); + } + + return expr_simple(); + } + + template<char oper, char other, class ExprKind> + ExprImpl expr_binary(ExprKind&& seed) + { + do + { + // Support chains of the operator to avoid breaking backwards compatibility + do + { + next(); + } while (allow_multiple_binary_operators() && cur() == oper); + + skip_whitespace(); + seed.exprs.push_back(expr_not()); + } while (cur() == oper); + + if (cur() == other) + { + add_error("mixing & and | is not allowed; use () to specify order of operations"); + } + + skip_whitespace(); + return ExprImpl(std::move(seed)); + } + }; + } + + using namespace detail; + + Expr::Expr() = default; + Expr::Expr(Expr&& other) = default; + Expr& Expr::operator=(Expr&& other) = default; + + Expr::Expr(const Expr& other) + { + if (other.underlying_) + { + underlying_ = std::make_unique<ExprImpl>(other.underlying_->clone()); + } + } + Expr& Expr::operator=(const Expr& other) + { + if (other.underlying_) + { + if (this->underlying_) + { + *this->underlying_ = other.underlying_->clone(); + } + else + { + this->underlying_ = std::make_unique<ExprImpl>(other.underlying_->clone()); + } + } + else + { + this->underlying_.reset(); + } + + return *this; + } + + Expr::Expr(std::unique_ptr<ExprImpl>&& e) : underlying_(std::move(e)) { } + Expr::~Expr() = default; + + Expr Expr::Identifier(StringView id) + { + return Expr(std::make_unique<ExprImpl>(ExprImpl{ExprIdentifier{id.to_string()}})); + } + Expr Expr::Not(Expr&& e) { return Expr(std::make_unique<ExprImpl>(ExprImpl{ExprNot{std::move(e.underlying_)}})); } + Expr Expr::And(std::vector<Expr>&& exprs) + { + std::vector<ExprImpl> impls; + for (auto& e : exprs) + { + impls.push_back(std::move(*e.underlying_)); + } + return Expr(std::make_unique<ExprImpl>(ExprAnd{std::move(impls)})); + } + Expr Expr::Or(std::vector<Expr>&& exprs) + { + std::vector<ExprImpl> impls; + for (auto& e : exprs) + { + impls.push_back(std::move(*e.underlying_)); + } + return Expr(std::make_unique<ExprImpl>(ExprOr{std::move(impls)})); + } + + bool Expr::evaluate(const Context& context) const + { + if (!underlying_) + { + return true; // empty expression is always true + } + + std::map<std::string, bool> override_ctxt; + { + auto override_vars = context.find("VCPKG_DEP_INFO_OVERRIDE_VARS"); + if (override_vars != context.end()) + { + auto cmake_list = Strings::split(override_vars->second, ';'); + for (auto& override_id : cmake_list) + { + if (!override_id.empty()) + { + if (override_id[0] == '!') + { + override_ctxt.insert({override_id.substr(1), false}); + } + else + { + override_ctxt.insert({override_id, true}); + } + } + } + } + } + + struct Visitor + { + const Context& context; + const std::map<std::string, bool>& override_ctxt; + + bool true_if_exists_and_equal(const std::string& variable_name, const std::string& value) const + { + auto iter = context.find(variable_name); + if (iter == context.end()) + { + return false; + } + return iter->second == value; + } + + bool visit(const ExprImpl& e) const { return std::visit(*this, e.underlying); } + + bool operator()(const ExprIdentifier& expr) const + { + if (!override_ctxt.empty()) + { + auto override_id = override_ctxt.find(expr.identifier); + if (override_id != override_ctxt.end()) + { + return override_id->second; + } + // Fall through to use the cmake logic if the id does not have an override + } + + auto id = string2identifier(expr.identifier); + switch (id) + { + case Identifier::invalid: + // Point out in the diagnostic that they should add to the override list because that is what + // most users should do, however it is also valid to update the built in identifiers to + // recognize the name. + System::printf(System::Color::error, + "Error: Unrecognized identifer name %s. Add to override list in triplet file.\n", + expr.identifier); + return false; + case Identifier::x64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x64"); + case Identifier::x86: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x86"); + case Identifier::arm: + // For backwards compatability arm is also true for arm64. + // This is because it previously was only checking for a substring. + return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm") || + true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64"); + case Identifier::arm64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64"); + case Identifier::windows: + return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "") || + true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"); + case Identifier::linux: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Linux"); + case Identifier::osx: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Darwin"); + case Identifier::uwp: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"); + case Identifier::android: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Android"); + case Identifier::emscripten: + return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Emscripten"); + case Identifier::wasm32: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "wasm32"); + case Identifier::static_link: return true_if_exists_and_equal("VCPKG_LIBRARY_LINKAGE", "static"); + default: + Checks::exit_with_message( + VCPKG_LINE_INFO, + "vcpkg bug: string2identifier returned a value that we don't recognize: %d\n", + static_cast<int>(id)); + } + } + + bool operator()(const ExprNot& expr) const { + bool res = visit(*expr.expr); + return !res; + } + + bool operator()(const ExprAnd& expr) const + { + bool valid = true; + + // we want to print errors in all expressions, so we check all of the expressions all the time + for (const auto& e : expr.exprs) + { + valid &= visit(e); + } + + return valid; + } + + bool operator()(const ExprOr& expr) const + { + bool valid = false; + // we want to print errors in all expressions, so we check all of the expressions all the time + for (const auto& e : expr.exprs) + { + valid |= visit(e); + } + return valid; + } + }; + + return Visitor{context, override_ctxt}.visit(*underlying_); + } + + ExpectedS<Expr> parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators) + { + auto parser = ExpressionParser(expression, multiple_binary_operators); + auto res = parser.parse(); + + if (auto p = parser.extract_error()) + { + return p->format(); + } + else + { + return res; + } + } +} diff --git a/toolsrc/src/vcpkg/portfileprovider.cpp b/toolsrc/src/vcpkg/portfileprovider.cpp index 6500f70a6..f0533f08d 100644 --- a/toolsrc/src/vcpkg/portfileprovider.cpp +++ b/toolsrc/src/vcpkg/portfileprovider.cpp @@ -25,40 +25,35 @@ namespace vcpkg::PortFileProvider } PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths, - const std::vector<std::string>* ports_dirs_paths) + const std::vector<std::string>& ports_dirs_paths) : filesystem(paths.get_filesystem()) { auto& fs = paths.get_filesystem(); - if (ports_dirs_paths) + for (auto&& overlay_path : ports_dirs_paths) { - for (auto&& overlay_path : *ports_dirs_paths) + if (!overlay_path.empty()) { - if (!overlay_path.empty()) + auto overlay = fs::u8path(overlay_path); + if (overlay.is_absolute()) { - auto overlay = fs::u8path(overlay_path); - if (overlay.is_absolute()) - { - overlay = fs.canonical(VCPKG_LINE_INFO, overlay); - } - else - { - overlay = fs.canonical(VCPKG_LINE_INFO, paths.original_cwd / overlay); - } + overlay = fs.canonical(VCPKG_LINE_INFO, overlay); + } + else + { + overlay = fs.canonical(VCPKG_LINE_INFO, paths.original_cwd / overlay); + } - Debug::print("Using overlay: ", overlay.u8string(), "\n"); + Debug::print("Using overlay: ", overlay.u8string(), "\n"); - Checks::check_exit(VCPKG_LINE_INFO, - filesystem.exists(overlay), - "Error: Path \"%s\" does not exist", - overlay.string()); + Checks::check_exit( + VCPKG_LINE_INFO, filesystem.exists(overlay), "Error: Path \"%s\" does not exist", overlay.string()); - Checks::check_exit(VCPKG_LINE_INFO, - fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)), - "Error: Path \"%s\" must be a directory", - overlay.string()); + Checks::check_exit(VCPKG_LINE_INFO, + fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)), + "Error: Path \"%s\" must be a directory", + overlay.string()); - ports_dirs.emplace_back(overlay); - } + ports_dirs.emplace_back(overlay); } } ports_dirs.emplace_back(paths.ports); @@ -75,7 +70,7 @@ namespace vcpkg::PortFileProvider for (auto&& ports_dir : ports_dirs) { // Try loading individual port - if (filesystem.exists(ports_dir / "CONTROL")) + if (Paragraphs::is_port_directory(filesystem, ports_dir)) { auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir); if (auto scf = maybe_scf.get()) @@ -94,10 +89,14 @@ namespace vcpkg::PortFileProvider Checks::exit_with_message( VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, ports_dir.u8string()); } + + continue; } - else if (filesystem.exists(ports_dir / spec / "CONTROL")) + + auto ports_spec = ports_dir / spec; + if (Paragraphs::is_port_directory(filesystem, ports_spec)) { - auto found_scf = Paragraphs::try_load_port(filesystem, ports_dir / spec); + auto found_scf = Paragraphs::try_load_port(filesystem, ports_spec); if (auto scf = found_scf.get()) { if (scf->get()->core_paragraph->name == spec) @@ -133,7 +132,7 @@ namespace vcpkg::PortFileProvider for (auto&& ports_dir : ports_dirs) { // Try loading individual port - if (filesystem.exists(ports_dir / "CONTROL")) + if (Paragraphs::is_port_directory(filesystem, ports_dir)) { auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir); if (auto scf = maybe_scf.get()) diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index 9b4dc86e0..23f9d0097 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -215,6 +215,10 @@ namespace vcpkg::Remove void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) { + if (paths.manifest_mode_enabled()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "vcpkg remove does not support manifest mode. In order to remove dependencies, you will need to edit your manifest (vcpkg.json)."); + } const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); StatusParagraphs status_db = database_load_check(paths); @@ -228,7 +232,7 @@ namespace vcpkg::Remove } // Load ports from ports dirs - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); specs = Util::fmap(Update::find_outdated_packages(provider, status_db), [](auto&& outdated) { return outdated.spec; }); diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp index b5e0ebe31..74ef2f9e1 100644 --- a/toolsrc/src/vcpkg/sourceparagraph.cpp +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include <vcpkg/logicexpression.h> +#include <vcpkg/platform-expression.h> #include <vcpkg/packagespec.h> #include <vcpkg/sourceparagraph.h> #include <vcpkg/triplet.h> @@ -8,6 +8,7 @@ #include <vcpkg/base/checks.h> #include <vcpkg/base/expected.h> #include <vcpkg/base/span.h> +#include <vcpkg/base/system.debug.h> #include <vcpkg/base/system.print.h> #include <vcpkg/base/util.h> @@ -18,28 +19,71 @@ namespace vcpkg namespace SourceParagraphFields { static const std::string BUILD_DEPENDS = "Build-Depends"; - static const std::string DEFAULTFEATURES = "Default-Features"; + static const std::string DEFAULT_FEATURES = "Default-Features"; static const std::string DESCRIPTION = "Description"; static const std::string FEATURE = "Feature"; - static const std::string MAINTAINER = "Maintainer"; - static const std::string SOURCE = "Source"; + static const std::string MAINTAINERS = "Maintainer"; + static const std::string NAME = "Source"; static const std::string VERSION = "Version"; + static const std::string PORT_VERSION = "Port-Version"; static const std::string HOMEPAGE = "Homepage"; static const std::string TYPE = "Type"; static const std::string SUPPORTS = "Supports"; } - static Span<const std::string> get_list_of_valid_fields() + namespace ManifestFields { - static const std::string valid_fields[] = { - SourceParagraphFields::SOURCE, + constexpr static StringLiteral NAME = "name"; + constexpr static StringLiteral VERSION = "version-string"; + + constexpr static StringLiteral PORT_VERSION = "port-version"; + constexpr static StringLiteral MAINTAINERS = "maintainers"; + constexpr static StringLiteral DESCRIPTION = "description"; + constexpr static StringLiteral HOMEPAGE = "homepage"; + constexpr static StringLiteral DOCUMENTATION = "documentation"; + constexpr static StringLiteral LICENSE = "license"; + constexpr static StringLiteral DEPENDENCIES = "dependencies"; + constexpr static StringLiteral DEV_DEPENDENCIES = "dev-dependencies"; + constexpr static StringLiteral FEATURES = "features"; + constexpr static StringLiteral DEFAULT_FEATURES = "default-features"; + constexpr static StringLiteral SUPPORTS = "supports"; + } + + static Span<const StringView> get_list_of_valid_fields() + { + static const StringView valid_fields[] = { + SourceParagraphFields::NAME, SourceParagraphFields::VERSION, + SourceParagraphFields::PORT_VERSION, SourceParagraphFields::DESCRIPTION, - SourceParagraphFields::MAINTAINER, + SourceParagraphFields::MAINTAINERS, SourceParagraphFields::BUILD_DEPENDS, SourceParagraphFields::HOMEPAGE, SourceParagraphFields::TYPE, SourceParagraphFields::SUPPORTS, + SourceParagraphFields::DEFAULT_FEATURES, + }; + + return valid_fields; + } + + static Span<const StringView> get_list_of_manifest_fields() + { + constexpr static StringView valid_fields[] = { + ManifestFields::NAME, + ManifestFields::VERSION, + + ManifestFields::PORT_VERSION, + ManifestFields::MAINTAINERS, + ManifestFields::DESCRIPTION, + ManifestFields::HOMEPAGE, + ManifestFields::DOCUMENTATION, + ManifestFields::LICENSE, + ManifestFields::DEPENDENCIES, + ManifestFields::DEV_DEPENDENCIES, + ManifestFields::FEATURES, + ManifestFields::DEFAULT_FEATURES, + ManifestFields::SUPPORTS, }; return valid_fields; @@ -65,22 +109,29 @@ namespace vcpkg if (!error_info->extra_fields.empty()) { System::print2(System::Color::error, - "Error: There are invalid fields in the control file of ", + "Error: There are invalid fields in the control or manifest file of ", error_info->name, '\n'); - System::print2("The following fields were not expected:\n\n ", - Strings::join("\n ", error_info->extra_fields), - "\n\n"); + System::print2("The following fields were not expected:\n"); + + for (const auto& pr : error_info->extra_fields) + { + System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n"); + } have_remaining_fields = true; } } if (have_remaining_fields) { - System::print2("This is the list of valid fields (case-sensitive): \n\n ", + System::print2("This is the list of valid fields for CONTROL files (case-sensitive): \n\n ", Strings::join("\n ", get_list_of_valid_fields()), "\n\n"); - System::print2("Different source may be available for vcpkg. Use .\\bootstrap-vcpkg.bat to update.\n\n"); + System::print2("And this is the list of valid fields for manifest files: \n\n ", + Strings::join("\n ", get_list_of_manifest_fields()), + "\n\n"); + System::print2("You may need to update the vcpkg binary; try running bootstrap-vcpkg.bat or " + "bootstrap-vcpkg.sh to update.\n\n"); } for (auto&& error_info : error_info_list) @@ -91,9 +142,29 @@ namespace vcpkg "Error: There are missing fields in the control file of ", error_info->name, '\n'); - System::print2("The following fields were missing:\n\n ", - Strings::join("\n ", error_info->missing_fields), - "\n\n"); + System::print2("The following fields were missing:\n"); + for (const auto& pr : error_info->missing_fields) + { + System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n"); + } + } + } + + for (auto&& error_info : error_info_list) + { + if (!error_info->expected_types.empty()) + { + System::print2(System::Color::error, + "Error: There are invalid field types in the CONTROL or manifest file of ", + error_info->name, + '\n'); + System::print2("The following fields had the wrong types:\n\n"); + + for (const auto& pr : error_info->expected_types) + { + System::printf(" %s was expected to be %s\n", pr.first, pr.second); + } + System::print2("\n"); } } } @@ -111,10 +182,27 @@ namespace vcpkg Type Type::from_string(const std::string& t) { if (t == "Alias") return Type{Type::ALIAS}; - if (t == "Port" || t == "") return Type{Type::PORT}; + if (t == "Port" || t.empty()) return Type{Type::PORT}; return Type{Type::UNKNOWN}; } + bool operator==(const Type& lhs, const Type& rhs) + { + return lhs.type == rhs.type; + } + bool operator!=(const Type& lhs, const Type& rhs) + { + return !(lhs == rhs); + } + + static void trim_all(std::vector<std::string>& arr) + { + for (auto& el : arr) + { + el = Strings::trim(std::move(el)); + } + } + static ParseExpected<SourceParagraph> parse_source_paragraph(const fs::path& path_to_control, Paragraph&& fields) { auto origin = path_to_control.u8string(); @@ -123,20 +211,52 @@ namespace vcpkg auto spgh = std::make_unique<SourceParagraph>(); - parser.required_field(SourceParagraphFields::SOURCE, spgh->name); + parser.required_field(SourceParagraphFields::NAME, spgh->name); parser.required_field(SourceParagraphFields::VERSION, spgh->version); - spgh->description = parser.optional_field(SourceParagraphFields::DESCRIPTION); - spgh->maintainer = parser.optional_field(SourceParagraphFields::MAINTAINER); + auto pv_str = parser.optional_field(SourceParagraphFields::PORT_VERSION); + if (!pv_str.empty()) + { + auto pv_opt = Strings::strto<int>(pv_str); + if (auto pv = pv_opt.get()) + { + spgh->port_version = *pv; + } + else + { + parser.add_type_error(SourceParagraphFields::PORT_VERSION, "a non-negative integer"); + } + } + + spgh->description = Strings::split(parser.optional_field(SourceParagraphFields::DESCRIPTION), '\n'); + trim_all(spgh->description); + + spgh->maintainers = Strings::split(parser.optional_field(SourceParagraphFields::MAINTAINERS), '\n'); + trim_all(spgh->maintainers); + spgh->homepage = parser.optional_field(SourceParagraphFields::HOMEPAGE); TextRowCol textrowcol; std::string buf; parser.optional_field(SourceParagraphFields::BUILD_DEPENDS, {buf, textrowcol}); - spgh->depends = parse_dependencies_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO); + spgh->dependencies = parse_dependencies_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO); buf.clear(); - parser.optional_field(SourceParagraphFields::DEFAULTFEATURES, {buf, textrowcol}); + parser.optional_field(SourceParagraphFields::DEFAULT_FEATURES, {buf, textrowcol}); spgh->default_features = parse_default_features_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO); - spgh->supports_expression = parser.optional_field(SourceParagraphFields::SUPPORTS); + + auto supports_expr = parser.optional_field(SourceParagraphFields::SUPPORTS); + if (!supports_expr.empty()) + { + auto maybe_expr = PlatformExpression::parse_platform_expression(supports_expr, PlatformExpression::MultipleBinaryOperators::Allow); + if (auto expr = maybe_expr.get()) + { + spgh->supports_expression = std::move(*expr); + } + else + { + parser.add_type_error(SourceParagraphFields::SUPPORTS, "a platform expression"); + } + } + spgh->type = Type::from_string(parser.optional_field(SourceParagraphFields::TYPE)); auto err = parser.error_info(spgh->name.empty() ? origin : spgh->name); if (err) @@ -153,10 +273,12 @@ namespace vcpkg auto fpgh = std::make_unique<FeatureParagraph>(); parser.required_field(SourceParagraphFields::FEATURE, fpgh->name); - parser.required_field(SourceParagraphFields::DESCRIPTION, fpgh->description); + fpgh->description = Strings::split(parser.required_field(SourceParagraphFields::DESCRIPTION), '\n'); + trim_all(fpgh->description); - fpgh->depends = parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS), origin) - .value_or_exit(VCPKG_LINE_INFO); + fpgh->dependencies = + parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS), origin) + .value_or_exit(VCPKG_LINE_INFO); auto err = parser.error_info(fpgh->name.empty() ? origin : fpgh->name); if (err) @@ -197,6 +319,497 @@ namespace vcpkg return control_file; } + static std::vector<std::string> invalid_json_fields(const Json::Object& obj, + Span<const StringView> known_fields) noexcept + { + const auto field_is_unknown = [known_fields](StringView sv) { + // allow directives + if (sv.size() != 0 && *sv.begin() == '$') + { + return false; + } + return std::find(known_fields.begin(), known_fields.end(), sv) == known_fields.end(); + }; + + std::vector<std::string> res; + for (const auto& kv : obj) + { + if (field_is_unknown(kv.first)) + { + res.push_back(kv.first.to_string()); + } + } + + return res; + } + + struct StringField : Json::VisitorCrtpBase<StringField> + { + using type = std::string; + StringView type_name() { return type_name_; } + + Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) { return sv.to_string(); } + + explicit StringField(StringView type_name_) : type_name_(type_name_) { } + + private: + StringView type_name_; + }; + + struct BooleanField : Json::VisitorCrtpBase<BooleanField> + { + using type = bool; + StringView type_name() { return "a boolean"; } + + Optional<bool> visit_bool(Json::Reader&, StringView, bool b) { return b; } + }; + + enum class AllowEmpty : bool + { + No, + Yes, + }; + + template<class T> + struct ArrayField : Json::VisitorCrtpBase<ArrayField<T>> + { + using type = std::vector<typename T::type>; + + StringView type_name() { return type_name_; } + + ArrayField(StringView type_name_, AllowEmpty allow_empty, T&& t = {}) : type_name_(type_name_), underlying_visitor_(static_cast<T&&>(t)), allow_empty_(allow_empty) { } + + Optional<type> visit_array(Json::Reader& r, StringView key, const Json::Array& arr) + { + if (allow_empty_ == AllowEmpty::No && arr.size() == 0) + { + return nullopt; + } + return r.array_elements(arr, key, underlying_visitor_); + } + + private: + StringView type_name_; + T underlying_visitor_; + AllowEmpty allow_empty_; + }; + + struct ParagraphField : Json::VisitorCrtpBase<ParagraphField> + { + using type = std::vector<std::string>; + StringView type_name() { return "a string or array of strings"; } + + Optional<std::vector<std::string>> visit_string(Json::Reader&, StringView, StringView sv) + { + std::vector<std::string> out; + out.push_back(sv.to_string()); + return out; + } + + Optional<std::vector<std::string>> visit_array(Json::Reader& r, StringView key, const Json::Array& arr) + { + return r.array_elements(arr, key, StringField{"a string"}); + } + }; + + struct IdentifierField : Json::VisitorCrtpBase<IdentifierField> + { + using type = std::string; + StringView type_name() { return "an identifier"; } + + // [a-z0-9]+(-[a-z0-9]+)*, plus not any of {prn, aux, nul, con, lpt[1-9], com[1-9], core, default} + static bool is_ident(StringView sv) + { + static const std::regex BASIC_IDENTIFIER = std::regex(R"([a-z0-9]+(-[a-z0-9]+)*)"); + + // we only check for lowercase in RESERVED since we already remove all + // strings with uppercase letters from the basic check + static const std::regex RESERVED = std::regex(R"(prn|aux|nul|con|(lpt|com)[1-9]|core|default)"); + + if (!std::regex_match(sv.begin(), sv.end(), BASIC_IDENTIFIER)) + { + return false; // we're not even in the shape of an identifier + } + + if (std::regex_match(sv.begin(), sv.end(), RESERVED)) + { + return false; // we're a reserved identifier + } + + return true; + } + + Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) + { + if (is_ident(sv)) + { + return sv.to_string(); + } + else + { + return nullopt; + } + } + }; + + struct PackageNameField : Json::VisitorCrtpBase<PackageNameField> + { + using type = std::string; + StringView type_name() { return "a package name"; } + + static bool is_package_name(StringView sv) + { + if (sv.size() == 0) + { + return false; + } + + for (const auto& ident : Strings::split(sv, '.')) + { + if (!IdentifierField::is_ident(ident)) + { + return false; + } + } + + return true; + } + + Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) + { + if (!is_package_name(sv)) + { + return nullopt; + } + return sv.to_string(); + } + }; + + // We "parse" this so that we can add actual license parsing at some point in the future + // without breaking anyone + struct LicenseExpressionField : Json::VisitorCrtpBase<LicenseExpressionField> + { + using type = std::string; + StringView type_name() { return "an SPDX license expression"; } + + enum class Mode + { + ExpectExpression, + ExpectContinue, + ExpectException, + }; + + constexpr static StringView EXPRESSION_WORDS[] = { + "WITH", + "AND", + "OR", + }; + constexpr static StringView VALID_LICENSES[] = +#include "spdx-licenses.inc" + ; + + constexpr static StringView VALID_EXCEPTIONS[] = +#include "spdx-exceptions.inc" + ; + + Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) + { + Mode mode = Mode::ExpectExpression; + size_t open_parens = 0; + std::string current_word; + + const auto check_current_word = [¤t_word, &mode] { + if (current_word.empty()) + { + return true; + } + + Span<const StringView> valid_ids; + bool case_sensitive = false; + switch (mode) + { + case Mode::ExpectExpression: + valid_ids = VALID_LICENSES; + mode = Mode::ExpectContinue; + // a single + is allowed on the end of licenses + if (current_word.back() == '+') + { + current_word.pop_back(); + } + break; + case Mode::ExpectContinue: + valid_ids = EXPRESSION_WORDS; + mode = Mode::ExpectExpression; + case_sensitive = true; + break; + case Mode::ExpectException: + valid_ids = VALID_EXCEPTIONS; + mode = Mode::ExpectContinue; + break; + } + + const auto equal = [&](StringView sv) { + if (case_sensitive) + { + return sv == current_word; + } + else + { + return Strings::case_insensitive_ascii_equals(sv, current_word); + } + }; + + if (std::find_if(valid_ids.begin(), valid_ids.end(), equal) == valid_ids.end()) + { + return false; + } + + if (current_word == "WITH") + { + mode = Mode::ExpectException; + } + + current_word.clear(); + return true; + }; + + for (const auto& ch : sv) + { + if (ch == ' ' || ch == '\t') + { + if (!check_current_word()) + { + return nullopt; + } + } + else if (ch == '(') + { + if (!check_current_word()) + { + return nullopt; + } + if (mode != Mode::ExpectExpression) + { + return nullopt; + } + ++open_parens; + } + else if (ch == ')') + { + if (!check_current_word()) + { + return nullopt; + } + if (mode != Mode::ExpectContinue) + { + return nullopt; + } + if (open_parens == 0) + { + return nullopt; + } + --open_parens; + } + else + { + current_word.push_back(ch); + } + } + + if (!check_current_word()) + { + return nullopt; + } + else + { + return sv.to_string(); + } + } + }; + + struct PlatformExprField : Json::VisitorCrtpBase<PlatformExprField> + { + using type = PlatformExpression::Expr; + StringView type_name() { return "a platform expression"; } + + Optional<PlatformExpression::Expr> visit_string(Json::Reader&, StringView, StringView sv) + { + auto opt = PlatformExpression::parse_platform_expression(sv, PlatformExpression::MultipleBinaryOperators::Deny); + if (auto res = opt.get()) + { + return std::move(*res); + } + else + { + Debug::print("Failed to parse platform expression: ", opt.error(), "\n"); + return nullopt; + } + } + }; + + struct DependencyField : Json::VisitorCrtpBase<DependencyField> + { + using type = Dependency; + StringView type_name() { return "a dependency"; } + + constexpr static StringView NAME = "name"; + constexpr static StringView FEATURES = "features"; + constexpr static StringView DEFAULT_FEATURES = "default-features"; + constexpr static StringView PLATFORM = "platform"; + constexpr static StringView KNOWN_FIELDS[] = {NAME, FEATURES, DEFAULT_FEATURES, PLATFORM}; + + Optional<Dependency> visit_string(Json::Reader&, StringView, StringView sv) + { + if (!PackageNameField::is_package_name(sv)) + { + return nullopt; + } + + Dependency dep; + dep.name = sv.to_string(); + return dep; + } + + Optional<Dependency> visit_object(Json::Reader& r, StringView, const Json::Object& obj) + { + { + auto extra_fields = invalid_json_fields(obj, KNOWN_FIELDS); + if (!extra_fields.empty()) + { + r.error().add_extra_fields(type_name().to_string(), std::move(extra_fields)); + } + } + + Dependency dep; + r.required_object_field(type_name(), obj, NAME, dep.name, PackageNameField{}); + r.optional_object_field( + obj, FEATURES, dep.features, ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes}); + + bool default_features = true; + r.optional_object_field(obj, DEFAULT_FEATURES, default_features, BooleanField{}); + if (!default_features) + { + dep.features.push_back("core"); + } + + r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprField{}); + + return dep; + } + }; + + struct FeatureField : Json::VisitorCrtpBase<FeatureField> + { + using type = std::unique_ptr<FeatureParagraph>; + StringView type_name() { return "a feature"; } + + constexpr static StringView NAME = "name"; + constexpr static StringView DESCRIPTION = "description"; + constexpr static StringView DEPENDENCIES = "dependencies"; + + Optional<std::unique_ptr<FeatureParagraph>> visit_object(Json::Reader& r, StringView, const Json::Object& obj) + { + auto feature = std::make_unique<FeatureParagraph>(); + + r.required_object_field(type_name(), obj, NAME, feature->name, IdentifierField{}); + r.required_object_field(type_name(), obj, DESCRIPTION, feature->description, ParagraphField{}); + r.optional_object_field(obj, + DEPENDENCIES, + feature->dependencies, + ArrayField<DependencyField>{"an array of dependencies", AllowEmpty::Yes}); + + return std::move(feature); + } + }; + + Parse::ParseExpected<SourceControlFile> SourceControlFile::parse_manifest_file(const fs::path& path_to_manifest, + const Json::Object& manifest) + { + struct JsonErr final : Json::ReaderError + { + ParseControlErrorInfo pcei; + + void add_missing_field(std::string&& type, std::string&& key) override + { + pcei.missing_fields[std::move(type)].push_back(std::move(key)); + } + void add_expected_type(std::string&& key, std::string&& expected_type) override + { + pcei.expected_types.emplace(std::move(key), std::move(expected_type)); + } + void add_extra_fields(std::string&& type, std::vector<std::string>&& fields) override + { + if (!fields.empty()) + { + auto& fields_for_type = pcei.extra_fields[std::move(type)]; + fields_for_type.insert(fields_for_type.end(), fields.begin(), fields.end()); + } + } + void add_mutually_exclusive_fields(std::string&& type, std::vector<std::string>&& fields) override + { + if (!fields.empty()) + { + auto& fields_for_type = pcei.mutually_exclusive_fields[std::move(type)]; + fields_for_type.insert(fields_for_type.end(), fields.begin(), fields.end()); + } + } + } err = {}; + auto visit = Json::Reader{&err}; + + err.pcei.name = path_to_manifest.u8string(); + { + auto extra_fields = invalid_json_fields(manifest, get_list_of_manifest_fields()); + if (!extra_fields.empty()) + { + err.pcei.extra_fields["manifest"] = std::move(extra_fields); + } + } + + auto control_file = std::make_unique<SourceControlFile>(); + control_file->core_paragraph = std::make_unique<SourceParagraph>(); + + auto& spgh = control_file->core_paragraph; + + constexpr static StringView type_name = "vcpkg.json"; + visit.required_object_field(type_name, manifest, ManifestFields::NAME, spgh->name, IdentifierField{}); + visit.required_object_field( + type_name, manifest, ManifestFields::VERSION, spgh->version, StringField{"a version"}); + visit.optional_object_field(manifest, ManifestFields::MAINTAINERS, spgh->maintainers, ParagraphField{}); + visit.optional_object_field(manifest, ManifestFields::DESCRIPTION, spgh->description, ParagraphField{}); + visit.optional_object_field(manifest, ManifestFields::HOMEPAGE, spgh->homepage, StringField{"a url"}); + visit.optional_object_field(manifest, ManifestFields::DOCUMENTATION, spgh->documentation, StringField{"a url"}); + visit.optional_object_field(manifest, ManifestFields::LICENSE, spgh->license, LicenseExpressionField{}); + visit.optional_object_field(manifest, + ManifestFields::DEPENDENCIES, + spgh->dependencies, + ArrayField<DependencyField>{"an array of dependencies", AllowEmpty::Yes}); + + if (manifest.contains(ManifestFields::DEV_DEPENDENCIES)) + { + System::print2(System::Color::error, "dev_dependencies are not yet supported"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + visit.optional_object_field(manifest, ManifestFields::SUPPORTS, spgh->supports_expression, PlatformExprField{}); + + visit.optional_object_field(manifest, + ManifestFields::DEFAULT_FEATURES, + spgh->default_features, + ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes}); + + visit.optional_object_field(manifest, + ManifestFields::FEATURES, + control_file->feature_paragraphs, + ArrayField<FeatureField>{"an array of feature definitions", AllowEmpty::Yes}); + + if (err.pcei.has_error()) + { + return std::make_unique<ParseControlErrorInfo>(std::move(err.pcei)); + } + + return std::move(control_file); + } + Optional<const FeatureParagraph&> SourceControlFile::find_feature(const std::string& featurename) const { auto it = Util::find_if(feature_paragraphs, @@ -211,10 +824,10 @@ namespace vcpkg { if (featurename == "core") { - return core_paragraph->depends; + return core_paragraph->dependencies; } else if (auto p_feature = find_feature(featurename).get()) - return p_feature->depends; + return p_feature->dependencies; else return nullopt; } @@ -226,11 +839,9 @@ namespace vcpkg std::vector<FullPackageSpec> ret; for (auto&& dep : deps) { - const auto& qualifier = dep.qualifier; - if (qualifier.empty() || - evaluate_expression(qualifier, {cmake_vars, t.canonical_name()}).value_or_exit(VCPKG_LINE_INFO)) + if (dep.platform.evaluate(cmake_vars)) { - ret.emplace_back(FullPackageSpec({dep.depend.name, t}, dep.depend.features)); + ret.emplace_back(FullPackageSpec({dep.name, t}, dep.features)); } } return ret; diff --git a/toolsrc/src/vcpkg/spdx-exceptions.inc b/toolsrc/src/vcpkg/spdx-exceptions.inc new file mode 100644 index 000000000..c6ef04b70 --- /dev/null +++ b/toolsrc/src/vcpkg/spdx-exceptions.inc @@ -0,0 +1,45 @@ +// Data downloaded from https://raw.githubusercontent.com/spdx/license-list-data/a3cab5c04eaf399ea8ee07ac69c749a9ad6a3f17/json/exceptions.json +// Generated by scripts/Generate-SpdxLicenseList.ps1 +{ + "GCC-exception-2.0", + "openvpn-openssl-exception", + "Nokia-Qt-exception-1.1", + "GPL-3.0-linking-exception", + "Fawkes-Runtime-exception", + "u-boot-exception-2.0", + "PS-or-PDF-font-exception-20170817", + "gnu-javamail-exception", + "LGPL-3.0-linking-exception", + "DigiRule-FOSS-exception", + "LLVM-exception", + "Linux-syscall-note", + "GPL-3.0-linking-source-exception", + "Qwt-exception-1.0", + "389-exception", + "mif-exception", + "eCos-exception-2.0", + "CLISP-exception-2.0", + "Bison-exception-2.2", + "Libtool-exception", + "LZMA-exception", + "OpenJDK-assembly-exception-1.0", + "Font-exception-2.0", + "OCaml-LGPL-linking-exception", + "GCC-exception-3.1", + "Bootloader-exception", + "SHL-2.0", + "Classpath-exception-2.0", + "Swift-exception", + "Autoconf-exception-2.0", + "FLTK-exception", + "freertos-exception-2.0", + "Universal-FOSS-exception-1.0", + "WxWindows-exception-3.1", + "OCCT-exception-1.0", + "Autoconf-exception-3.0", + "i2p-gpl-java-exception", + "GPL-CC-1.0", + "Qt-LGPL-exception-1.1", + "SHL-2.1", + "Qt-GPL-exception-1.0", +}
diff --git a/toolsrc/src/vcpkg/spdx-licenses.inc b/toolsrc/src/vcpkg/spdx-licenses.inc new file mode 100644 index 000000000..ad3523934 --- /dev/null +++ b/toolsrc/src/vcpkg/spdx-licenses.inc @@ -0,0 +1,426 @@ +// Data downloaded from https://raw.githubusercontent.com/spdx/license-list-data/a3cab5c04eaf399ea8ee07ac69c749a9ad6a3f17/json/licenses.json +// Generated by scripts/Generate-SpdxLicenseList.ps1 +{ + "0BSD", + "AAL", + "ADSL", + "AFL-1.1", + "AFL-1.2", + "AFL-2.0", + "AFL-2.1", + "AFL-3.0", + "AGPL-1.0", + "AGPL-1.0-only", + "AGPL-1.0-or-later", + "AGPL-3.0", + "AGPL-3.0-only", + "AGPL-3.0-or-later", + "AMDPLPA", + "AML", + "AMPAS", + "ANTLR-PD", + "APAFML", + "APL-1.0", + "APSL-1.0", + "APSL-1.1", + "APSL-1.2", + "APSL-2.0", + "Abstyles", + "Adobe-2006", + "Adobe-Glyph", + "Afmparse", + "Aladdin", + "Apache-1.0", + "Apache-1.1", + "Apache-2.0", + "Artistic-1.0", + "Artistic-1.0-Perl", + "Artistic-1.0-cl8", + "Artistic-2.0", + "BSD-1-Clause", + "BSD-2-Clause", + "BSD-2-Clause-FreeBSD", + "BSD-2-Clause-NetBSD", + "BSD-2-Clause-Patent", + "BSD-3-Clause", + "BSD-3-Clause-Attribution", + "BSD-3-Clause-Clear", + "BSD-3-Clause-LBNL", + "BSD-3-Clause-No-Nuclear-License", + "BSD-3-Clause-No-Nuclear-License-2014", + "BSD-3-Clause-No-Nuclear-Warranty", + "BSD-3-Clause-Open-MPI", + "BSD-4-Clause", + "BSD-4-Clause-UC", + "BSD-Protection", + "BSD-Source-Code", + "BSL-1.0", + "Bahyph", + "Barr", + "Beerware", + "BitTorrent-1.0", + "BitTorrent-1.1", + "BlueOak-1.0.0", + "Borceux", + "CAL-1.0", + "CAL-1.0-Combined-Work-Exception", + "CATOSL-1.1", + "CC-BY-1.0", + "CC-BY-2.0", + "CC-BY-2.5", + "CC-BY-3.0", + "CC-BY-4.0", + "CC-BY-NC-1.0", + "CC-BY-NC-2.0", + "CC-BY-NC-2.5", + "CC-BY-NC-3.0", + "CC-BY-NC-4.0", + "CC-BY-NC-ND-1.0", + "CC-BY-NC-ND-2.0", + "CC-BY-NC-ND-2.5", + "CC-BY-NC-ND-3.0", + "CC-BY-NC-ND-4.0", + "CC-BY-NC-SA-1.0", + "CC-BY-NC-SA-2.0", + "CC-BY-NC-SA-2.5", + "CC-BY-NC-SA-3.0", + "CC-BY-NC-SA-4.0", + "CC-BY-ND-1.0", + "CC-BY-ND-2.0", + "CC-BY-ND-2.5", + "CC-BY-ND-3.0", + "CC-BY-ND-4.0", + "CC-BY-SA-1.0", + "CC-BY-SA-2.0", + "CC-BY-SA-2.5", + "CC-BY-SA-3.0", + "CC-BY-SA-4.0", + "CC-PDDC", + "CC0-1.0", + "CDDL-1.0", + "CDDL-1.1", + "CDLA-Permissive-1.0", + "CDLA-Sharing-1.0", + "CECILL-1.0", + "CECILL-1.1", + "CECILL-2.0", + "CECILL-2.1", + "CECILL-B", + "CECILL-C", + "CERN-OHL-1.1", + "CERN-OHL-1.2", + "CERN-OHL-P-2.0", + "CERN-OHL-S-2.0", + "CERN-OHL-W-2.0", + "CNRI-Jython", + "CNRI-Python", + "CNRI-Python-GPL-Compatible", + "CPAL-1.0", + "CPL-1.0", + "CPOL-1.02", + "CUA-OPL-1.0", + "Caldera", + "ClArtistic", + "Condor-1.1", + "Crossword", + "CrystalStacker", + "Cube", + "D-FSL-1.0", + "DOC", + "DSDP", + "Dotseqn", + "ECL-1.0", + "ECL-2.0", + "EFL-1.0", + "EFL-2.0", + "EPL-1.0", + "EPL-2.0", + "EUDatagrid", + "EUPL-1.0", + "EUPL-1.1", + "EUPL-1.2", + "Entessa", + "ErlPL-1.1", + "Eurosym", + "FSFAP", + "FSFUL", + "FSFULLR", + "FTL", + "Fair", + "Frameworx-1.0", + "FreeImage", + "GFDL-1.1", + "GFDL-1.1-only", + "GFDL-1.1-or-later", + "GFDL-1.2", + "GFDL-1.2-only", + "GFDL-1.2-or-later", + "GFDL-1.3", + "GFDL-1.3-only", + "GFDL-1.3-or-later", + "GL2PS", + "GPL-1.0", + "GPL-1.0+", + "GPL-1.0-only", + "GPL-1.0-or-later", + "GPL-2.0", + "GPL-2.0+", + "GPL-2.0-only", + "GPL-2.0-or-later", + "GPL-2.0-with-GCC-exception", + "GPL-2.0-with-autoconf-exception", + "GPL-2.0-with-bison-exception", + "GPL-2.0-with-classpath-exception", + "GPL-2.0-with-font-exception", + "GPL-3.0", + "GPL-3.0+", + "GPL-3.0-only", + "GPL-3.0-or-later", + "GPL-3.0-with-GCC-exception", + "GPL-3.0-with-autoconf-exception", + "Giftware", + "Glide", + "Glulxe", + "HPND", + "HPND-sell-variant", + "HaskellReport", + "Hippocratic-2.1", + "IBM-pibs", + "ICU", + "IJG", + "IPA", + "IPL-1.0", + "ISC", + "ImageMagick", + "Imlib2", + "Info-ZIP", + "Intel", + "Intel-ACPI", + "Interbase-1.0", + "JPNIC", + "JSON", + "JasPer-2.0", + "LAL-1.2", + "LAL-1.3", + "LGPL-2.0", + "LGPL-2.0+", + "LGPL-2.0-only", + "LGPL-2.0-or-later", + "LGPL-2.1", + "LGPL-2.1+", + "LGPL-2.1-only", + "LGPL-2.1-or-later", + "LGPL-3.0", + "LGPL-3.0+", + "LGPL-3.0-only", + "LGPL-3.0-or-later", + "LGPLLR", + "LPL-1.0", + "LPL-1.02", + "LPPL-1.0", + "LPPL-1.1", + "LPPL-1.2", + "LPPL-1.3a", + "LPPL-1.3c", + "Latex2e", + "Leptonica", + "LiLiQ-P-1.1", + "LiLiQ-R-1.1", + "LiLiQ-Rplus-1.1", + "Libpng", + "Linux-OpenIB", + "MIT", + "MIT-0", + "MIT-CMU", + "MIT-advertising", + "MIT-enna", + "MIT-feh", + "MITNFA", + "MPL-1.0", + "MPL-1.1", + "MPL-2.0", + "MPL-2.0-no-copyleft-exception", + "MS-PL", + "MS-RL", + "MTLL", + "MakeIndex", + "MirOS", + "Motosoto", + "MulanPSL-1.0", + "MulanPSL-2.0", + "Multics", + "Mup", + "NASA-1.3", + "NBPL-1.0", + "NCGL-UK-2.0", + "NCSA", + "NGPL", + "NLOD-1.0", + "NLPL", + "NOSL", + "NPL-1.0", + "NPL-1.1", + "NPOSL-3.0", + "NRL", + "NTP", + "NTP-0", + "Naumen", + "Net-SNMP", + "NetCDF", + "Newsletr", + "Nokia", + "Noweb", + "Nunit", + "O-UDA-1.0", + "OCCT-PL", + "OCLC-2.0", + "ODC-By-1.0", + "ODbL-1.0", + "OFL-1.0", + "OFL-1.0-RFN", + "OFL-1.0-no-RFN", + "OFL-1.1", + "OFL-1.1-RFN", + "OFL-1.1-no-RFN", + "OGC-1.0", + "OGL-Canada-2.0", + "OGL-UK-1.0", + "OGL-UK-2.0", + "OGL-UK-3.0", + "OGTSL", + "OLDAP-1.1", + "OLDAP-1.2", + "OLDAP-1.3", + "OLDAP-1.4", + "OLDAP-2.0", + "OLDAP-2.0.1", + "OLDAP-2.1", + "OLDAP-2.2", + "OLDAP-2.2.1", + "OLDAP-2.2.2", + "OLDAP-2.3", + "OLDAP-2.4", + "OLDAP-2.5", + "OLDAP-2.6", + "OLDAP-2.7", + "OLDAP-2.8", + "OML", + "OPL-1.0", + "OSET-PL-2.1", + "OSL-1.0", + "OSL-1.1", + "OSL-2.0", + "OSL-2.1", + "OSL-3.0", + "OpenSSL", + "PDDL-1.0", + "PHP-3.0", + "PHP-3.01", + "PSF-2.0", + "Parity-6.0.0", + "Parity-7.0.0", + "Plexus", + "PolyForm-Noncommercial-1.0.0", + "PolyForm-Small-Business-1.0.0", + "PostgreSQL", + "Python-2.0", + "QPL-1.0", + "Qhull", + "RHeCos-1.1", + "RPL-1.1", + "RPL-1.5", + "RPSL-1.0", + "RSA-MD", + "RSCPL", + "Rdisc", + "Ruby", + "SAX-PD", + "SCEA", + "SGI-B-1.0", + "SGI-B-1.1", + "SGI-B-2.0", + "SHL-0.5", + "SHL-0.51", + "SISSL", + "SISSL-1.2", + "SMLNJ", + "SMPPL", + "SNIA", + "SPL-1.0", + "SSH-OpenSSH", + "SSH-short", + "SSPL-1.0", + "SWL", + "Saxpath", + "Sendmail", + "Sendmail-8.23", + "SimPL-2.0", + "Sleepycat", + "Spencer-86", + "Spencer-94", + "Spencer-99", + "StandardML-NJ", + "SugarCRM-1.1.3", + "TAPR-OHL-1.0", + "TCL", + "TCP-wrappers", + "TMate", + "TORQUE-1.1", + "TOSL", + "TU-Berlin-1.0", + "TU-Berlin-2.0", + "UCL-1.0", + "UPL-1.0", + "Unicode-DFS-2015", + "Unicode-DFS-2016", + "Unicode-TOU", + "Unlicense", + "VOSTROM", + "VSL-1.0", + "Vim", + "W3C", + "W3C-19980720", + "W3C-20150513", + "WTFPL", + "Watcom-1.0", + "Wsuipa", + "X11", + "XFree86-1.1", + "XSkat", + "Xerox", + "Xnet", + "YPL-1.0", + "YPL-1.1", + "ZPL-1.1", + "ZPL-2.0", + "ZPL-2.1", + "Zed", + "Zend-2.0", + "Zimbra-1.3", + "Zimbra-1.4", + "Zlib", + "blessing", + "bzip2-1.0.5", + "bzip2-1.0.6", + "copyleft-next-0.3.0", + "copyleft-next-0.3.1", + "curl", + "diffmark", + "dvipdfm", + "eCos-2.0", + "eGenix", + "etalab-2.0", + "gSOAP-1.3b", + "gnuplot", + "iMatix", + "libpng-2.0", + "libselinux-1.0", + "libtiff", + "mpich2", + "psfrag", + "psutils", + "wxWindows", + "xinetd", + "xpp", + "zlib-acknowledgement", +}
diff --git a/toolsrc/src/vcpkg/statusparagraph.cpp b/toolsrc/src/vcpkg/statusparagraph.cpp index 2f29fb6c6..3c6e5dc7a 100644 --- a/toolsrc/src/vcpkg/statusparagraph.cpp +++ b/toolsrc/src/vcpkg/statusparagraph.cpp @@ -12,7 +12,7 @@ namespace vcpkg static const std::string STATUS = "Status"; } - StatusParagraph::StatusParagraph() noexcept : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) {} + StatusParagraph::StatusParagraph() noexcept : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) { } void serialize(const StatusParagraph& pgh, std::string& out_str) { @@ -92,10 +92,10 @@ namespace vcpkg std::map<std::string, std::vector<FeatureSpec>> deps; - deps.emplace("core", Util::fmap(core->package.depends, extract_deps)); + deps.emplace("core", Util::fmap(core->package.dependencies, extract_deps)); for (const StatusParagraph* const& feature : features) - deps.emplace(feature->package.feature, Util::fmap(feature->package.depends, extract_deps)); + deps.emplace(feature->package.feature, Util::fmap(feature->package.dependencies, extract_deps)); return deps; } @@ -106,11 +106,11 @@ namespace vcpkg // Todo: make this unneeded by collapsing all package dependencies into the core package std::vector<std::string> deps; for (auto&& feature : features) - for (auto&& dep : feature->package.depends) + for (auto&& dep : feature->package.dependencies) deps.push_back(dep); // Add the core paragraph dependencies to the list - for (auto&& dep : core->package.depends) + for (auto&& dep : core->package.dependencies) deps.push_back(dep); Util::erase_remove_if(deps, [&](const std::string& pspec) { return pspec == spec().name(); }); diff --git a/toolsrc/src/vcpkg/statusparagraphs.cpp b/toolsrc/src/vcpkg/statusparagraphs.cpp index 91058a473..a4d4c2185 100644 --- a/toolsrc/src/vcpkg/statusparagraphs.cpp +++ b/toolsrc/src/vcpkg/statusparagraphs.cpp @@ -35,9 +35,12 @@ namespace vcpkg if (p->package.spec.name() == spec.name() && p->package.spec.triplet() == spec.triplet() && p->is_installed()) { - if (p->package.is_feature()) { + if (p->package.is_feature()) + { ipv.features.emplace_back(p.get()); - } else { + } + else + { Checks::check_exit(VCPKG_LINE_INFO, ipv.core == nullptr); ipv.core = p.get(); } @@ -55,8 +58,8 @@ namespace vcpkg { if (feature == "core") { - // The core feature maps to .feature == "" - return find(name, triplet, ""); + // The core feature maps to .feature is empty + return find(name, triplet, {}); } return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { const PackageSpec& spec = pgh->package.spec; diff --git a/toolsrc/src/vcpkg/tools.cpp b/toolsrc/src/vcpkg/tools.cpp index f657a4aef..717a7dbcf 100644 --- a/toolsrc/src/vcpkg/tools.cpp +++ b/toolsrc/src/vcpkg/tools.cpp @@ -392,7 +392,7 @@ git version 2.17.1.windows.2 struct IfwInstallerBaseProvider : ToolProvider { - std::string m_exe = ""; + std::string m_exe; std::string m_toolname = "installerbase"; virtual const std::string& tool_data_name() const override { return m_toolname; } diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp index 556f9125c..aa8d654e3 100644 --- a/toolsrc/src/vcpkg/update.cpp +++ b/toolsrc/src/vcpkg/update.cpp @@ -57,7 +57,7 @@ namespace vcpkg::Update const StatusParagraphs status_db = database_load_check(paths); - PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get()); + PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); const auto outdated_packages = SortedVector<OutdatedPackage>(find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp index fd044c298..45db0c2e7 100644 --- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp +++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include <vcpkg/base/system.print.h> +#include <vcpkg/base/system.debug.h> #include <vcpkg/commands.h> #include <vcpkg/globalstate.h> #include <vcpkg/metrics.h> @@ -8,14 +9,62 @@ namespace vcpkg { + static void set_from_feature_flag(const std::vector<std::string>& flags, StringView flag, Optional<bool>& place) + { + if (!place.has_value()) + { + const auto not_flag = [flag](const std::string& el) { + return !el.empty() && el[0] == '-' && flag == StringView{el.data() + 1, el.data() + el.size()}; + }; + + if (std::find(flags.begin(), flags.end(), flag) != flags.end()) + { + place = true; + } + if (std::find_if(flags.begin(), flags.end(), not_flag) != flags.end()) + { + if (place.has_value()) + { + System::printf( + System::Color::error, "Error: both %s and -%s were specified as feature flags\n", flag, flag); + Metrics::g_metrics.lock()->track_property("error", "error feature flag +-" + flag.to_string()); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + place = false; + } + } + } + + static void parse_feature_flags(const std::vector<std::string>& flags, VcpkgCmdArguments& args) + { + // NOTE: when these features become default, switch the value_or(false) to value_or(true) + struct FeatureFlag + { + StringView flag_name; + Optional<bool>& local_option; + }; + + const FeatureFlag flag_descriptions[] = { + {VcpkgCmdArguments::BINARY_CACHING_FEATURE, args.binary_caching}, + {VcpkgCmdArguments::MANIFEST_MODE_FEATURE, args.manifest_mode}, + {VcpkgCmdArguments::COMPILER_TRACKING_FEATURE, args.compiler_tracking}, + }; + + for (const auto& desc : flag_descriptions) + { + set_from_feature_flag(flags, desc.flag_name, desc.local_option); + } + } + static void parse_value(const std::string* arg_begin, const std::string* arg_end, - const std::string& option_name, + StringView option_name, std::unique_ptr<std::string>& option_field) { if (arg_begin == arg_end) { - System::print2(System::Color::error, "Error: expected value after ", option_name, '\n'); + System::print2(System::Color::error, "Error: expected value after --", option_name, '\n'); Metrics::g_metrics.lock()->track_property("error", "error option name"); print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); @@ -23,7 +72,7 @@ namespace vcpkg if (option_field != nullptr) { - System::print2(System::Color::error, "Error: ", option_name, " specified multiple times\n"); + System::print2(System::Color::error, "Error: --", option_name, " specified multiple times\n"); Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times"); print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); @@ -32,22 +81,22 @@ namespace vcpkg option_field = std::make_unique<std::string>(*arg_begin); } - static void parse_cojoined_value(std::string new_value, - const std::string& option_name, + static void parse_cojoined_value(StringView new_value, + StringView option_name, std::unique_ptr<std::string>& option_field) { if (nullptr != option_field) { - System::printf(System::Color::error, "Error: %s specified multiple times\n", option_name); + System::printf(System::Color::error, "Error: --%s specified multiple times\n", option_name); Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times"); print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); } - option_field = std::make_unique<std::string>(std::move(new_value)); + option_field = std::make_unique<std::string>(new_value.begin(), new_value.end()); } - static void parse_switch(bool new_setting, const std::string& option_name, Optional<bool>& option_field) + static void parse_switch(bool new_setting, StringView option_name, Optional<bool>& option_field) { if (option_field && option_field != new_setting) { @@ -59,11 +108,11 @@ namespace vcpkg option_field = new_setting; } - static void parse_cojoined_multivalue(std::string new_value, - const std::string& option_name, - std::unique_ptr<std::vector<std::string>>& option_field) + static void parse_cojoined_multivalue(StringView new_value, + StringView option_name, + std::vector<std::string>& option_field) { - if (new_value.empty()) + if (new_value.size() == 0) { System::print2(System::Color::error, "Error: expected value after ", option_name, '\n'); Metrics::g_metrics.lock()->track_property("error", "error option name"); @@ -71,18 +120,14 @@ namespace vcpkg Checks::exit_fail(VCPKG_LINE_INFO); } - if (!option_field) - { - option_field = std::make_unique<std::vector<std::string>>(); - } - option_field->emplace_back(std::move(new_value)); + option_field.emplace_back(new_value.begin(), new_value.end()); } - static void parse_cojoined_multivalue(std::string new_value, - const std::string& option_name, - std::vector<std::string>& option_field) + static void parse_cojoined_list_multivalue(StringView new_value, + StringView option_name, + std::vector<std::string>& option_field) { - if (new_value.empty()) + if (new_value.size() == 0) { System::print2(System::Color::error, "Error: expected value after ", option_name, '\n'); Metrics::g_metrics.lock()->track_property("error", "error option name"); @@ -90,7 +135,10 @@ namespace vcpkg Checks::exit_fail(VCPKG_LINE_INFO); } - option_field.emplace_back(std::move(new_value)); + for (const auto& v : Strings::split(new_value, ',')) + { + option_field.emplace_back(v.begin(), v.end()); + } } VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const Files::Filesystem& fs, @@ -127,186 +175,193 @@ namespace vcpkg return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size()); } + // returns true if this does parse this argument as this option + // REQUIRES: Strings::starts_with(argument, "--"); + template<class T, class F> + static bool try_parse_argument_as_option(StringView option, StringView argument, T& place, F parser) + { + // remove the first two '-'s + const auto arg = argument.substr(2); + if (arg.size() <= option.size() + 1) + { + // it is impossible for this argument to be this option + return false; + } + + if (Strings::starts_with(arg, option) && arg.byte_at_index(option.size()) == '=') + { + parser(arg.substr(option.size() + 1), option, place); + return true; + } + + return false; + } + + // returns true if this does parse this argument as this option + // REQUIRES: Strings::starts_with(argument, "--"); + template<class T> + static bool try_parse_argument_as_switch(StringView option, StringView argument, T& place) + { + // remove the first two '-'s + const auto arg = argument.substr(2); + + if (arg == option) + { + parse_switch(true, option, place); + return true; + } + + if (Strings::starts_with(arg, "no-") && arg.substr(3) == option) + { + parse_switch(false, option, place); + return true; + } + + return false; + } + VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin, const std::string* arg_end) { VcpkgCmdArguments args; + std::vector<std::string> feature_flags; - for (; arg_begin != arg_end; ++arg_begin) + for (auto it = arg_begin; it != arg_end; ++it) { - std::string arg = *arg_begin; + std::string arg = *it; if (arg.empty()) { continue; } - if (arg[0] == '-' && arg[1] != '-') + if (arg.size() >= 2 && arg[0] == '-' && arg[1] != '-') { Metrics::g_metrics.lock()->track_property("error", "error short options are not supported"); Checks::exit_with_message(VCPKG_LINE_INFO, "Error: short options are not supported: %s", arg); } - if (arg[0] == '-' && arg[1] == '-') + if (arg.size() < 2 || arg[0] != '-') { - // make argument case insensitive before the first = - auto& f = std::use_facet<std::ctype<char>>(std::locale()); - auto first_eq = std::find(std::begin(arg), std::end(arg), '='); - f.tolower(&arg[0], &arg[0] + (first_eq - std::begin(arg))); - // command switch - if (arg == "--vcpkg-root") - { - ++arg_begin; - parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir); - continue; - } - if (Strings::starts_with(arg, "--x-buildtrees-root=")) - { - parse_cojoined_value(arg.substr(sizeof("--x-buildtrees-root=") - 1), - "--x-buildtrees-root", - args.buildtrees_root_dir); - continue; - } - if (Strings::starts_with(arg, "--downloads-root=")) - { - parse_cojoined_value( - arg.substr(sizeof("--downloads-root=") - 1), "--downloads-root", args.downloads_root_dir); - continue; - } - if (Strings::starts_with(arg, "--x-install-root=")) - { - parse_cojoined_value( - arg.substr(sizeof("--x-install-root=") - 1), "--x-install-root=", args.install_root_dir); - continue; - } - if (Strings::starts_with(arg, "--x-packages-root=")) - { - parse_cojoined_value( - arg.substr(sizeof("--x-packages-root=") - 1), "--x-packages-root=", args.packages_root_dir); - continue; - } - if (Strings::starts_with(arg, "--x-scripts-root=")) - { - parse_cojoined_value( - arg.substr(sizeof("--x-scripts-root=") - 1), "--x-scripts-root", args.scripts_root_dir); - continue; - } - if (arg == "--triplet") - { - ++arg_begin; - parse_value(arg_begin, arg_end, "--triplet", args.triplet); - continue; - } - if (Strings::starts_with(arg, "--overlay-ports=")) - { - parse_cojoined_multivalue( - arg.substr(sizeof("--overlay-ports=") - 1), "--overlay-ports", args.overlay_ports); - continue; - } - if (Strings::starts_with(arg, "--overlay-triplets=")) - { - parse_cojoined_multivalue( - arg.substr(sizeof("--overlay-triplets=") - 1), "--overlay-triplets", args.overlay_triplets); - continue; - } - if (Strings::starts_with(arg, "--x-binarysource=")) - { - parse_cojoined_multivalue( - arg.substr(sizeof("--x-binarysource=") - 1), "--x-binarysource", args.binarysources); - continue; - } - if (arg == "--debug") - { - parse_switch(true, "debug", args.debug); - continue; - } - if (arg == "--sendmetrics") - { - parse_switch(true, "sendmetrics", args.send_metrics); - continue; - } - if (arg == "--printmetrics") - { - parse_switch(true, "printmetrics", args.print_metrics); - continue; - } - if (arg == "--disable-metrics") - { - parse_switch(true, "disable-metrics", args.disable_metrics); - continue; - } - if (arg == "--no-sendmetrics") - { - parse_switch(false, "no-sendmetrics", args.send_metrics); - continue; - } - if (arg == "--no-printmetrics") + if (args.command.empty()) { - parse_switch(false, "no-printmetrics", args.print_metrics); - continue; + args.command = std::move(arg); } - if (arg == "--no-disable-metrics") - { - parse_switch(false, "no-disable-metrics", args.disable_metrics); - continue; - } - if (arg == "--featurepackages") + else { - parse_switch(true, "featurepackages", args.feature_packages); - continue; + args.command_arguments.push_back(std::move(arg)); } - if (arg == "--no-featurepackages") + continue; + } + + // arg[0] == '-' && arg[1] == '-' + // make argument case insensitive before the first = + auto first_eq = std::find(std::begin(arg), std::end(arg), '='); + Strings::ascii_to_lowercase(std::begin(arg), first_eq); + + // command switch + if (arg.substr(2) == VCPKG_ROOT_DIR_ARG) + { + ++it; + parse_value(it, arg_end, VCPKG_ROOT_DIR_ARG, args.vcpkg_root_dir); + continue; + } + if (arg.substr(2) == TRIPLET_ARG) + { + ++it; + parse_value(it, arg_end, TRIPLET_ARG, args.triplet); + continue; + } + + constexpr static std::pair<StringView, std::unique_ptr<std::string> VcpkgCmdArguments::*> + cojoined_values[] = { + {MANIFEST_ROOT_DIR_ARG, &VcpkgCmdArguments::manifest_root_dir}, + {BUILDTREES_ROOT_DIR_ARG, &VcpkgCmdArguments::buildtrees_root_dir}, + {DOWNLOADS_ROOT_DIR_ARG, &VcpkgCmdArguments::downloads_root_dir}, + {INSTALL_ROOT_DIR_ARG, &VcpkgCmdArguments::install_root_dir}, + {PACKAGES_ROOT_DIR_ARG, &VcpkgCmdArguments::packages_root_dir}, + {SCRIPTS_ROOT_DIR_ARG, &VcpkgCmdArguments::scripts_root_dir}, + }; + + constexpr static std::pair<StringView, std::vector<std::string> VcpkgCmdArguments::*> + cojoined_multivalues[] = { + {OVERLAY_PORTS_ARG, &VcpkgCmdArguments::overlay_ports}, + {OVERLAY_TRIPLETS_ARG, &VcpkgCmdArguments::overlay_triplets}, + {BINARY_SOURCES_ARG, &VcpkgCmdArguments::binary_sources}, + }; + + constexpr static std::pair<StringView, Optional<bool> VcpkgCmdArguments::*> switches[] = { + {DEBUG_SWITCH, &VcpkgCmdArguments::debug}, + {DISABLE_METRICS_SWITCH, &VcpkgCmdArguments::disable_metrics}, + {SEND_METRICS_SWITCH, &VcpkgCmdArguments::send_metrics}, + {PRINT_METRICS_SWITCH, &VcpkgCmdArguments::print_metrics}, + {FEATURE_PACKAGES_SWITCH, &VcpkgCmdArguments::feature_packages}, + {BINARY_CACHING_SWITCH, &VcpkgCmdArguments::binary_caching}, + }; + + bool found = false; + for (const auto& pr : cojoined_values) + { + if (try_parse_argument_as_option(pr.first, arg, args.*pr.second, parse_cojoined_value)) { - parse_switch(false, "featurepackages", args.feature_packages); - continue; + found = true; + break; } - if (arg == "--binarycaching") + } + if (found) continue; + + for (const auto& pr : cojoined_multivalues) + { + if (try_parse_argument_as_option(pr.first, arg, args.*pr.second, parse_cojoined_multivalue)) { - parse_switch(true, "binarycaching", args.binary_caching); - continue; + found = true; + break; } - if (arg == "--no-binarycaching") + } + if (found) continue; + + if (try_parse_argument_as_option(FEATURE_FLAGS_ARG, arg, feature_flags, parse_cojoined_list_multivalue)) + { + continue; + } + + for (const auto& pr : switches) + { + if (try_parse_argument_as_switch(pr.first, arg, args.*pr.second)) { - parse_switch(false, "no-binarycaching", args.binary_caching); - continue; + found = true; + break; } + } + if (found) continue; - const auto eq_pos = arg.find('='); - if (eq_pos != std::string::npos) - { - const auto& key = arg.substr(0, eq_pos); - const auto& value = arg.substr(eq_pos + 1); + const auto eq_pos = arg.find('='); + if (eq_pos != std::string::npos) + { + const auto& key = arg.substr(0, eq_pos); + const auto& value = arg.substr(eq_pos + 1); - auto it = args.optional_command_arguments.find(key); - if (args.optional_command_arguments.end() == it) - { - args.optional_command_arguments.emplace(key, std::vector<std::string>{value}); - } - else - { - if (auto* maybe_values = it->second.get()) - { - maybe_values->emplace_back(value); - } - } + auto key_it = args.optional_command_arguments.find(key); + if (key_it == args.optional_command_arguments.end()) + { + args.optional_command_arguments.emplace(key, std::vector<std::string>{value}); } else { - args.optional_command_arguments.emplace(arg, nullopt); + if (auto* maybe_values = key_it->second.get()) + { + maybe_values->emplace_back(value); + } } - continue; - } - - if (args.command.empty()) - { - args.command = arg; } else { - args.command_arguments.push_back(arg); + args.optional_command_arguments.emplace(arg, nullopt); } } + parse_feature_flags(feature_flags, args); + return args; } @@ -523,48 +578,39 @@ namespace vcpkg void VcpkgCmdArguments::append_common_options(HelpTableFormatter& table) { - table.format("--triplet <t>", "Specify the target architecture triplet. See 'vcpkg help triplet'"); + constexpr static auto opt = [](StringView arg, StringView joiner, StringView value) { + return Strings::format("--%s%s%s", arg, joiner, value); + }; + + table.format(opt(TRIPLET_ARG, " ", "<t>"), "Specify the target architecture triplet. See 'vcpkg help triplet'"); table.format("", "(default: " + format_environment_variable("VCPKG_DEFAULT_TRIPLET") + ')'); - table.format("--overlay-ports=<path>", "Specify directories to be used when searching for ports"); - table.format("--overlay-triplets=<path>", "Specify directories containing triplets files"); - table.format("--downloads-root=<path>", "Specify the downloads root directory"); + table.format(opt(OVERLAY_PORTS_ARG, "=", "<path>"), "Specify directories to be used when searching for ports"); + table.format(opt(OVERLAY_TRIPLETS_ARG, "=", "<path>"), "Specify directories containing triplets files"); + table.format(opt(DOWNLOADS_ROOT_DIR_ARG, "=", "<path>"), "Specify the downloads root directory"); table.format("", "(default: " + format_environment_variable("VCPKG_DOWNLOADS") + ')'); - table.format("--vcpkg-root <path>", "Specify the vcpkg root directory"); + table.format(opt(VCPKG_ROOT_DIR_ARG, " ", "<path>"), "Specify the vcpkg root directory"); table.format("", "(default: " + format_environment_variable("VCPKG_ROOT") + ')'); - table.format("--x-buildtrees-root=<path>", "(Experimental) Specify the buildtrees root directory"); - table.format("--x-install-root=<path>", "(Experimental) Specify the install root directory"); - table.format("--x-packages-root=<path>", "(Experimental) Specify the packages root directory"); - table.format("--x-scripts-root=<path>", "(Experimental) Specify the scripts root directory"); + table.format(opt(BUILDTREES_ROOT_DIR_ARG, "=", "<path>"), + "(Experimental) Specify the buildtrees root directory"); + table.format(opt(INSTALL_ROOT_DIR_ARG, "=", "<path>"), "(Experimental) Specify the install root directory"); + table.format(opt(PACKAGES_ROOT_DIR_ARG, "=", "<path>"), "(Experimental) Specify the packages root directory"); + table.format(opt(SCRIPTS_ROOT_DIR_ARG, "=", "<path>"), "(Experimental) Specify the scripts root directory"); } void VcpkgCmdArguments::imbue_from_environment() { if (!disable_metrics) { - const auto vcpkg_disable_metrics_env = System::get_environment_variable("VCPKG_DISABLE_METRICS"); - if (vcpkg_disable_metrics_env) + const auto vcpkg_disable_metrics_env = System::get_environment_variable(DISABLE_METRICS_ENV); + if (vcpkg_disable_metrics_env.has_value()) { disable_metrics = true; } } - const auto vcpkg_feature_flags_env = System::get_environment_variable("VCPKG_FEATURE_FLAGS"); - if (const auto unpacked = vcpkg_feature_flags_env.get()) - { - auto flags = Strings::split(*unpacked, ','); - if (!binary_caching && Util::Vectors::contains(flags, "binarycaching")) - { - binary_caching = true; - } - if (Util::Vectors::contains(flags, "compilertracking")) - { - compiler_tracking = true; - } - } - if (!triplet) { - const auto vcpkg_default_triplet_env = System::get_environment_variable("VCPKG_DEFAULT_TRIPLET"); + const auto vcpkg_default_triplet_env = System::get_environment_variable(TRIPLET_ENV); if (const auto unpacked = vcpkg_default_triplet_env.get()) { triplet = std::make_unique<std::string>(*unpacked); @@ -573,7 +619,7 @@ namespace vcpkg if (!vcpkg_root_dir) { - const auto vcpkg_root_env = System::get_environment_variable("VCPKG_ROOT"); + const auto vcpkg_root_env = System::get_environment_variable(VCPKG_ROOT_DIR_ENV); if (const auto unpacked = vcpkg_root_env.get()) { vcpkg_root_dir = std::make_unique<std::string>(*unpacked); @@ -582,15 +628,22 @@ namespace vcpkg if (!downloads_root_dir) { - const auto vcpkg_downloads_env = vcpkg::System::get_environment_variable("VCPKG_DOWNLOADS"); + const auto vcpkg_downloads_env = vcpkg::System::get_environment_variable(DOWNLOADS_ROOT_DIR_ENV); if (const auto unpacked = vcpkg_downloads_env.get()) { downloads_root_dir = std::make_unique<std::string>(*unpacked); } } + const auto vcpkg_feature_flags_env = System::get_environment_variable(FEATURE_FLAGS_ENV); + if (const auto v = vcpkg_feature_flags_env.get()) { - const auto vcpkg_visual_studio_path_env = System::get_environment_variable("VCPKG_VISUAL_STUDIO_PATH"); + auto flags = Strings::split(*v, ','); + parse_feature_flags(flags, *this); + } + + { + const auto vcpkg_visual_studio_path_env = System::get_environment_variable(DEFAULT_VISUAL_STUDIO_PATH_ENV); if (const auto unpacked = vcpkg_visual_studio_path_env.get()) { default_visual_studio_path = std::make_unique<std::string>(*unpacked); @@ -598,6 +651,48 @@ namespace vcpkg } } + void VcpkgCmdArguments::check_feature_flag_consistency() const + { + struct + { + StringView flag; + StringView option; + bool is_inconsistent; + } possible_inconsistencies[] = { + {BINARY_CACHING_FEATURE, BINARY_SOURCES_ARG, !binary_sources.empty() && !binary_caching.value_or(true)}, + {MANIFEST_MODE_FEATURE, MANIFEST_ROOT_DIR_ARG, manifest_root_dir && !manifest_mode.value_or(true)}, + }; + for (const auto& el : possible_inconsistencies) + { + if (el.is_inconsistent) + { + System::printf(System::Color::warning, + "Warning: %s feature specifically turned off, but --%s was specified.\n", + el.flag, + el.option); + System::printf(System::Color::warning, "Warning: Defaulting to %s being on.\n", el.flag); + Metrics::g_metrics.lock()->track_property( + "warning", Strings::format("warning %s alongside %s", el.flag, el.option)); + } + } + } + + void VcpkgCmdArguments::track_feature_flag_metrics() const + { + struct + { + StringView flag; + bool enabled; + } flags[] = { + {BINARY_CACHING_FEATURE, binary_caching_enabled()} + }; + + for (const auto& flag : flags) + { + Metrics::g_metrics.lock()->track_feature(flag.flag.to_string(), flag.enabled); + } + } + std::string format_environment_variable(StringLiteral lit) { std::string result; diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index 0099854a9..3d5a3bd6f 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -8,6 +8,7 @@ #include <vcpkg/base/system.process.h> #include <vcpkg/build.h> #include <vcpkg/commands.h> +#include <vcpkg/globalstate.h> #include <vcpkg/metrics.h> #include <vcpkg/packagespec.h> #include <vcpkg/vcpkgpaths.h> @@ -60,6 +61,20 @@ namespace return result; } + void uppercase_win32_drive_letter(fs::path& path) + { +#if defined(_WIN32) + const auto& nativePath = path.native(); + if (nativePath.size() > 2 && (nativePath[0] >= L'a' && nativePath[0] <= L'z') && nativePath[1] == L':') + { + auto uppercaseFirstLetter = std::move(path).native(); + uppercaseFirstLetter[0] = nativePath[0] - L'a' + L'A'; + path = uppercaseFirstLetter; + } +#endif + (void)path; + } + } // unnamed namespace namespace vcpkg @@ -84,11 +99,11 @@ namespace vcpkg std::unique_ptr<ToolCache> m_tool_cache; Cache<Triplet, fs::path> m_triplets_cache; Build::EnvCache m_env_cache; + + fs::SystemHandle file_lock_handle; }; } - VcpkgPaths::~VcpkgPaths() noexcept {} - VcpkgPaths::VcpkgPaths(Files::Filesystem& filesystem, const VcpkgCmdArguments& args) : m_pimpl(std::make_unique<details::VcpkgPathsImpl>(filesystem, args.compiler_tracking_enabled())) { @@ -106,20 +121,72 @@ namespace vcpkg filesystem.canonical(VCPKG_LINE_INFO, System::get_exe_path_of_current_process()), ".vcpkg-root"); } } + uppercase_win32_drive_letter(root); + Checks::check_exit(VCPKG_LINE_INFO, !root.empty(), "Error: Could not detect vcpkg-root."); + Debug::print("Using vcpkg-root: ", root.u8string(), '\n'); + + std::error_code ec; + const auto vcpkg_lock = root / ".vcpkg-root"; + m_pimpl->file_lock_handle = filesystem.try_take_exclusive_file_lock(vcpkg_lock, ec); + if (ec) + { + System::printf(System::Color::error, "Failed to take the filesystem lock on %s:\n", vcpkg_lock.u8string()); + System::printf(System::Color::error, " %s\n", ec.message()); + System::print2(System::Color::error, "Exiting now.\n"); + Checks::exit_fail(VCPKG_LINE_INFO); + } -#if defined(_WIN32) - // fixup Windows drive letter to uppercase - const auto& nativeRoot = root.native(); - if (nativeRoot.size() > 2 && (nativeRoot[0] >= L'a' && nativeRoot[0] <= L'z') && nativeRoot[1] == L':') + if (args.manifest_root_dir) { - auto uppercaseFirstLetter = nativeRoot; - uppercaseFirstLetter[0] = nativeRoot[0] - L'a' + L'A'; - root = uppercaseFirstLetter; + manifest_root_dir = filesystem.canonical(VCPKG_LINE_INFO, fs::u8path(*args.manifest_root_dir)); } -#endif // defined(_WIN32) + else + { + manifest_root_dir = filesystem.find_file_recursively_up(original_cwd, "vcpkg.json"); + } + uppercase_win32_drive_letter(manifest_root_dir); - Checks::check_exit(VCPKG_LINE_INFO, !root.empty(), "Error: Could not detect vcpkg-root."); - Debug::print("Using vcpkg-root: ", root.u8string(), '\n'); + if (!manifest_root_dir.empty() && args.manifest_mode.value_or(true)) + { + Debug::print("Using manifest-root: ", root.u8string(), '\n'); + + installed = process_output_directory( + filesystem, manifest_root_dir, args.install_root_dir.get(), "vcpkg_installed", VCPKG_LINE_INFO); + } + else + { + // we ignore the manifest root dir if the user requests -manifest + if (!manifest_root_dir.empty() && !args.manifest_mode.has_value()) + { + System::print2(System::Color::warning, + "Warning: manifest-root detected at ", + manifest_root_dir.generic_u8string(), + ", but manifests are not enabled.\n"); + System::printf(System::Color::warning, + R"(If you wish to use manifest mode, you may do one of the following: + * Add the `%s` feature flag to the comma-separated environment + variable `%s`. + * Add the `%s` feature flag to the `--%s` option. + * Pass your manifest directory to the `--%s` option. +If you wish to silence this error and use classic mode, you can: + * Add the `-%s` feature flag to `%s`. + * Add the `-%s` feature flag to `--%s`. +)", + VcpkgCmdArguments::MANIFEST_MODE_FEATURE, + VcpkgCmdArguments::FEATURE_FLAGS_ENV, + VcpkgCmdArguments::MANIFEST_MODE_FEATURE, + VcpkgCmdArguments::FEATURE_FLAGS_ARG, + VcpkgCmdArguments::MANIFEST_ROOT_DIR_ARG, + VcpkgCmdArguments::MANIFEST_MODE_FEATURE, + VcpkgCmdArguments::FEATURE_FLAGS_ENV, + VcpkgCmdArguments::MANIFEST_MODE_FEATURE, + VcpkgCmdArguments::FEATURE_FLAGS_ARG); + } + + manifest_root_dir.clear(); + installed = + process_output_directory(filesystem, root, args.install_root_dir.get(), "installed", VCPKG_LINE_INFO); + } buildtrees = process_output_directory(filesystem, root, args.buildtrees_root_dir.get(), "buildtrees", VCPKG_LINE_INFO); @@ -128,8 +195,6 @@ namespace vcpkg packages = process_output_directory(filesystem, root, args.packages_root_dir.get(), "packages", VCPKG_LINE_INFO); ports = filesystem.canonical(VCPKG_LINE_INFO, root / fs::u8path("ports")); - installed = - process_output_directory(filesystem, root, args.install_root_dir.get(), "installed", VCPKG_LINE_INFO); scripts = process_input_directory(filesystem, root, args.scripts_root_dir.get(), "scripts", VCPKG_LINE_INFO); prefab = root / fs::u8path("prefab"); @@ -155,13 +220,10 @@ namespace vcpkg ports_cmake = filesystem.canonical(VCPKG_LINE_INFO, scripts / fs::u8path("ports.cmake")); - if (args.overlay_triplets) + for (auto&& overlay_triplets_dir : args.overlay_triplets) { - for (auto&& overlay_triplets_dir : *args.overlay_triplets) - { - m_pimpl->triplets_dirs.emplace_back( - filesystem.canonical(VCPKG_LINE_INFO, fs::u8path(overlay_triplets_dir))); - } + m_pimpl->triplets_dirs.emplace_back( + filesystem.canonical(VCPKG_LINE_INFO, fs::u8path(overlay_triplets_dir))); } m_pimpl->triplets_dirs.emplace_back(triplets); m_pimpl->triplets_dirs.emplace_back(community_triplets); @@ -245,13 +307,13 @@ namespace vcpkg { static Toolset external_toolset = []() -> Toolset { Toolset ret; - ret.dumpbin = ""; + ret.dumpbin.clear(); ret.supported_architectures = { ToolsetArchOption{"", System::get_host_processor(), System::get_host_processor()}}; - ret.vcvarsall = ""; + ret.vcvarsall.clear(); ret.vcvarsall_options = {}; ret.version = "external"; - ret.visual_studio_root_path = ""; + ret.visual_studio_root_path.clear(); return ret; }(); return external_toolset; @@ -320,4 +382,30 @@ namespace vcpkg } Files::Filesystem& VcpkgPaths::get_filesystem() const { return *m_pimpl->fs_ptr; } + + void VcpkgPaths::track_feature_flag_metrics() const + { + struct + { + StringView flag; + bool enabled; + } flags[] = { + {VcpkgCmdArguments::MANIFEST_MODE_FEATURE, manifest_mode_enabled()} + }; + + for (const auto& flag : flags) + { + Metrics::g_metrics.lock()->track_feature(flag.flag.to_string(), flag.enabled); + } + } + + VcpkgPaths::~VcpkgPaths() + { + std::error_code ec; + m_pimpl->fs_ptr->unlock_file_lock(m_pimpl->file_lock_handle, ec); + if (ec) + { + Debug::print("Failed to unlock filesystem lock: ", ec.message(), '\n'); + } + } } |
