From 58fde40e01c9fc938ca03dbb674d2fb481715c83 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Sat, 9 Jan 2021 22:04:03 +0000 Subject: [PATCH 1/8] [vcpkg] Improve efficiency and tests of versioning --- .../end-to-end-tests-dir/versions.ps1 | 32 ++- scripts/generatePortVersionsDb.py | 2 +- .../default-baseline-2/vcpkg.json | 2 +- .../port_versions/z-/zlib.json | 14 ++ .../without-default-baseline-2/vcpkg.json | 7 + toolsrc/include/vcpkg/vcpkgpaths.h | 11 +- toolsrc/src/vcpkg-test/dependencies.cpp | 2 +- toolsrc/src/vcpkg/registries.cpp | 23 +- toolsrc/src/vcpkg/vcpkgpaths.cpp | 212 +++++++++++------- 9 files changed, 183 insertions(+), 122 deletions(-) create mode 100644 scripts/testing/version-files/without-default-baseline-2/port_versions/z-/zlib.json create mode 100644 scripts/testing/version-files/without-default-baseline-2/vcpkg.json diff --git a/scripts/azure-pipelines/end-to-end-tests-dir/versions.ps1 b/scripts/azure-pipelines/end-to-end-tests-dir/versions.ps1 index 5d61b2101d6c73..2d3d35fe6155ca 100644 --- a/scripts/azure-pipelines/end-to-end-tests-dir/versions.ps1 +++ b/scripts/azure-pipelines/end-to-end-tests-dir/versions.ps1 @@ -59,17 +59,35 @@ Throw-IfFailed Throw-IfFailed $CurrentTest = "default baseline" -$out = ./vcpkg $commonArgs "--feature-flags=versions" install --x-manifest-root=scripts/testing/version-files/default-baseline-1 +$out = ./vcpkg $commonArgs "--feature-flags=versions" install --x-manifest-root=scripts/testing/version-files/default-baseline-1 2>&1 | Out-String Throw-IfNotFailed -# if ($out -notmatch "Error: while checking out baseline" -or $out -notmatch " does not exist in ") -# { -# $out -# throw "Expected to fail due to missing baseline" -# } +if ($out -notmatch ".*Error: while checking out baseline.*") +{ + $out + throw "Expected to fail due to missing baseline" +} git fetch https://github.com/vicroms/test-registries -$CurrentTest = "default baseline" +$CurrentTest = "without default baseline 2 -- enabling versions should not change behavior" +Remove-Item -Recurse $buildtreesRoot/versioning -ErrorAction SilentlyContinue +./vcpkg $commonArgs "--feature-flags=versions" install ` + "--dry-run" ` + "--x-manifest-root=scripts/testing/version-files/without-default-baseline-2" ` + "--x-builtin-port-versions-dir=scripts/testing/version-files/default-baseline-2/port_versions" +Throw-IfFailed +Require-FileNotExists $buildtreesRoot/versioning + +$CurrentTest = "default baseline 2" ./vcpkg $commonArgs "--feature-flags=versions" install ` + "--dry-run" ` "--x-manifest-root=scripts/testing/version-files/default-baseline-2" ` "--x-builtin-port-versions-dir=scripts/testing/version-files/default-baseline-2/port_versions" Throw-IfFailed +Require-FileExists $buildtreesRoot/versioning + +$CurrentTest = "using version features fails without flag" +./vcpkg $commonArgs "--feature-flags=-versions" install ` + "--dry-run" ` + "--x-manifest-root=scripts/testing/version-files/default-baseline-2" ` + "--x-builtin-port-versions-dir=scripts/testing/version-files/default-baseline-2/port_versions" +Throw-IfNotFailed diff --git a/scripts/generatePortVersionsDb.py b/scripts/generatePortVersionsDb.py index 3b7de69421c428..3be4a939e478d7 100644 --- a/scripts/generatePortVersionsDb.py +++ b/scripts/generatePortVersionsDb.py @@ -35,7 +35,7 @@ def generate_port_versions_file(port_name): env = os.environ.copy() env['GIT_OPTIONAL_LOCKS'] = '0' output = subprocess.run( - [os.path.join(SCRIPT_DIRECTORY, '../vcpkg.exe'), + [os.path.join(SCRIPT_DIRECTORY, '../vcpkg'), 'x-history', port_name, '--x-json', f'--output={output_file_path}'], capture_output=True, encoding='utf-8', env=env) if output.returncode != 0: diff --git a/scripts/testing/version-files/default-baseline-2/vcpkg.json b/scripts/testing/version-files/default-baseline-2/vcpkg.json index 1e41002ff99bf5..101d6f752c3061 100644 --- a/scripts/testing/version-files/default-baseline-2/vcpkg.json +++ b/scripts/testing/version-files/default-baseline-2/vcpkg.json @@ -1,5 +1,5 @@ { - "name": "default-baseline-test", + "name": "default-baseline-test-2", "version-string": "0", "$x-default-baseline": "cbd5a68012471f820b7cf28d618199b4a4d89c58", "dependencies": [ diff --git a/scripts/testing/version-files/without-default-baseline-2/port_versions/z-/zlib.json b/scripts/testing/version-files/without-default-baseline-2/port_versions/z-/zlib.json new file mode 100644 index 00000000000000..f5ee7cb9dc66d6 --- /dev/null +++ b/scripts/testing/version-files/without-default-baseline-2/port_versions/z-/zlib.json @@ -0,0 +1,14 @@ +{ + "versions": [ + { + "git-tree": "7bb2b2f3783303a4dd41163553fe4cc103dc9262", + "version-string": "1.2.11", + "port-version": 9 + }, + { + "git-tree": "4927735fa9baca564ebddf6e6880de344b20d7a8", + "version-string": "1.2.11", + "port-version": 8 + } + ] +} \ No newline at end of file diff --git a/scripts/testing/version-files/without-default-baseline-2/vcpkg.json b/scripts/testing/version-files/without-default-baseline-2/vcpkg.json new file mode 100644 index 00000000000000..839418fb71ecbf --- /dev/null +++ b/scripts/testing/version-files/without-default-baseline-2/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "without-default-baseline-test-2", + "version-string": "0", + "dependencies": [ + "zlib" + ] +} \ No newline at end of file diff --git a/toolsrc/include/vcpkg/vcpkgpaths.h b/toolsrc/include/vcpkg/vcpkgpaths.h index a175d51e219c1b..737389704964c1 100644 --- a/toolsrc/include/vcpkg/vcpkgpaths.h +++ b/toolsrc/include/vcpkg/vcpkgpaths.h @@ -118,8 +118,8 @@ namespace vcpkg const std::string& get_tool_version(const std::string& tool) const; // Git manipulation in the vcpkg directory - fs::path git_checkout_baseline(Files::Filesystem& filesystem, StringView commit_sha) const; - fs::path git_checkout_port(Files::Filesystem& filesystem, StringView port_name, StringView git_tree) const; + ExpectedS git_checkout_baseline(StringView commit_sha) const; + ExpectedS git_checkout_port(StringView port_name, StringView git_tree, const fs::path& dot_git_dir) const; ExpectedS git_show(const std::string& treeish, const fs::path& dot_git_dir) const; ExpectedS>> git_get_local_port_treeish_map() const; @@ -158,12 +158,5 @@ namespace vcpkg const fs::path& destination, const fs::path& dot_git_dir, const fs::path& work_tree); - - static void git_checkout_object(const VcpkgPaths& paths, - StringView git_object, - const fs::path& local_repo, - const fs::path& destination, - const fs::path& dot_git_dir, - const fs::path& work_tree); }; } diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp index 2831ea5a1abb2c..d4f03d0b6cf7e0 100644 --- a/toolsrc/src/vcpkg-test/dependencies.cpp +++ b/toolsrc/src/vcpkg-test/dependencies.cpp @@ -196,7 +196,7 @@ struct MockOverlayProvider : PortFileProvider::IOverlayProvider, Util::ResourceB static const MockOverlayProvider s_empty_mock_overlay; -ExpectedS create_versioned_install_plan( +static ExpectedS create_versioned_install_plan( const PortFileProvider::IVersionedPortfileProvider& provider, const PortFileProvider::IBaselineProvider& bprovider, const CMakeVars::CMakeVarProvider& var_provider, diff --git a/toolsrc/src/vcpkg/registries.cpp b/toolsrc/src/vcpkg/registries.cpp index 31f3078deb13f6..5bde3e1640d97d 100644 --- a/toolsrc/src/vcpkg/registries.cpp +++ b/toolsrc/src/vcpkg/registries.cpp @@ -122,17 +122,6 @@ namespace DelayedInit m_baseline; }; - ExpectedS get_git_baseline_json_path(const VcpkgPaths& paths, StringView baseline_commit_sha) - { - auto baseline_path = paths.git_checkout_baseline(paths.get_filesystem(), baseline_commit_sha); - if (paths.get_filesystem().exists(baseline_path)) - { - return std::move(baseline_path); - } - return {Strings::concat("Error: Baseline database file does not exist: ", fs::u8string(baseline_path)), - expected_right_tag}; - } - struct VersionDbEntry { VersionT version; @@ -195,7 +184,8 @@ namespace std::unique_ptr BuiltinRegistry::get_port_entry(const VcpkgPaths& paths, StringView port_name) const { auto versions_path = paths.builtin_port_versions / relative_path_to_versions(port_name); - if ((paths.get_feature_flags().registries || paths.get_feature_flags().versions) && + if ((paths.get_feature_flags().registries || + (paths.get_feature_flags().versions && !this->m_baseline_identifier.empty())) && paths.get_filesystem().exists(versions_path)) { auto maybe_version_entries = @@ -273,7 +263,7 @@ namespace } // attempt to check out the baseline: - auto maybe_path = get_git_baseline_json_path(paths, baseline_identifier); + auto maybe_path = paths.git_checkout_baseline(baseline_identifier); if (!maybe_path.has_value()) { return Strings::format("Couldn't find explicitly specified baseline `\"%s\"` in the baseline file, " @@ -428,12 +418,13 @@ namespace auto it = std::find(git_entry->port_versions.begin(), git_entry->port_versions.end(), version); if (it == git_entry->port_versions.end()) { - return Strings::concat( - "Error: No version entry for ", git_entry->port_name, " at version ", version, "."); + return { + Strings::concat("Error: No version entry for ", git_entry->port_name, " at version ", version, "."), + expected_right_tag}; } const auto& git_tree = git_entry->git_trees[it - git_entry->port_versions.begin()]; - return paths.git_checkout_port(paths.get_filesystem(), git_entry->port_name, git_tree); + return paths.git_checkout_port(git_entry->port_name, git_tree, paths.root / fs::u8path(".git")); } if (scfl_version == version) diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index 27b7cfe0281db3..d38b85ef993ce1 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -609,119 +609,157 @@ If you wish to silence this error and use classic mode, you can: return ret; } - void VcpkgPaths::git_checkout_object(const VcpkgPaths& paths, - StringView git_object, - const fs::path& local_repo, - const fs::path& destination, - const fs::path& dot_git_dir, - const fs::path& work_tree) - { - Files::Filesystem& fs = paths.get_filesystem(); - fs.remove_all(work_tree, VCPKG_LINE_INFO); - fs.remove_all(destination, VCPKG_LINE_INFO); - - if (!fs.exists(dot_git_dir)) - { - // All git commands are run with: --git-dir={dot_git_dir} --work-tree={work_tree_temp} - // git clone --no-checkout --local {vcpkg_root} {dot_git_dir} - System::CmdLineBuilder clone_cmd_builder = git_cmd_builder(paths, dot_git_dir, work_tree) - .string_arg("clone") - .string_arg("--no-checkout") - .string_arg("--local") - .string_arg("--no-hardlinks") - .path_arg(local_repo) - .path_arg(dot_git_dir); - const auto clone_output = System::cmd_execute_and_capture_output(clone_cmd_builder.extract()); - Checks::check_exit(VCPKG_LINE_INFO, - clone_output.exit_code == 0, - "Failed to clone temporary vcpkg instance.\n%s\n", - clone_output.output); - } - else - { - System::CmdLineBuilder fetch_cmd_builder = - git_cmd_builder(paths, dot_git_dir, work_tree).string_arg("fetch"); - const auto fetch_output = System::cmd_execute_and_capture_output(fetch_cmd_builder.extract()); - Checks::check_exit(VCPKG_LINE_INFO, - fetch_output.exit_code == 0, - "Failed to update refs on temporary vcpkg repository.\n%s\n", - fetch_output.output); - } - - if (!fs.exists(work_tree)) - { - fs.create_directories(work_tree, VCPKG_LINE_INFO); - } - - // git checkout {tree_object} . - System::CmdLineBuilder checkout_cmd_builder = git_cmd_builder(paths, dot_git_dir, work_tree) - .string_arg("checkout") - .string_arg(git_object) - .string_arg("."); - const auto checkout_output = System::cmd_execute_and_capture_output(checkout_cmd_builder.extract()); - Checks::check_exit(VCPKG_LINE_INFO, checkout_output.exit_code == 0, "Failed to checkout %s", git_object); - - const auto& containing_folder = destination.parent_path(); - if (!fs.exists(containing_folder)) - { - fs.create_directories(containing_folder, VCPKG_LINE_INFO); - } - - std::error_code ec; - fs.rename_or_copy(work_tree, destination, ".tmp", ec); - fs.remove_all(work_tree, VCPKG_LINE_INFO); - if (ec) - { - System::printf(System::Color::error, - "Error: Couldn't move checked out files from %s to destination %s", - fs::u8string(work_tree), - fs::u8string(destination)); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - fs::path VcpkgPaths::git_checkout_baseline(Files::Filesystem& fs, StringView commit_sha) const + ExpectedS VcpkgPaths::git_checkout_baseline(StringView commit_sha) const { + Files::Filesystem& fs = get_filesystem(); const fs::path destination_parent = this->baselines_output / fs::u8path(commit_sha); - const fs::path destination = destination_parent / fs::u8path("baseline.json"); + fs::path destination = destination_parent / fs::u8path("baseline.json"); if (!fs.exists(destination)) { + const fs::path destination_tmp = destination_parent / fs::u8path("baseline.json.tmp"); auto treeish = Strings::concat(commit_sha, ":port_versions/baseline.json"); auto maybe_contents = git_show(treeish, this->root / fs::u8path(".git")); if (auto contents = maybe_contents.get()) { - fs.create_directories(destination_parent, VCPKG_LINE_INFO); - fs.write_contents(destination, *contents, VCPKG_LINE_INFO); + std::error_code ec; + fs.create_directories(destination_parent, ec); + if (ec) + { + return {Strings::format( + "Error: while checking out baseline %s\nError: while creating directories %s: %s", + commit_sha, + fs::u8string(destination_parent), + ec.message()), + expected_right_tag}; + } + fs.write_contents(destination_tmp, *contents, ec); + if (ec) + { + return {Strings::format("Error: while checking out baseline %s\nError: while writing %s: %s", + commit_sha, + fs::u8string(destination_tmp), + ec.message()), + expected_right_tag}; + } + fs.rename(destination_tmp, destination, ec); + if (ec) + { + return {Strings::format("Error: while checking out baseline %s\nError: while renaming %s to %s: %s", + commit_sha, + fs::u8string(destination_tmp), + fs::u8string(destination), + ec.message()), + expected_right_tag}; + } } else { - Checks::exit_with_message( - VCPKG_LINE_INFO, "Error: while checking out baseline '%s':\n%s", treeish, maybe_contents.error()); + return { + Strings::format("Error: while checking out baseline '%s':\n%s", treeish, maybe_contents.error()), + expected_right_tag}; } } return destination; } - fs::path VcpkgPaths::git_checkout_port(Files::Filesystem& fs, StringView port_name, StringView git_tree) const + ExpectedS VcpkgPaths::git_checkout_port(StringView port_name, + StringView git_tree, + const fs::path& dot_git_dir) const { - /* Clone a new vcpkg repository instance using the local instance as base. - * - * The `--git-dir` directory will store all the Git metadata files, - * and the `--work-tree` is the directory where files will be checked out. + /* Check out a git tree into the versioned port recipes folder * * Since we are checking a git tree object, all files will be checked out to the root of `work-tree`. * Because of that, it makes sense to use the git hash as the name for the directory. */ - const fs::path& local_repo = this->root; - fs::path destination = this->versions_output / fs::u8path(git_tree) / fs::u8path(port_name); + Files::Filesystem& fs = get_filesystem(); + fs::path destination = this->versions_output / fs::u8path(port_name) / fs::u8path(git_tree); + if (fs.exists(destination)) + { + return destination; + } + + const fs::path destination_tmp = + this->versions_output / fs::u8path(port_name) / fs::u8path(Strings::concat(git_tree, ".tmp")); + const fs::path destination_tar = + this->versions_output / fs::u8path(port_name) / fs::u8path(Strings::concat(git_tree, ".tar")); +#define PRELUDE "Error: while checking out port ", port_name, " with git tree ", git_tree, "\n" + std::error_code ec; + fs::path failure_point; + fs.remove_all(destination_tmp, ec, failure_point); + if (ec) + { + return {Strings::concat(PRELUDE, "Error: while removing ", fs::u8string(failure_point), ": ", ec.message()), + expected_right_tag}; + } + fs.create_directories(destination_tmp, ec); + if (ec) + { + return { + Strings::concat( + PRELUDE, "Error: while creating directories ", fs::u8string(destination_tmp), ": ", ec.message()), + expected_right_tag}; + } + + // All git commands are run with: --git-dir={dot_git_dir} --work-tree={work_tree_temp} + // git clone --no-checkout --local {vcpkg_root} {dot_git_dir} + System::CmdLineBuilder tar_cmd_builder = git_cmd_builder(*this, dot_git_dir, dot_git_dir) + .string_arg("archive") + .string_arg(git_tree) + .string_arg("-o") + .path_arg(destination_tar); + const auto tar_output = System::cmd_execute_and_capture_output(tar_cmd_builder); + if (tar_output.exit_code != 0) + { + return {Strings::concat(PRELUDE, "Error: Failed to tar port directory\n", tar_output.output), + expected_right_tag}; + } + + System::CmdLineBuilder extract_cmd_builder; + extract_cmd_builder.string_arg("cd") + .path_arg(destination_tmp) + .ampersand() + .path_arg(this->get_tool_exe(Tools::CMAKE)) + .string_arg("-E") + .string_arg("tar") + .string_arg("xf") + .path_arg(destination_tar); + + auto cmdline = extract_cmd_builder.extract(); +#ifdef WIN32 + // Invoke through `cmd` to support `&&` + cmdline.insert(0, "cmd /c \""); + cmdline.push_back('"'); +#endif - if (!fs.exists(destination / "CONTROL") && !fs.exists(destination / "vcpkg.json")) + const auto extract_output = System::cmd_execute_and_capture_output(cmdline); + if (extract_output.exit_code != 0) + { + return {Strings::concat(PRELUDE, "Error: Failed to extract port directory\n", extract_output.output), + expected_right_tag}; + } + fs.remove(destination_tar, ec); + if (ec) { - git_checkout_object( - *this, git_tree, local_repo, destination, this->versions_dot_git_dir, this->versions_work_tree); + return { + Strings::concat(PRELUDE, "Error: while removing ", fs::u8string(destination_tar), ": ", ec.message()), + expected_right_tag}; } + fs.rename(destination_tmp, destination, ec); + if (ec) + { + return {Strings::concat(PRELUDE, + "Error: while renaming ", + fs::u8string(destination_tmp), + " to ", + fs::u8string(destination), + ": ", + ec.message()), + expected_right_tag}; + } + return destination; +#undef PRELUDE } Optional VcpkgPaths::get_manifest() const From 904ae01855ff3b57f50b1a1a75ea666391688d78 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Mon, 11 Jan 2021 00:08:15 +0000 Subject: [PATCH 2/8] [vcpkg] Add initial versioning documentation and rename x-default-baseline to builtin-baseline --- docs/users/manifests.md | 56 +++++++++++- docs/users/versioning.md | 84 ++++++++++++++++++ .../end-to-end-tests-prelude.ps1 | 2 +- .../default-baseline-1/vcpkg.json | 2 +- .../default-baseline-2/vcpkg.json | 2 +- scripts/vcpkg.schema.json | 4 + toolsrc/include/vcpkg/base/expected.h | 9 +- toolsrc/include/vcpkg/sourceparagraph.h | 1 + toolsrc/include/vcpkg/vcpkgpaths.h | 6 +- toolsrc/include/vcpkg/versions.h | 3 +- toolsrc/src/vcpkg-test/dependencies.cpp | 25 +++--- toolsrc/src/vcpkg-test/manifests.cpp | 84 ++++++++++-------- toolsrc/src/vcpkg/help.cpp | 66 +++++++++++++- toolsrc/src/vcpkg/install.cpp | 17 +++- toolsrc/src/vcpkg/paragraphs.cpp | 9 +- toolsrc/src/vcpkg/registries.cpp | 27 ++++-- toolsrc/src/vcpkg/sourceparagraph.cpp | 88 ++++++++++++------- toolsrc/src/vcpkg/vcpkgpaths.cpp | 43 +++++++-- 18 files changed, 412 insertions(+), 116 deletions(-) create mode 100644 docs/users/versioning.md diff --git a/docs/users/manifests.md b/docs/users/manifests.md index 427d336c340caf..45ef9bd080b34b 100644 --- a/docs/users/manifests.md +++ b/docs/users/manifests.md @@ -64,6 +64,12 @@ Additionally, the `"port-version"` field is used by registries of packages, as a way to version "the package gotten from `vcpkg install`" differently from the upstream package version. You shouldn't need to worry about this at all. +#### Additional version fields + +**Experimental behind the `versions` feature flag** + +See [versioning.md](versioning.md#version%20schemes) for additional version types. + ### `"description"` This is where you describe your project. Give it a good description to help in searching for it! @@ -71,6 +77,14 @@ This can be a single string, or it can be an array of strings; in the latter case, the first string is treated as a summary, while the remaining strings are treated as the full description. +### `"builtin-baseline"` + +**Experimental behind the `versions` feature flag** + +This field indicates the commit of vcpkg which provides global minimum version information for your manifest. It is required for top-level manifest files using versioning. + +See also [versioning](versioning.md#builtin-baseline) for more semantic details. + ### `"dependencies"` This field lists all the dependencies you'll need to build your library (as well as any your dependents might need, @@ -131,6 +145,22 @@ The common identifiers are: although one can define their own. +#### `"version>="` + +**Experimental behind the `versions` feature flag** + +A minimum version constraint on the dependency. + +This field specifies the primary scheme text of the dependency and the optional `"port-version"` field specifies a minimum port version (if any). + +See also [versioning](versioning.md#constraints) for more semantic details. + +#### `"port-version"` + +**Experimental behind the `versions` feature flag** + +See `"version>="` above. + #### Example: ```json @@ -151,6 +181,26 @@ although one can define their own. } ``` +### `"overrides"` + +**Experimental behind the `versions` feature flag** + +This field enables version resolution to be ignored for certain dependencies and to use specific versions instead. + +See also [versioning](versioning.md#overrides) for more semantic details. + +#### Example: + +```json +{ + "overrides": [ + { + "name": "arrow", "version": "1.2.3", "port-version": 7 + } + ] +} +``` + ### `"supports"` If your project doesn't support common platforms, you can tell your users this with the `"supports"` field. @@ -180,8 +230,9 @@ and that's the `"default-features"` field, which is an array of feature names. "name": "libdb", "description": [ "An example database library.", - "Optionally uses one of CBOR, JSON, or CSV as a backend." + "Optionally can build with CBOR, JSON, or CSV as backends." ], + "$default-features-explanation": "Users using this library transitively will get all backends automatically", "default-features": [ "cbor", "csv", "json" ], "features": { "cbor": { @@ -189,8 +240,7 @@ and that's the `"default-features"` field, which is an array of feature names. "dependencies": [ { "$explanation": [ - "This is currently how you tell vcpkg that the cbor feature depends on the json feature of this package", - "We're looking into making this easier" + "This is how you tell vcpkg that the cbor feature depends on the json feature of this package" ], "name": "libdb", "default-features": false, diff --git a/docs/users/versioning.md b/docs/users/versioning.md new file mode 100644 index 00000000000000..b7c256c6048923 --- /dev/null +++ b/docs/users/versioning.md @@ -0,0 +1,84 @@ +# Versioning + +**This feature is experimental and requires `--feature-flags=versions`** + +Versioning allows you to deterministically control the precise revisions of dependencies used by +your project from within your manifest file. + +## Version schemes + +### Schemes +Versions in vcpkg come in three primary flavors: + +#### version +A dot-separated sequence of numbers (1.2.3.4) + +#### version-date +A date (2021-01-01.5) + +#### version-semver +A Semantic Version 2.0 (2.1.0-rc2) + +See https://semver.org/ for a full specification. + +#### version-string +An exact, incomparable version (strawberry) + +### Port Versions +Each version additionally has a "port-version" which is a natural number. When rendered as text, the +port version (if nonzero) is added as a suffix to the primary version text separated by a hash (#). +Port-versions are sorted lexographically after the primary field, for example: + + 1.0.0 < 1.0.0#1 < 1.0.1 < 1.0.1#5 < 2.0.0 + +## Constraints + +Manifests can place three kinds of constraints upon the versions used: + +### builtin-baseline +The baseline references a commit within the vcpkg repository that +establishes a minimum version on every dependency in the graph. If +no other constraints are specified (directly or transitively), +then the version from the baseline of the top level manifest will +be used. + +You can get the current commit of your vcpkg instance either by adding an empty `"builtin-baseline"` field, installing, and examining the error message or by running `git rev-parse HEAD` in the root of the vcpkg instance. + +Baselines provide stability and ease of development for top-level manifest files. They are not considered from ports consumed as a dependency. If a minimum version constraint is required during transitive version resolution, the port should use `version>=`. + +### version>= +Within the "dependencies" field, each dependency can have a +minimum constraint listed. These minimum constraints will be used +when transitively depending upon this library. A minimum +port-version can additionally be specified with the "port-version" +field in the same object. + +### overrides +When used as the top-level manifest (such as when running `vcpkg +install` in the directory), overrides allow a manifest to +short-circuit dependency resolution and specify exactly the +version to use. These can be used to handle version conflicts, +such as with `version-string` dependencies. + +Overrides are not considered from ports consumed as a dependency. + +## Example top-level manifest: +```json +{ + "name": "example", + "version": "1.0", + "builtin-baseline": "a14a6bcb27287e3ec138dba1b948a0cdbc337a3a", + "dependencies": [ + { "name": "zlib", "version>=": "1.2.11", "port-version": 8 }, + "rapidjson" + ], + "overrides": [ + { "name": "rapidjson", "version": "2020-09-14" } + ] +} +``` +See also the [manifest documentation](manifests.md) for more syntax information. + +## Original Specification + +See also the [original specification](https://github.com/vicroms/vcpkg/blob/versioning-spec/docs/specifications/versioning.md) diff --git a/scripts/azure-pipelines/end-to-end-tests-prelude.ps1 b/scripts/azure-pipelines/end-to-end-tests-prelude.ps1 index 0c675e9bf23506..e256d658f4068d 100644 --- a/scripts/azure-pipelines/end-to-end-tests-prelude.ps1 +++ b/scripts/azure-pipelines/end-to-end-tests-prelude.ps1 @@ -12,7 +12,7 @@ $commonArgs = @( "--x-buildtrees-root=$buildtreesRoot", "--x-install-root=$installRoot", "--x-packages-root=$packagesRoot", - "--overlay-ports=scripts/e2e_ports/overlays" + "--overlay-ports=$PSScriptRoot/../e2e_ports/overlays" ) $Script:CurrentTest = 'unassigned' diff --git a/scripts/testing/version-files/default-baseline-1/vcpkg.json b/scripts/testing/version-files/default-baseline-1/vcpkg.json index f6d902393d3271..51e65c82ca1942 100644 --- a/scripts/testing/version-files/default-baseline-1/vcpkg.json +++ b/scripts/testing/version-files/default-baseline-1/vcpkg.json @@ -1,7 +1,7 @@ { "name": "default-baseline-test", "version-string": "0", - "$x-default-baseline": "fca18ba3572f8aebe3b8158c359db62a7e26134e", + "builtin-baseline": "fca18ba3572f8aebe3b8158c359db62a7e26134e", "dependencies": [ "zlib" ] diff --git a/scripts/testing/version-files/default-baseline-2/vcpkg.json b/scripts/testing/version-files/default-baseline-2/vcpkg.json index 101d6f752c3061..a5ca157f25b9a4 100644 --- a/scripts/testing/version-files/default-baseline-2/vcpkg.json +++ b/scripts/testing/version-files/default-baseline-2/vcpkg.json @@ -1,7 +1,7 @@ { "name": "default-baseline-test-2", "version-string": "0", - "$x-default-baseline": "cbd5a68012471f820b7cf28d618199b4a4d89c58", + "builtin-baseline": "cbd5a68012471f820b7cf28d618199b4a4d89c58", "dependencies": [ "zlib" ] diff --git a/scripts/vcpkg.schema.json b/scripts/vcpkg.schema.json index 9df3620869354b..7402e51fb27004 100644 --- a/scripts/vcpkg.schema.json +++ b/scripts/vcpkg.schema.json @@ -256,6 +256,10 @@ "description": "An SPDX license expression at version 3.9.", "type": "string" }, + "builtin-baseline": { + "description": "A vcpkg repository commit for version control.", + "type": "string" + }, "dependencies": { "description": "Dependencies that are always required.", "type": "array", diff --git a/toolsrc/include/vcpkg/base/expected.h b/toolsrc/include/vcpkg/base/expected.h index 13e7b4bcbf61d3..20c23f07730780 100644 --- a/toolsrc/include/vcpkg/base/expected.h +++ b/toolsrc/include/vcpkg/base/expected.h @@ -226,11 +226,10 @@ namespace vcpkg void exit_if_error(const LineInfo& line_info) const { // This is used for quick value_or_exit() calls, so always put line_info in the error message. - Checks::check_exit(line_info, - !m_s.has_error(), - "Failed at [%s] with message:\n%s", - line_info.to_string(), - m_s.to_string()); + if (m_s.has_error()) + { + Checks::exit_with_message(line_info, "Failed at [%s] with message:\n%s", line_info, m_s.to_string()); + } } ErrorHolder m_s; diff --git a/toolsrc/include/vcpkg/sourceparagraph.h b/toolsrc/include/vcpkg/sourceparagraph.h index 50417b8adc4061..33be331e2940a9 100644 --- a/toolsrc/include/vcpkg/sourceparagraph.h +++ b/toolsrc/include/vcpkg/sourceparagraph.h @@ -68,6 +68,7 @@ namespace vcpkg std::vector overrides; std::vector default_features; std::string license; // SPDX license expression + Optional builtin_baseline; Type type; PlatformExpression::Expr supports_expression; diff --git a/toolsrc/include/vcpkg/vcpkgpaths.h b/toolsrc/include/vcpkg/vcpkgpaths.h index 737389704964c1..670fd7eeb6bfa2 100644 --- a/toolsrc/include/vcpkg/vcpkgpaths.h +++ b/toolsrc/include/vcpkg/vcpkgpaths.h @@ -118,8 +118,12 @@ namespace vcpkg const std::string& get_tool_version(const std::string& tool) const; // Git manipulation in the vcpkg directory + ExpectedS get_current_git_sha() const; + std::string get_current_git_sha_message() const; ExpectedS git_checkout_baseline(StringView commit_sha) const; - ExpectedS git_checkout_port(StringView port_name, StringView git_tree, const fs::path& dot_git_dir) const; + ExpectedS git_checkout_port(StringView port_name, + StringView git_tree, + const fs::path& dot_git_dir) const; ExpectedS git_show(const std::string& treeish, const fs::path& dot_git_dir) const; ExpectedS>> git_get_local_port_treeish_map() const; diff --git a/toolsrc/include/vcpkg/versions.h b/toolsrc/include/vcpkg/versions.h index 19b5546eaf2c10..b26c90dfbaf6be 100644 --- a/toolsrc/include/vcpkg/versions.h +++ b/toolsrc/include/vcpkg/versions.h @@ -81,8 +81,7 @@ namespace vcpkg::Versions enum class Type { None, - Minimum, - Exact + Minimum }; }; } diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp index d4f03d0b6cf7e0..8f936abc38e17f 100644 --- a/toolsrc/src/vcpkg-test/dependencies.cpp +++ b/toolsrc/src/vcpkg-test/dependencies.cpp @@ -335,7 +335,7 @@ TEST_CASE ("basic version install scheme baseline missing success", "[versionpla bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "2"}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2"}}, }, {}, toplevel_spec())); @@ -375,7 +375,7 @@ TEST_CASE ("version string baseline agree", "[versionplan]") MockCMakeVarProvider var_provider; auto install_plan = create_versioned_install_plan( - vp, bp, var_provider, {Dependency{"a", {}, {}, {Constraint::Type::Exact, "2"}}}, {}, toplevel_spec()); + vp, bp, var_provider, {Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2"}}}, {}, toplevel_spec()); REQUIRE(install_plan.has_value()); } @@ -396,7 +396,7 @@ TEST_CASE ("version install scheme baseline conflict", "[versionplan]") bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "3"}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "3"}}, }, {}, toplevel_spec()); @@ -421,7 +421,7 @@ TEST_CASE ("version install string port version", "[versionplan]") bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 1}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2", 1}}, }, {}, toplevel_spec())); @@ -447,7 +447,7 @@ TEST_CASE ("version install string port version 2", "[versionplan]") bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 0}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2", 0}}, }, {}, toplevel_spec())); @@ -463,10 +463,10 @@ TEST_CASE ("version install transitive string", "[versionplan]") MockVersionedPortfileProvider vp; vp.emplace("a", {"2", 0}).source_control_file->core_paragraph->dependencies = { - Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Exact, "1"}}, + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "1"}}, }; vp.emplace("a", {"2", 1}).source_control_file->core_paragraph->dependencies = { - Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Exact, "2"}}, + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "2"}}, }; vp.emplace("b", {"1", 0}); vp.emplace("b", {"2", 0}); @@ -478,7 +478,7 @@ TEST_CASE ("version install transitive string", "[versionplan]") bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 1}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2", 1}}, }, {}, toplevel_spec())); @@ -1006,7 +1006,7 @@ TEST_CASE ("version install scheme change in port version", "[versionplan]") { MockVersionedPortfileProvider vp; vp.emplace("a", {"2", 0}).source_control_file->core_paragraph->dependencies = { - Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Exact, "1"}}, + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "1"}}, }; vp.emplace("a", {"2", 1}).source_control_file->core_paragraph->dependencies = { Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "1", 1}}, @@ -1026,7 +1026,7 @@ TEST_CASE ("version install scheme change in port version", "[versionplan]") bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 1}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2", 1}}, }, {}, toplevel_spec())); @@ -1045,7 +1045,7 @@ TEST_CASE ("version install scheme change in port version", "[versionplan]") bp, var_provider, { - Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 0}}, + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "2", 0}}, }, {}, toplevel_spec())); @@ -1305,7 +1305,8 @@ TEST_CASE ("version install transitive overrides", "[versionplan]") MockVersionedPortfileProvider vp; vp.emplace("b", {"1", 0}, Scheme::Relaxed) - .source_control_file->core_paragraph->dependencies.push_back({"c", {}, {}, {Constraint::Type::Exact, "2", 1}}); + .source_control_file->core_paragraph->dependencies.push_back( + {"c", {}, {}, {Constraint::Type::Minimum, "2", 1}}); vp.emplace("b", {"2", 0}, Scheme::Relaxed); vp.emplace("c", {"1", 0}, Scheme::String); vp.emplace("c", {"2", 1}, Scheme::String); diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp index f03caf3bf76bd7..a9ef95d8c1e734 100644 --- a/toolsrc/src/vcpkg-test/manifests.cpp +++ b/toolsrc/src/vcpkg-test/manifests.cpp @@ -62,6 +62,7 @@ TEST_CASE ("manifest construct minimum", "[manifests]") REQUIRE(pgh.core_paragraph->maintainers.empty()); REQUIRE(pgh.core_paragraph->description.empty()); REQUIRE(pgh.core_paragraph->dependencies.empty()); + REQUIRE(!pgh.core_paragraph->builtin_baseline.has_value()); REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_without_versioning)); } @@ -140,19 +141,6 @@ TEST_CASE ("manifest versioning", "[manifests]") TEST_CASE ("manifest constraints error hash", "[manifests]") { - test_parse_manifest(R"json({ - "name": "zlib", - "version-string": "abcd", - "dependencies": [ - { - "name": "b", - "version=": "5#1" - } - ] -} -)json", - true); - test_parse_manifest(R"json({ "name": "zlib", "version-string": "abcd", @@ -219,13 +207,9 @@ TEST_CASE ("manifest constraints", "[manifests]") std::string raw = R"json({ "name": "zlib", "version-string": "abcd", + "builtin-baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4", "dependencies": [ "a", - { - "name": "b", - "port-version": 12, - "version=": "5" - }, { "$extra": null, "name": "c" @@ -244,32 +228,17 @@ TEST_CASE ("manifest constraints", "[manifests]") REQUIRE(pgh.check_against_feature_flags({}, feature_flags_without_versioning)); REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning)); REQUIRE(Json::stringify(serialize_manifest(pgh), Json::JsonStyle::with_spaces(4)) == raw); - REQUIRE(pgh.core_paragraph->dependencies.size() == 4); + REQUIRE(pgh.core_paragraph->dependencies.size() == 3); REQUIRE(pgh.core_paragraph->dependencies[0].name == "a"); REQUIRE(pgh.core_paragraph->dependencies[0].constraint == DependencyConstraint{Versions::Constraint::Type::None, "", 0}); - REQUIRE(pgh.core_paragraph->dependencies[1].name == "b"); + REQUIRE(pgh.core_paragraph->dependencies[1].name == "c"); REQUIRE(pgh.core_paragraph->dependencies[1].constraint == - DependencyConstraint{Versions::Constraint::Type::Exact, "5", 12}); - REQUIRE(pgh.core_paragraph->dependencies[2].name == "c"); - REQUIRE(pgh.core_paragraph->dependencies[2].constraint == DependencyConstraint{Versions::Constraint::Type::None, "", 0}); - REQUIRE(pgh.core_paragraph->dependencies[3].name == "d"); - REQUIRE(pgh.core_paragraph->dependencies[3].constraint == + REQUIRE(pgh.core_paragraph->dependencies[2].name == "d"); + REQUIRE(pgh.core_paragraph->dependencies[2].constraint == DependencyConstraint{Versions::Constraint::Type::Minimum, "2018-09-01", 0}); - - test_parse_manifest(R"json({ - "name": "zlib", - "version-string": "abcd", - "dependencies": [ - { - "name": "d", - "version=": "2018-09-01", - "version>=": "2018-09-01" - } - ] - })json", - true); + REQUIRE(pgh.core_paragraph->builtin_baseline == "089fa4de7dca22c67dcab631f618d5cd0697c8d4"); test_parse_manifest(R"json({ "name": "zlib", @@ -284,6 +253,45 @@ TEST_CASE ("manifest constraints", "[manifests]") true); } +TEST_CASE ("manifest builtin-baseline", "[manifests]") +{ + SECTION ("valid baseline") + { + std::string raw = R"json({ + "name": "zlib", + "version-string": "abcd", + "builtin-baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4" +} +)json"; + auto m_pgh = test_parse_manifest(raw); + + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + REQUIRE(pgh.check_against_feature_flags({}, feature_flags_without_versioning)); + REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning)); + REQUIRE(pgh.core_paragraph->builtin_baseline.value_or("does not have a value") == + "089fa4de7dca22c67dcab631f618d5cd0697c8d4"); + } + + SECTION ("empty baseline") + { + std::string raw = R"json({ + "name": "zlib", + "version-string": "abcd", + "builtin-baseline": "" +} +)json"; + + auto m_pgh = test_parse_manifest(raw); + + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + REQUIRE(pgh.check_against_feature_flags({}, feature_flags_without_versioning)); + REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning)); + REQUIRE(pgh.core_paragraph->builtin_baseline.value_or("does not have a value") == ""); + } +} + TEST_CASE ("manifest overrides", "[manifests]") { std::tuple data[] = { diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index ded6592fbe90be..519400b2bf498b 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -47,7 +47,70 @@ namespace vcpkg::Help nullptr, }; - static constexpr std::array topics = {{ + static void help_topic_versioning(const VcpkgPaths&) + { + HelpTableFormatter tbl; + tbl.text("Versioning allows you to deterministically control the precise revisions of dependencies used by " + "your project from within your manifest file."); + tbl.blank(); + tbl.blank(); + tbl.text("** This feature is experimental and requires `--feature-flags=versions` **"); + tbl.blank(); + tbl.blank(); + tbl.header("Versions in vcpkg come in three primary flavors"); + tbl.format("version", "A dot-separated sequence of numbers (1.2.3.4)"); + tbl.format("version-date", "A date (2021-01-01.5)"); + tbl.format("version-semver", "A Semantic Version 2.0 (2.1.0-rc2)"); + tbl.format("version-string", "An exact, incomparable version (strawberry)"); + tbl.blank(); + tbl.text( + "Each version additionally has a \"port-version\" which is a natural number. When rendered as text, the " + "port version (if nonzero) is added as a suffix to the primary version text separated by a hash (#). " + "Port-versions are sorted lexographically after the primary field, for example:"); + tbl.blank(); + tbl.blank(); + tbl.text(" 1.0.0 < 1.0.0#1 < 1.0.1 < 1.0.1#5 < 2.0.0"); + tbl.blank(); + tbl.blank(); + tbl.header("Manifests can place three kinds of constraints upon the versions used"); + tbl.format("builtin-baseline", + "The baseline references a commit within the vcpkg repository that establishes a minimum version on " + "every dependency in the graph. If no other constraints are specified (directly or transitively), " + "then the version from the baseline of the top level manifest will be used. Baselines of transitive " + "dependencies are ignored."); + tbl.blank(); + tbl.format("version>=", + "Within the \"dependencies\" field, each dependency can have a minimum constraint listed. These " + "minimum constraints will be used when transitively depending upon this library. A minimum " + "port-version can be specified with the \"port-version\" field."); + tbl.blank(); + tbl.format( + "overrides", + "When used as the top-level manifest (such as when running `vcpkg install` in the directory), overrides " + "allow a manifest to short-circuit dependency resolution and specify exactly the version to use. These can " + "be used to handle version conflicts, such as with `version-string` dependencies. They will not be " + "considered when transitively depended upon."); + tbl.blank(); + tbl.text("Example manifest:"); + tbl.blank(); + tbl.text(R"({ + "name": "example", + "version": "1.0", + "builtin-baseline": "a14a6bcb27287e3ec138dba1b948a0cdbc337a3a", + "dependencies": [ + { "name": "zlib", "version>=": "1.2.11", "port-version": 8 }, + "rapidjson" + ], + "overrides": [ + { "name": "rapidjson", "version": "2020-09-14" } + ] +})"); + System::print2(tbl.m_str, + "\nExtended documentation is available at " + "https://github.com/Microsoft/vcpkg/tree/master/docs/users/versioning.md\n"); + } + + static constexpr std::array topics = {{ {"binarycaching", help_topic_binary_caching}, {"create", command_topic_fn}, {"depend-info", command_topic_fn}, @@ -63,6 +126,7 @@ namespace vcpkg::Help {"search", command_topic_fn}, {"topics", help_topics}, {"triplet", help_topic_valid_triplet}, + {"versioning", help_topic_versioning}, }}; static void help_topics(const VcpkgPaths&) diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 51c7b8289fed93..2c9c41d2d74ffd 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -848,10 +849,20 @@ namespace vcpkg::Install { auto verprovider = PortFileProvider::make_versioned_portfile_provider(paths); auto baseprovider = PortFileProvider::make_baseline_provider(paths); - if (auto p_baseline = manifest_scf.core_paragraph->extra_info.get("$x-default-baseline")) + if (auto p_baseline = manifest_scf.core_paragraph->builtin_baseline.get()) { - paths.get_configuration().registry_set.experimental_set_builtin_registry_baseline( - p_baseline->string()); + if (p_baseline->size() != 40 || !std::all_of(p_baseline->begin(), p_baseline->end(), [](char ch) { + return (ch >= 'a' || ch <= 'f') || Parse::ParserBase::is_ascii_digit(ch); + })) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, + "Error: the top-level builtin-baseline (%s) was not a valid commit sha: " + "expected 40 lowercase hexadecimal characters.\n%s\n", + *p_baseline, + paths.get_current_git_sha_message()); + } + paths.get_configuration().registry_set.experimental_set_builtin_registry_baseline(*p_baseline); } auto oprovider = PortFileProvider::make_overlay_provider(paths, args.overlay_ports); diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp index 32103c76b9e6f6..2d28c49d705d6c 100644 --- a/toolsrc/src/vcpkg/paragraphs.cpp +++ b/toolsrc/src/vcpkg/paragraphs.cpp @@ -373,7 +373,14 @@ namespace vcpkg::Paragraphs auto error_info = std::make_unique(); error_info->name = fs::u8string(path.filename()); - error_info->error = "Failed to find either a CONTROL file or vcpkg.json file."; + if (fs.exists(path)) + { + error_info->error = "Failed to find either a CONTROL file or vcpkg.json file."; + } + else + { + error_info->error = "The port directory (" + fs::u8string(path) + ") does not exist"; + } return error_info; } diff --git a/toolsrc/src/vcpkg/registries.cpp b/toolsrc/src/vcpkg/registries.cpp index 5bde3e1640d97d..e89912c6615d2c 100644 --- a/toolsrc/src/vcpkg/registries.cpp +++ b/toolsrc/src/vcpkg/registries.cpp @@ -258,18 +258,20 @@ namespace if (baseline_identifier == "default") { - return Strings::format("Couldn't find explicitly specified baseline `\"default\"` in the baseline file.", - baseline_identifier); + return Strings::format( + "Error: Couldn't find explicitly specified baseline `\"default\"` in the baseline file.", + baseline_identifier); } // attempt to check out the baseline: auto maybe_path = paths.git_checkout_baseline(baseline_identifier); if (!maybe_path.has_value()) { - return Strings::format("Couldn't find explicitly specified baseline `\"%s\"` in the baseline file, " - "and there was no baseline at that commit or the commit didn't exist.\n%s", + return Strings::format("Error: Couldn't find explicitly specified baseline `\"%s\"` in the baseline file, " + "and there was no baseline at that commit or the commit didn't exist.\n%s\n%s", baseline_identifier, - maybe_path.error()); + maybe_path.error(), + paths.get_current_git_sha_message()); } res_baseline = load_baseline_versions(paths, *maybe_path.get()); @@ -283,7 +285,7 @@ namespace return std::move(*p); } - return Strings::format("Couldn't find explicitly specified baseline `\"%s\"` in the baseline " + return Strings::format("Error: Couldn't find explicitly specified baseline `\"%s\"` in the baseline " "file, and the `\"default\"` baseline does not exist at that commit.", baseline_identifier); } @@ -419,7 +421,16 @@ namespace if (it == git_entry->port_versions.end()) { return { - Strings::concat("Error: No version entry for ", git_entry->port_name, " at version ", version, "."), + Strings::concat("Error: No version entry for ", + git_entry->port_name, + " at version ", + version, + ". This may be fixed by updating vcpkg to the latest master via `git " + "pull`.\nAvailable versions:\n", + Strings::join("", + git_entry->port_versions, + [](const VersionT& v) { return Strings::concat(" ", v, "\n"); }), + "\nSee `vcpkg help versioning` for more information."), expected_right_tag}; } @@ -923,7 +934,7 @@ namespace vcpkg if (!default_registry_is_builtin || registries_.size() != 0) { System::print2(System::Color::warning, - "Warning: when using the registries feature, one should not use `\"$x-default-baseline\"` " + "Warning: when using the registries feature, one should not use `\"builtin-baseline\"` " "to set the baseline.\n", " Instead, use the \"baseline\" field of the registry.\n"); } diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp index f80a1494e5d963..5bcfbb0bc0ec0f 100644 --- a/toolsrc/src/vcpkg/sourceparagraph.cpp +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -431,7 +431,6 @@ namespace vcpkg constexpr static StringLiteral DEFAULT_FEATURES = "default-features"; constexpr static StringLiteral PLATFORM = "platform"; constexpr static StringLiteral PORT_VERSION = "port-version"; - constexpr static StringLiteral VERSION_EQ = "version="; constexpr static StringLiteral VERSION_GE = "version>="; virtual Span valid_fields() const override @@ -442,7 +441,6 @@ namespace vcpkg DEFAULT_FEATURES, PLATFORM, PORT_VERSION, - VERSION_EQ, VERSION_GE, }; @@ -490,30 +488,19 @@ namespace vcpkg static auto version_deserializer = make_version_deserializer("a version"); - auto has_eq_constraint = - r.optional_object_field(obj, VERSION_EQ, dep.constraint.value, *version_deserializer); auto has_ge_constraint = r.optional_object_field(obj, VERSION_GE, dep.constraint.value, *version_deserializer); auto has_port_ver = r.optional_object_field( obj, PORT_VERSION, dep.constraint.port_version, Json::NaturalNumberDeserializer::instance); - if (has_eq_constraint) - { - dep.constraint.type = Versions::Constraint::Type::Exact; - if (has_ge_constraint) - { - r.add_generic_error(type_name(), "cannot have both exact and minimum constraints simultaneously"); - } - } - else if (has_ge_constraint) + if (has_ge_constraint) { dep.constraint.type = Versions::Constraint::Type::Minimum; } else if (has_port_ver) // does not have a primary constraint { - r.add_generic_error( - type_name(), - "\"port-version\" cannot be used without a primary constraint (\"version=\" or \"version>=\")"); + r.add_generic_error(type_name(), + "\"port-version\" cannot be used without a primary constraint (\"version>=\")"); } return dep; @@ -540,7 +527,6 @@ namespace vcpkg constexpr StringLiteral DependencyDeserializer::FEATURES; constexpr StringLiteral DependencyDeserializer::DEFAULT_FEATURES; constexpr StringLiteral DependencyDeserializer::PLATFORM; - constexpr StringLiteral DependencyDeserializer::VERSION_EQ; constexpr StringLiteral DependencyDeserializer::VERSION_GE; constexpr StringLiteral DependencyDeserializer::PORT_VERSION; @@ -860,6 +846,21 @@ namespace vcpkg }; LicenseExpressionDeserializer LicenseExpressionDeserializer::instance; + struct BaselineCommitDeserializer final : Json::IDeserializer + { + virtual StringView type_name() const override { return "a vcpkg repository commit"; } + + virtual Optional visit_string(Json::Reader&, StringView s) override + { + // We allow non-sha strings here to allow the core vcpkg code to provide better error + // messages including the current git commit + return s.to_string(); + } + + static BaselineCommitDeserializer instance; + }; + BaselineCommitDeserializer BaselineCommitDeserializer::instance; + struct ManifestDeserializer : Json::IDeserializer> { virtual StringView type_name() const override { return "a manifest"; } @@ -876,6 +877,7 @@ namespace vcpkg constexpr static StringLiteral DEFAULT_FEATURES = "default-features"; constexpr static StringLiteral SUPPORTS = "supports"; constexpr static StringLiteral OVERRIDES = "overrides"; + constexpr static StringLiteral BUILTIN_BASELINE = "builtin-baseline"; virtual Span valid_fields() const override { @@ -892,6 +894,7 @@ namespace vcpkg DEFAULT_FEATURES, SUPPORTS, OVERRIDES, + BUILTIN_BASELINE, }; static const auto t = Util::Vectors::concat(schemed_deserializer_fields(), u); @@ -917,9 +920,9 @@ namespace vcpkg static Json::StringDeserializer url_deserializer{"a url"}; - constexpr static StringView type_name = "vcpkg.json"; + constexpr static StringView inner_type_name = "vcpkg.json"; DependencyOverrideDeserializer::visit_impl( - type_name, r, obj, spgh->name, spgh->version, spgh->version_scheme, spgh->port_version); + inner_type_name, r, obj, spgh->name, spgh->version, spgh->version_scheme, spgh->port_version); r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer::instance); r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer::instance); @@ -933,8 +936,12 @@ namespace vcpkg if (obj.contains(DEV_DEPENDENCIES)) { - System::print2(System::Color::error, DEV_DEPENDENCIES, " are not yet supported"); - Checks::exit_fail(VCPKG_LINE_INFO); + r.add_generic_error(type_name(), DEV_DEPENDENCIES, " are not yet supported"); + } + std::string baseline; + if (r.optional_object_field(obj, BUILTIN_BASELINE, baseline, BaselineCommitDeserializer::instance)) + { + spgh->builtin_baseline = std::move(baseline); } r.optional_object_field(obj, SUPPORTS, spgh->supports_expression, PlatformExprDeserializer::instance); @@ -1007,6 +1014,7 @@ namespace vcpkg Optional SourceControlFile::check_against_feature_flags(const fs::path& origin, const FeatureFlagSettings& flags) const { + static constexpr StringLiteral s_extended_help = "See `vcpkg help versioning` for more information."; if (!flags.versions) { if (core_paragraph->version_scheme != Versions::Scheme::String) @@ -1014,7 +1022,8 @@ namespace vcpkg return Strings::concat(fs::u8string(origin), " was rejected because it uses a non-string version scheme and the `", VcpkgCmdArguments::VERSIONS_FEATURE, - "` feature flag is disabled.\nThis can be fixed by using \"version-string\"."); + "` feature flag is disabled.\nThis can be fixed by using \"version-string\".\n", + s_extended_help); } auto check_deps = [&](View deps) -> Optional { @@ -1022,11 +1031,12 @@ namespace vcpkg { if (dep.constraint.type != Versions::Constraint::Type::None) { - return Strings::concat(fs::u8string(origin), - " was rejected because it uses constraints and the `", - VcpkgCmdArguments::VERSIONS_FEATURE, - "` feature flag is disabled.\nThis can be fixed by removing uses of " - "\"version>=\" and \"version=\"."); + return Strings::concat( + fs::u8string(origin), + " was rejected because it uses constraints and the `", + VcpkgCmdArguments::VERSIONS_FEATURE, + "` feature flag is disabled.\nThis can be fixed by removing uses of \"version>=\".\n", + s_extended_help); } } return nullopt; @@ -1044,7 +1054,18 @@ namespace vcpkg return Strings::concat(fs::u8string(origin), " was rejected because it uses overrides and the `", VcpkgCmdArguments::VERSIONS_FEATURE, - "` feature flag is disabled.\nThis can be fixed by removing \"overrides\"."); + "` feature flag is disabled.\nThis can be fixed by removing \"overrides\".\n", + s_extended_help); + } + + if (core_paragraph->builtin_baseline.has_value()) + { + return Strings::concat( + fs::u8string(origin), + " was rejected because it uses builtin-baseline and the `", + VcpkgCmdArguments::VERSIONS_FEATURE, + "` feature flag is disabled.\nThis can be fixed by removing \"builtin-baseline\".\n", + s_extended_help); } } return nullopt; @@ -1256,11 +1277,7 @@ namespace vcpkg Json::Value::integer(dep.constraint.port_version)); } - if (dep.constraint.type == Versions::Constraint::Type::Exact) - { - dep_obj.insert(DependencyDeserializer::VERSION_EQ, Json::Value::string(dep.constraint.value)); - } - else if (dep.constraint.type == Versions::Constraint::Type::Minimum) + if (dep.constraint.type == Versions::Constraint::Type::Minimum) { dep_obj.insert(DependencyDeserializer::VERSION_GE, Json::Value::string(dep.constraint.value)); } @@ -1302,6 +1319,11 @@ namespace vcpkg serialize_optional_string(obj, ManifestDeserializer::LICENSE, scf.core_paragraph->license); serialize_optional_string( obj, ManifestDeserializer::SUPPORTS, to_string(scf.core_paragraph->supports_expression)); + if (scf.core_paragraph->builtin_baseline.has_value()) + { + obj.insert(ManifestDeserializer::BUILTIN_BASELINE, + Json::Value::string(scf.core_paragraph->builtin_baseline.value_or_exit(VCPKG_LINE_INFO))); + } if (!scf.core_paragraph->dependencies.empty() || debug) { diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index d38b85ef993ce1..4f1341ad5629b7 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -545,6 +545,33 @@ If you wish to silence this error and use classic mode, you can: } } + ExpectedS VcpkgPaths::get_current_git_sha() const + { + auto cmd = git_cmd_builder(*this, this->root / fs::u8path(".git"), this->root); + cmd.string_arg("rev-parse").string_arg("HEAD"); + auto output = System::cmd_execute_and_capture_output(cmd); + if (output.exit_code != 0) + { + return {std::move(output.output), expected_right_tag}; + } + else + { + return {Strings::trim(std::move(output.output)), expected_left_tag}; + } + } + std::string VcpkgPaths::get_current_git_sha_message() const + { + auto maybe_cur_sha = get_current_git_sha(); + if (auto p_sha = maybe_cur_sha.get()) + { + return Strings::concat("The current commit is \"", *p_sha, '"'); + } + else + { + return Strings::concat("Failed to determine the current commit:\n", maybe_cur_sha.error()); + } + } + ExpectedS VcpkgPaths::git_show(const std::string& treeish, const fs::path& dot_git_dir) const { // All git commands are run with: --git-dir={dot_git_dir} --work-tree={work_tree_temp} @@ -655,9 +682,11 @@ If you wish to silence this error and use classic mode, you can: } else { - return { - Strings::format("Error: while checking out baseline '%s':\n%s", treeish, maybe_contents.error()), - expected_right_tag}; + return {Strings::format("Error: while checking out baseline '%s':\n%s\nThis may be fixed by updating " + "vcpkg to the latest master via `git pull`.", + treeish, + maybe_contents.error()), + expected_right_tag}; } } return destination; @@ -701,8 +730,6 @@ If you wish to silence this error and use classic mode, you can: expected_right_tag}; } - // All git commands are run with: --git-dir={dot_git_dir} --work-tree={work_tree_temp} - // git clone --no-checkout --local {vcpkg_root} {dot_git_dir} System::CmdLineBuilder tar_cmd_builder = git_cmd_builder(*this, dot_git_dir, dot_git_dir) .string_arg("archive") .string_arg(git_tree) @@ -716,7 +743,11 @@ If you wish to silence this error and use classic mode, you can: } System::CmdLineBuilder extract_cmd_builder; - extract_cmd_builder.string_arg("cd") + extract_cmd_builder + .string_arg("cd") +#ifdef WIN32 + .string_arg("/D") +#endif .path_arg(destination_tmp) .ampersand() .path_arg(this->get_tool_exe(Tools::CMAKE)) From 63ffdc3b461df9282f0fdd63b915d57caf68f6f4 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Mon, 11 Jan 2021 20:17:30 +0000 Subject: [PATCH 3/8] [vcpkg] Enable metrics for builtin-baseline & overrides --- toolsrc/src/vcpkg/install.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 2c9c41d2d74ffd..cd262ec0f0ae5e 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -849,8 +849,13 @@ namespace vcpkg::Install { auto verprovider = PortFileProvider::make_versioned_portfile_provider(paths); auto baseprovider = PortFileProvider::make_baseline_provider(paths); + if (!manifest_scf.core_paragraph->overrides.empty()) + { + Metrics::g_metrics.lock()->track_property("manifest_overrides", "defined"); + } if (auto p_baseline = manifest_scf.core_paragraph->builtin_baseline.get()) { + Metrics::g_metrics.lock()->track_property("manifest_baseline", "defined"); if (p_baseline->size() != 40 || !std::all_of(p_baseline->begin(), p_baseline->end(), [](char ch) { return (ch >= 'a' || ch <= 'f') || Parse::ParserBase::is_ascii_digit(ch); })) From 5e048464f063f91f0be0dceef9a39b88827c88ef Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Wed, 13 Jan 2021 19:22:48 +0000 Subject: [PATCH 4/8] [vcpkg] Address PR comments --- docs/users/versioning.md | 6 +++--- toolsrc/include/vcpkg/base/jsonreader.h | 2 +- toolsrc/src/vcpkg/help.cpp | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/users/versioning.md b/docs/users/versioning.md index b7c256c6048923..a247aef7503879 100644 --- a/docs/users/versioning.md +++ b/docs/users/versioning.md @@ -22,12 +22,12 @@ A Semantic Version 2.0 (2.1.0-rc2) See https://semver.org/ for a full specification. #### version-string -An exact, incomparable version (strawberry) +An exact, incomparable version (Vista) ### Port Versions -Each version additionally has a "port-version" which is a natural number. When rendered as text, the +Each version additionally has a "port-version" which is a nonnegative integer. When rendered as text, the port version (if nonzero) is added as a suffix to the primary version text separated by a hash (#). -Port-versions are sorted lexographically after the primary field, for example: +Port-versions are sorted lexographically after the primary version text, for example: 1.0.0 < 1.0.0#1 < 1.0.1 < 1.0.1#5 < 2.0.0 diff --git a/toolsrc/include/vcpkg/base/jsonreader.h b/toolsrc/include/vcpkg/base/jsonreader.h index da086fa028cf9f..4aca02998a9af2 100644 --- a/toolsrc/include/vcpkg/base/jsonreader.h +++ b/toolsrc/include/vcpkg/base/jsonreader.h @@ -278,7 +278,7 @@ namespace vcpkg::Json struct NaturalNumberDeserializer final : IDeserializer { - virtual StringView type_name() const override { return "a natural number"; } + virtual StringView type_name() const override { return "a nonnegative integer"; } virtual Optional visit_integer(Reader&, int64_t value) override { diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index 519400b2bf498b..a2785e5e218303 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -61,12 +61,11 @@ namespace vcpkg::Help tbl.format("version", "A dot-separated sequence of numbers (1.2.3.4)"); tbl.format("version-date", "A date (2021-01-01.5)"); tbl.format("version-semver", "A Semantic Version 2.0 (2.1.0-rc2)"); - tbl.format("version-string", "An exact, incomparable version (strawberry)"); + tbl.format("version-string", "An exact, incomparable version (Vista)"); tbl.blank(); - tbl.text( - "Each version additionally has a \"port-version\" which is a natural number. When rendered as text, the " - "port version (if nonzero) is added as a suffix to the primary version text separated by a hash (#). " - "Port-versions are sorted lexographically after the primary field, for example:"); + tbl.text("Each version additionally has a \"port-version\" which is a nonnegative integer. When rendered as " + "text, the port version (if nonzero) is added as a suffix to the primary version text separated by a " + "hash (#). Port-versions are sorted lexographically after the primary version text, for example:"); tbl.blank(); tbl.blank(); tbl.text(" 1.0.0 < 1.0.0#1 < 1.0.1 < 1.0.1#5 < 2.0.0"); From 661c132c70fe988de074e4abe96a9ebcd1d885f8 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Wed, 13 Jan 2021 19:50:15 +0000 Subject: [PATCH 5/8] [vcpkg] Add support for syntax in version>= --- toolsrc/src/vcpkg-test/manifests.cpp | 30 +++++++++++++++++++++- toolsrc/src/vcpkg/sourceparagraph.cpp | 37 ++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp index a38ed0b2ce68ca..7be7d398c4bd9e 100644 --- a/toolsrc/src/vcpkg-test/manifests.cpp +++ b/toolsrc/src/vcpkg-test/manifests.cpp @@ -158,8 +158,35 @@ TEST_CASE ("manifest versioning", "[manifests]") } } -TEST_CASE ("manifest constraints error hash", "[manifests]") +TEST_CASE ("manifest constraints hash", "[manifests]") { +#if 1 /*** SUPPORT_HASH_SYNTAX ***/ + auto p = unwrap(test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "dependencies": [ + { + "name": "d", + "version>=": "2018-09-01#1" + } + ] +})json")); + REQUIRE(p->core_paragraph->dependencies.at(0).constraint.value == "2018-09-01"); + REQUIRE(p->core_paragraph->dependencies.at(0).constraint.port_version == 1); + + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "dependencies": [ + { + "name": "d", + "version>=": "2018-09-01#1", + "port-version": 1 + } + ] +})json", + true); +#else /*** SUPPORT_HASH_SYNTAX ***/ test_parse_manifest(R"json({ "name": "zlib", "version-string": "abcd", @@ -171,6 +198,7 @@ TEST_CASE ("manifest constraints error hash", "[manifests]") ] })json", true); +#endif /*** SUPPORT_HASH_SYNTAX ***/ } TEST_CASE ("manifest overrides error hash", "[manifests]") diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp index c58695573cd0c0..4d275bebf8e1f5 100644 --- a/toolsrc/src/vcpkg/sourceparagraph.cpp +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -486,21 +486,50 @@ namespace vcpkg r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer::instance); - static auto version_deserializer = make_version_deserializer("a version"); + static Json::StringDeserializer version_deserializer("a version"); auto has_ge_constraint = - r.optional_object_field(obj, VERSION_GE, dep.constraint.value, *version_deserializer); + r.optional_object_field(obj, VERSION_GE, dep.constraint.value, version_deserializer); auto has_port_ver = r.optional_object_field( obj, PORT_VERSION, dep.constraint.port_version, Json::NaturalNumberDeserializer::instance); if (has_ge_constraint) { dep.constraint.type = Versions::Constraint::Type::Minimum; + /*** SUPPORT_HASH_SYNTAX ***/ + auto h = dep.constraint.value.find('#'); + if (h != std::string::npos) + { + if (has_port_ver) + { + r.add_generic_error(type_name(), + "\"port-version\" cannot be used with an embedded '#' in the primary " + "constraint (\"", + VERSION_GE, + "\")"); + } + auto opt = Strings::strto(dep.constraint.value.c_str() + h + 1); + auto v = opt.get(); + if (v && *v >= 0) + { + dep.constraint.port_version = *v; + } + else + { + r.add_generic_error(type_name(), + "embedded port-version ('#') in the primary " + "constraint (\"", + VERSION_GE, + "\") must be a nonnegative integer"); + } + dep.constraint.value.erase(h); + } + /*** /SUPPORT_HASH_SYNTAX ***/ } else if (has_port_ver) // does not have a primary constraint { - r.add_generic_error(type_name(), - "\"port-version\" cannot be used without a primary constraint (\"version>=\")"); + r.add_generic_error( + type_name(), "\"port-version\" cannot be used without a primary constraint (\"", VERSION_GE, "\")"); } return dep; From 62e7b88be34b8d8d032001dd95977ff9e34e7427 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Thu, 14 Jan 2021 20:52:35 +0000 Subject: [PATCH 6/8] [vcpkg] Remove port-version from dependency syntax --- docs/users/manifests.md | 8 +----- docs/users/versioning.md | 7 +++-- scripts/vcpkg.schema.json | 9 ++---- toolsrc/src/vcpkg-test/manifests.cpp | 22 ++++++++++---- toolsrc/src/vcpkg/help.cpp | 4 +-- toolsrc/src/vcpkg/sourceparagraph.cpp | 41 ++++++++------------------- 6 files changed, 38 insertions(+), 53 deletions(-) diff --git a/docs/users/manifests.md b/docs/users/manifests.md index 45ef9bd080b34b..e1bc0c58b6dc85 100644 --- a/docs/users/manifests.md +++ b/docs/users/manifests.md @@ -151,16 +151,10 @@ although one can define their own. A minimum version constraint on the dependency. -This field specifies the primary scheme text of the dependency and the optional `"port-version"` field specifies a minimum port version (if any). +This field specifies the minimum version of the dependency using a '#' suffix to denote port-version if non-zero. See also [versioning](versioning.md#constraints) for more semantic details. -#### `"port-version"` - -**Experimental behind the `versions` feature flag** - -See `"version>="` above. - #### Example: ```json diff --git a/docs/users/versioning.md b/docs/users/versioning.md index a247aef7503879..b2b68f99eb5f6e 100644 --- a/docs/users/versioning.md +++ b/docs/users/versioning.md @@ -50,8 +50,9 @@ Baselines provide stability and ease of development for top-level manifest files Within the "dependencies" field, each dependency can have a minimum constraint listed. These minimum constraints will be used when transitively depending upon this library. A minimum -port-version can additionally be specified with the "port-version" -field in the same object. +port-version can additionally be specified with a '#' suffix. + +This constraint must refer to an existing, valid version (including port-version). ### overrides When used as the top-level manifest (such as when running `vcpkg @@ -69,7 +70,7 @@ Overrides are not considered from ports consumed as a dependency. "version": "1.0", "builtin-baseline": "a14a6bcb27287e3ec138dba1b948a0cdbc337a3a", "dependencies": [ - { "name": "zlib", "version>=": "1.2.11", "port-version": 8 }, + { "name": "zlib", "version>=": "1.2.11#8" }, "rapidjson" ], "overrides": [ diff --git a/scripts/vcpkg.schema.json b/scripts/vcpkg.schema.json index 7402e51fb27004..c485aa4158d189 100644 --- a/scripts/vcpkg.schema.json +++ b/scripts/vcpkg.schema.json @@ -136,12 +136,9 @@ "$ref": "#/definitions/platform-expression" }, "version>=": { - "description": "Minimum required port version", - "$ref": "#/definitions/version-text" - }, - "port-version": { - "description": "Minimum port revision (defaults to 0)", - "$ref": "#/definitions/port-version" + "description": "Minimum required version", + "type": "string", + "pattern": "^[^#]+(#\\d+)?$" } }, "patternProperties": { diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp index 7be7d398c4bd9e..0af9f4f968a409 100644 --- a/toolsrc/src/vcpkg-test/manifests.cpp +++ b/toolsrc/src/vcpkg-test/manifests.cpp @@ -160,7 +160,6 @@ TEST_CASE ("manifest versioning", "[manifests]") TEST_CASE ("manifest constraints hash", "[manifests]") { -#if 1 /*** SUPPORT_HASH_SYNTAX ***/ auto p = unwrap(test_parse_manifest(R"json({ "name": "zlib", "version-string": "abcd", @@ -180,25 +179,36 @@ TEST_CASE ("manifest constraints hash", "[manifests]") "dependencies": [ { "name": "d", - "version>=": "2018-09-01#1", - "port-version": 1 + "version>=": "2018-09-01#0" } ] })json", true); -#else /*** SUPPORT_HASH_SYNTAX ***/ + test_parse_manifest(R"json({ "name": "zlib", "version-string": "abcd", "dependencies": [ { "name": "d", - "version>=": "2018-09-01#1" + "version>=": "2018-09-01#-1" + } + ] +})json", + true); + + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "dependencies": [ + { + "name": "d", + "version>=": "2018-09-01", + "port-version": 1 } ] })json", true); -#endif /*** SUPPORT_HASH_SYNTAX ***/ } TEST_CASE ("manifest overrides error hash", "[manifests]") diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index a2785e5e218303..21528886a8bd28 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -81,7 +81,7 @@ namespace vcpkg::Help tbl.format("version>=", "Within the \"dependencies\" field, each dependency can have a minimum constraint listed. These " "minimum constraints will be used when transitively depending upon this library. A minimum " - "port-version can be specified with the \"port-version\" field."); + "port-version can additionally be specified with a '#' suffix."); tbl.blank(); tbl.format( "overrides", @@ -97,7 +97,7 @@ namespace vcpkg::Help "version": "1.0", "builtin-baseline": "a14a6bcb27287e3ec138dba1b948a0cdbc337a3a", "dependencies": [ - { "name": "zlib", "version>=": "1.2.11", "port-version": 8 }, + { "name": "zlib", "version>=": "1.2.11#8" }, "rapidjson" ], "overrides": [ diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp index 4d275bebf8e1f5..df8261a7e48bb4 100644 --- a/toolsrc/src/vcpkg/sourceparagraph.cpp +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -430,7 +430,6 @@ namespace vcpkg constexpr static StringLiteral FEATURES = "features"; constexpr static StringLiteral DEFAULT_FEATURES = "default-features"; constexpr static StringLiteral PLATFORM = "platform"; - constexpr static StringLiteral PORT_VERSION = "port-version"; constexpr static StringLiteral VERSION_GE = "version>="; virtual Span valid_fields() const override @@ -440,7 +439,6 @@ namespace vcpkg FEATURES, DEFAULT_FEATURES, PLATFORM, - PORT_VERSION, VERSION_GE, }; @@ -490,27 +488,16 @@ namespace vcpkg auto has_ge_constraint = r.optional_object_field(obj, VERSION_GE, dep.constraint.value, version_deserializer); - auto has_port_ver = r.optional_object_field( - obj, PORT_VERSION, dep.constraint.port_version, Json::NaturalNumberDeserializer::instance); if (has_ge_constraint) { dep.constraint.type = Versions::Constraint::Type::Minimum; - /*** SUPPORT_HASH_SYNTAX ***/ auto h = dep.constraint.value.find('#'); if (h != std::string::npos) { - if (has_port_ver) - { - r.add_generic_error(type_name(), - "\"port-version\" cannot be used with an embedded '#' in the primary " - "constraint (\"", - VERSION_GE, - "\")"); - } auto opt = Strings::strto(dep.constraint.value.c_str() + h + 1); auto v = opt.get(); - if (v && *v >= 0) + if (v && *v > 0) { dep.constraint.port_version = *v; } @@ -520,16 +507,10 @@ namespace vcpkg "embedded port-version ('#') in the primary " "constraint (\"", VERSION_GE, - "\") must be a nonnegative integer"); + "\") must be a positive integer"); } dep.constraint.value.erase(h); } - /*** /SUPPORT_HASH_SYNTAX ***/ - } - else if (has_port_ver) // does not have a primary constraint - { - r.add_generic_error( - type_name(), "\"port-version\" cannot be used without a primary constraint (\"", VERSION_GE, "\")"); } return dep; @@ -557,7 +538,6 @@ namespace vcpkg constexpr StringLiteral DependencyDeserializer::DEFAULT_FEATURES; constexpr StringLiteral DependencyDeserializer::PLATFORM; constexpr StringLiteral DependencyDeserializer::VERSION_GE; - constexpr StringLiteral DependencyDeserializer::PORT_VERSION; struct DependencyOverrideDeserializer : Json::IDeserializer { @@ -1291,15 +1271,18 @@ namespace vcpkg serialize_optional_array(dep_obj, DependencyDeserializer::FEATURES, features_copy); serialize_optional_string(dep_obj, DependencyDeserializer::PLATFORM, to_string(dep.platform)); - if (dep.constraint.port_version != 0) - { - dep_obj.insert(DependencyDeserializer::PORT_VERSION, - Json::Value::integer(dep.constraint.port_version)); - } - if (dep.constraint.type == Versions::Constraint::Type::Minimum) { - dep_obj.insert(DependencyDeserializer::VERSION_GE, Json::Value::string(dep.constraint.value)); + if (dep.constraint.port_version != 0) + { + dep_obj.insert(DependencyDeserializer::VERSION_GE, + Json::Value::string( + Strings::concat(dep.constraint.value, '#', dep.constraint.port_version))); + } + else + { + dep_obj.insert(DependencyDeserializer::VERSION_GE, Json::Value::string(dep.constraint.value)); + } } } }; From 342194b1199aa50423849359c57bde07bebdce02 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Fri, 15 Jan 2021 20:05:24 +0000 Subject: [PATCH 7/8] [vcpkg] Address CR comment --- toolsrc/src/vcpkg/sourceparagraph.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp index df8261a7e48bb4..61cc7f8264d542 100644 --- a/toolsrc/src/vcpkg/sourceparagraph.cpp +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -1273,16 +1273,12 @@ namespace vcpkg serialize_optional_string(dep_obj, DependencyDeserializer::PLATFORM, to_string(dep.platform)); if (dep.constraint.type == Versions::Constraint::Type::Minimum) { + auto s = dep.constraint.value; if (dep.constraint.port_version != 0) { - dep_obj.insert(DependencyDeserializer::VERSION_GE, - Json::Value::string( - Strings::concat(dep.constraint.value, '#', dep.constraint.port_version))); - } - else - { - dep_obj.insert(DependencyDeserializer::VERSION_GE, Json::Value::string(dep.constraint.value)); + Strings::append(s, '#', dep.constraint.port_version); } + dep_obj.insert(DependencyDeserializer::VERSION_GE, Json::Value::string(std::move(s))); } } }; From 1bfe8cc3162809ec38b59f5983c176ba339a6e0e Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Fri, 15 Jan 2021 20:17:38 +0000 Subject: [PATCH 8/8] [vcpkg] Minor docs fixup --- docs/users/versioning.md | 2 +- toolsrc/src/vcpkg/help.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/users/versioning.md b/docs/users/versioning.md index b2b68f99eb5f6e..86840422be7d45 100644 --- a/docs/users/versioning.md +++ b/docs/users/versioning.md @@ -8,7 +8,7 @@ your project from within your manifest file. ## Version schemes ### Schemes -Versions in vcpkg come in three primary flavors: +Versions in vcpkg come in four primary flavors: #### version A dot-separated sequence of numbers (1.2.3.4) diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index 21528886a8bd28..0556b4fa064e03 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -57,7 +57,7 @@ namespace vcpkg::Help tbl.text("** This feature is experimental and requires `--feature-flags=versions` **"); tbl.blank(); tbl.blank(); - tbl.header("Versions in vcpkg come in three primary flavors"); + tbl.header("Versions in vcpkg come in four primary flavors"); tbl.format("version", "A dot-separated sequence of numbers (1.2.3.4)"); tbl.format("version-date", "A date (2021-01-01.5)"); tbl.format("version-semver", "A Semantic Version 2.0 (2.1.0-rc2)");