aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authornicole mazzuca <mazzucan@outlook.com>2020-06-30 10:40:18 -0700
committerGitHub <noreply@github.com>2020-06-30 10:40:18 -0700
commit1d8f0acc9c3085d18152a3f639077a28109196b6 (patch)
tree1daa4780e0d5f51844217dc61dbf275433153abf /toolsrc/src
parent67ab6130b64cce6701c5a95ca3b9bdc4d949cb8a (diff)
downloadvcpkg-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')
-rw-r--r--toolsrc/src/vcpkg-fuzz/main.cpp60
-rw-r--r--toolsrc/src/vcpkg-test/arguments.cpp24
-rw-r--r--toolsrc/src/vcpkg-test/dependencies.cpp21
-rw-r--r--toolsrc/src/vcpkg-test/json.cpp4
-rw-r--r--toolsrc/src/vcpkg-test/manifests.cpp235
-rw-r--r--toolsrc/src/vcpkg-test/paragraph.cpp120
-rw-r--r--toolsrc/src/vcpkg-test/plan.cpp2
-rw-r--r--toolsrc/src/vcpkg.cpp7
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp92
-rw-r--r--toolsrc/src/vcpkg/base/json.cpp66
-rw-r--r--toolsrc/src/vcpkg/base/parse.cpp14
-rw-r--r--toolsrc/src/vcpkg/base/strings.cpp7
-rw-r--r--toolsrc/src/vcpkg/base/stringview.cpp11
-rw-r--r--toolsrc/src/vcpkg/binarycaching.cpp2
-rw-r--r--toolsrc/src/vcpkg/binaryparagraph.cpp215
-rw-r--r--toolsrc/src/vcpkg/build.cpp4
-rw-r--r--toolsrc/src/vcpkg/buildenvironment.cpp1
-rw-r--r--toolsrc/src/vcpkg/commands.buildexternal.cpp6
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp37
-rw-r--r--toolsrc/src/vcpkg/commands.cpp5
-rw-r--r--toolsrc/src/vcpkg/commands.dependinfo.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.env.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.exportifw.cpp4
-rw-r--r--toolsrc/src/vcpkg/commands.format-manifest.cpp100
-rw-r--r--toolsrc/src/vcpkg/commands.import.cpp126
-rw-r--r--toolsrc/src/vcpkg/commands.list.cpp12
-rw-r--r--toolsrc/src/vcpkg/commands.search.cpp55
-rw-r--r--toolsrc/src/vcpkg/commands.setinstalled.cpp96
-rw-r--r--toolsrc/src/vcpkg/commands.upgrade.cpp4
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp19
-rw-r--r--toolsrc/src/vcpkg/export.chocolatey.cpp6
-rw-r--r--toolsrc/src/vcpkg/export.cpp6
-rw-r--r--toolsrc/src/vcpkg/install.cpp76
-rw-r--r--toolsrc/src/vcpkg/logicexpression.cpp320
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp16
-rw-r--r--toolsrc/src/vcpkg/packagespec.cpp33
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp59
-rw-r--r--toolsrc/src/vcpkg/platform-expression.cpp471
-rw-r--r--toolsrc/src/vcpkg/portfileprovider.cpp55
-rw-r--r--toolsrc/src/vcpkg/remove.cpp6
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp677
-rw-r--r--toolsrc/src/vcpkg/spdx-exceptions.inc45
-rw-r--r--toolsrc/src/vcpkg/spdx-licenses.inc426
-rw-r--r--toolsrc/src/vcpkg/statusparagraph.cpp10
-rw-r--r--toolsrc/src/vcpkg/statusparagraphs.cpp11
-rw-r--r--toolsrc/src/vcpkg/tools.cpp2
-rw-r--r--toolsrc/src/vcpkg/update.cpp2
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp477
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp134
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 = [&current_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');
+ }
+ }
}