From b158fe3bfea0064edf589f44b1864fe7040af2dd Mon Sep 17 00:00:00 2001 From: Javier Matos Denizac Date: Fri, 21 Jul 2023 13:59:41 -0700 Subject: [PATCH 1/6] initial implementation --- include/vcpkg/base/message-data.inc.h | 13 ++++++ locales/messages.json | 7 +++ src/vcpkg/commands.export.cpp | 61 +++++++++++++++++++-------- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/include/vcpkg/base/message-data.inc.h b/include/vcpkg/base/message-data.inc.h index 3057caf3ab..997e5ffe4f 100644 --- a/include/vcpkg/base/message-data.inc.h +++ b/include/vcpkg/base/message-data.inc.h @@ -494,6 +494,15 @@ DECLARE_MESSAGE(CmdDependInfoOptSort, DECLARE_MESSAGE(CmdEditOptAll, (), "", "Open editor into the port as well as the port-specific buildtree subfolder") DECLARE_MESSAGE(CmdEditOptBuildTrees, (), "", "Open editor into the port-specific buildtree subfolder") DECLARE_MESSAGE(CmdEnvOptions, (msg::path, msg::env_var), "", "Add installed {path} to {env_var}") +DECLARE_MESSAGE(CmdExportEmptyPlan, (), "", "Export plan cannot be empty. Install packages before exporting.") +DECLARE_MESSAGE(CmdExportInvalidOption, + (msg::option), + "{option} is an argument supplied to the vcpkg export command.", + "{option} argument is not allowed in manifest mode.") +DECLARE_MESSAGE(CmdExportMissingArgument, + (msg::option), + "{option} is an argument supplied to the vcpkg export command.", + "The {option} argument must be provided when running in manifest mode.") DECLARE_MESSAGE(CmdExportOpt7Zip, (), "", "Export to a 7zip (.7z) file") DECLARE_MESSAGE(CmdExportOptChocolatey, (), "", "Export a Chocolatey package (experimental feature)") DECLARE_MESSAGE(CmdExportOptDebug, (), "", "Enable prefab debug") @@ -1635,6 +1644,10 @@ DECLARE_MESSAGE(InvalidDependency, (), "", "dependencies must be lowercase alphanumeric+hyphens, and not one of the reserved names") +DECLARE_MESSAGE(InvalidExportArgument, + (msg::option), + "{option} is an argument passed to the vcpkg export command.", + "{option} arguments are not allowed in manifest mode.") DECLARE_MESSAGE(InvalidFeature, (), "", diff --git a/locales/messages.json b/locales/messages.json index 7d09d1546e..e7a0f547f6 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -287,6 +287,11 @@ "CmdEditOptBuildTrees": "Open editor into the port-specific buildtree subfolder", "CmdEnvOptions": "Add installed {path} to {env_var}", "_CmdEnvOptions.comment": "An example of {path} is /foo/bar. An example of {env_var} is VCPKG_DEFAULT_TRIPLET.", + "CmdExportEmptyPlan": "Export plan cannot be empty. Install packages before exporting.", + "CmdExportInvalidOption": "{option} argument is not allowed in manifest mode.", + "_CmdExportInvalidOption.comment": "{option} is an argument supplied to the vcpkg export command. An example of {option} is editable.", + "CmdExportMissingArgument": "The {option} argument must be provided when running in manifest mode.", + "_CmdExportMissingArgument.comment": "{option} is an argument supplied to the vcpkg export command. An example of {option} is editable.", "CmdExportOpt7Zip": "Export to a 7zip (.7z) file", "CmdExportOptChocolatey": "Export a Chocolatey package (experimental feature)", "CmdExportOptDebug": "Enable prefab debug", @@ -918,6 +923,8 @@ "_InvalidCommitId.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949.", "InvalidDefaultFeatureName": "'default' is a reserved feature name", "InvalidDependency": "dependencies must be lowercase alphanumeric+hyphens, and not one of the reserved names", + "InvalidExportArgument": "{option} arguments are not allowed in manifest mode.", + "_InvalidExportArgument.comment": "{option} is an argument passed to the vcpkg export command. An example of {option} is editable.", "InvalidFeature": "features must be lowercase alphanumeric+hyphens, and not one of the reserved names", "InvalidFileType": "failed: {path} cannot handle file type", "_InvalidFileType.comment": "An example of {path} is /foo/bar.", diff --git a/src/vcpkg/commands.export.cpp b/src/vcpkg/commands.export.cpp index d983355907..d11531a0bf 100644 --- a/src/vcpkg/commands.export.cpp +++ b/src/vcpkg/commands.export.cpp @@ -389,31 +389,60 @@ namespace vcpkg::Export ret.prefab_options.enable_maven = Util::Sets::contains(options.switches, OPTION_PREFAB_ENABLE_MAVEN); ret.prefab_options.enable_debug = Util::Sets::contains(options.switches, OPTION_PREFAB_ENABLE_DEBUG); ret.maybe_output = Util::lookup_value_copy(options.settings, OPTION_OUTPUT); - ret.output_dir = Util::lookup_value(options.settings, OPTION_OUTPUT_DIR) - .map([&](const Path& p) { return paths.original_cwd / p; }) - .value_or(paths.root); ret.all_installed = Util::Sets::contains(options.switches, OPTION_ALL_INSTALLED); - if (ret.all_installed) + if (paths.manifest_mode_enabled()) { + auto output_dir_opt = Util::lookup_value(options.settings, OPTION_OUTPUT_DIR); + + // --output-dir is required in manifest mode + if (!output_dir_opt.has_value()) + { + msg::println_error(msgCmdExportMissingArgument, msg::option = "--output-dir"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + ret.output_dir = paths.original_cwd / output_dir_opt.value_or_exit(VCPKG_LINE_INFO); auto installed_ipv = get_installed_ports(status_db); std::transform(installed_ipv.begin(), installed_ipv.end(), std::back_inserter(ret.specs), [](const auto& ipv) { return ipv.spec(); }); + + // In manifest mode the entire installed directory is exported + if (!options.command_arguments.empty()) + { + msg::println_error(msgCmdExportInvalidOption, msg::option = ""); + Checks::exit_fail(VCPKG_LINE_INFO); + } } else { - // input sanitization - bool default_triplet_used = false; - ret.specs = Util::fmap(options.command_arguments, [&](auto&& arg) { - return parse_package_spec( - arg, default_triplet, default_triplet_used, COMMAND_STRUCTURE.get_example_text()); - }); - - if (default_triplet_used) + ret.output_dir = Util::lookup_value(options.settings, OPTION_OUTPUT_DIR) + .map([&](const Path& p) { return paths.original_cwd / p; }) + .value_or(paths.root); + + if (ret.all_installed) { - print_default_triplet_warning(args, paths.get_triplet_db()); + auto installed_ipv = get_installed_ports(status_db); + std::transform(installed_ipv.begin(), + installed_ipv.end(), + std::back_inserter(ret.specs), + [](const auto& ipv) { return ipv.spec(); }); + } + else + { + // input sanitization + bool default_triplet_used = false; + ret.specs = Util::fmap(options.command_arguments, [&](auto&& arg) { + return parse_package_spec( + arg, default_triplet, default_triplet_used, COMMAND_STRUCTURE.get_example_text()); + }); + + if (default_triplet_used) + { + print_default_triplet_warning(args, paths.get_triplet_db()); + } } } @@ -593,10 +622,6 @@ namespace vcpkg::Export Triplet host_triplet) { (void)host_triplet; - if (paths.manifest_mode_enabled()) - { - Checks::msg_exit_maybe_upgrade(VCPKG_LINE_INFO, msgExportUnsupportedInManifest); - } const StatusParagraphs status_db = database_load_check(paths.get_filesystem(), paths.installed()); const auto opts = handle_export_command_arguments(paths, args, default_triplet, status_db); @@ -610,7 +635,7 @@ namespace vcpkg::Export std::vector export_plan = create_export_plan(opts.specs, status_db); if (export_plan.empty()) { - Debug::print("Export plan cannot be empty."); + msg::println_error(msgCmdExportEmptyPlan); Checks::exit_fail(VCPKG_LINE_INFO); } From 3753febc44b0338226c78df94f140925dbc85650 Mon Sep 17 00:00:00 2001 From: Javier Matos Denizac Date: Thu, 27 Jul 2023 12:10:40 -0700 Subject: [PATCH 2/6] avoid value or exit and reuse MissinOption message --- src/vcpkg/commands.export.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vcpkg/commands.export.cpp b/src/vcpkg/commands.export.cpp index d11531a0bf..af4cdf74fc 100644 --- a/src/vcpkg/commands.export.cpp +++ b/src/vcpkg/commands.export.cpp @@ -396,13 +396,16 @@ namespace vcpkg::Export auto output_dir_opt = Util::lookup_value(options.settings, OPTION_OUTPUT_DIR); // --output-dir is required in manifest mode - if (!output_dir_opt.has_value()) + if (auto d = output_dir_opt.get()) { - msg::println_error(msgCmdExportMissingArgument, msg::option = "--output-dir"); - Checks::exit_fail(VCPKG_LINE_INFO); - } + ret.output_dir = paths.original_cwd / *d; + } + else + { + msg::println_error(msgMissingOption, msg::option = "output-dir"); + Checks::exit_fail(VCPKG_LINE_INFO); + } - ret.output_dir = paths.original_cwd / output_dir_opt.value_or_exit(VCPKG_LINE_INFO); auto installed_ipv = get_installed_ports(status_db); std::transform(installed_ipv.begin(), installed_ipv.end(), From 31f9114f116ee92c87a6b4794078fd1c1ce527c2 Mon Sep 17 00:00:00 2001 From: Javier Matos Denizac Date: Thu, 27 Jul 2023 12:19:02 -0700 Subject: [PATCH 3/6] improve messaging --- include/vcpkg/base/message-data.inc.h | 17 ++++------------- locales/messages.json | 8 +------- src/vcpkg/commands.export.cpp | 10 +++++----- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/include/vcpkg/base/message-data.inc.h b/include/vcpkg/base/message-data.inc.h index 997e5ffe4f..2f6d24d91c 100644 --- a/include/vcpkg/base/message-data.inc.h +++ b/include/vcpkg/base/message-data.inc.h @@ -494,15 +494,10 @@ DECLARE_MESSAGE(CmdDependInfoOptSort, DECLARE_MESSAGE(CmdEditOptAll, (), "", "Open editor into the port as well as the port-specific buildtree subfolder") DECLARE_MESSAGE(CmdEditOptBuildTrees, (), "", "Open editor into the port-specific buildtree subfolder") DECLARE_MESSAGE(CmdEnvOptions, (msg::path, msg::env_var), "", "Add installed {path} to {env_var}") -DECLARE_MESSAGE(CmdExportEmptyPlan, (), "", "Export plan cannot be empty. Install packages before exporting.") -DECLARE_MESSAGE(CmdExportInvalidOption, - (msg::option), - "{option} is an argument supplied to the vcpkg export command.", - "{option} argument is not allowed in manifest mode.") -DECLARE_MESSAGE(CmdExportMissingArgument, - (msg::option), - "{option} is an argument supplied to the vcpkg export command.", - "The {option} argument must be provided when running in manifest mode.") +DECLARE_MESSAGE(CmdExportEmptyPlan, + (), + "", + "Refusing to create an export of zero packages. Install packages before exporting.") DECLARE_MESSAGE(CmdExportOpt7Zip, (), "", "Export to a 7zip (.7z) file") DECLARE_MESSAGE(CmdExportOptChocolatey, (), "", "Export a Chocolatey package (experimental feature)") DECLARE_MESSAGE(CmdExportOptDebug, (), "", "Enable prefab debug") @@ -1644,10 +1639,6 @@ DECLARE_MESSAGE(InvalidDependency, (), "", "dependencies must be lowercase alphanumeric+hyphens, and not one of the reserved names") -DECLARE_MESSAGE(InvalidExportArgument, - (msg::option), - "{option} is an argument passed to the vcpkg export command.", - "{option} arguments are not allowed in manifest mode.") DECLARE_MESSAGE(InvalidFeature, (), "", diff --git a/locales/messages.json b/locales/messages.json index e7a0f547f6..27bb149047 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -287,11 +287,7 @@ "CmdEditOptBuildTrees": "Open editor into the port-specific buildtree subfolder", "CmdEnvOptions": "Add installed {path} to {env_var}", "_CmdEnvOptions.comment": "An example of {path} is /foo/bar. An example of {env_var} is VCPKG_DEFAULT_TRIPLET.", - "CmdExportEmptyPlan": "Export plan cannot be empty. Install packages before exporting.", - "CmdExportInvalidOption": "{option} argument is not allowed in manifest mode.", - "_CmdExportInvalidOption.comment": "{option} is an argument supplied to the vcpkg export command. An example of {option} is editable.", - "CmdExportMissingArgument": "The {option} argument must be provided when running in manifest mode.", - "_CmdExportMissingArgument.comment": "{option} is an argument supplied to the vcpkg export command. An example of {option} is editable.", + "CmdExportEmptyPlan": "Refusing to create an export of zero packages. Install packages before exporting.", "CmdExportOpt7Zip": "Export to a 7zip (.7z) file", "CmdExportOptChocolatey": "Export a Chocolatey package (experimental feature)", "CmdExportOptDebug": "Enable prefab debug", @@ -923,8 +919,6 @@ "_InvalidCommitId.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949.", "InvalidDefaultFeatureName": "'default' is a reserved feature name", "InvalidDependency": "dependencies must be lowercase alphanumeric+hyphens, and not one of the reserved names", - "InvalidExportArgument": "{option} arguments are not allowed in manifest mode.", - "_InvalidExportArgument.comment": "{option} is an argument passed to the vcpkg export command. An example of {option} is editable.", "InvalidFeature": "features must be lowercase alphanumeric+hyphens, and not one of the reserved names", "InvalidFileType": "failed: {path} cannot handle file type", "_InvalidFileType.comment": "An example of {path} is /foo/bar.", diff --git a/src/vcpkg/commands.export.cpp b/src/vcpkg/commands.export.cpp index af4cdf74fc..891489ee9f 100644 --- a/src/vcpkg/commands.export.cpp +++ b/src/vcpkg/commands.export.cpp @@ -398,13 +398,13 @@ namespace vcpkg::Export // --output-dir is required in manifest mode if (auto d = output_dir_opt.get()) { - ret.output_dir = paths.original_cwd / *d; - } + ret.output_dir = paths.original_cwd / *d; + } else { msg::println_error(msgMissingOption, msg::option = "output-dir"); - Checks::exit_fail(VCPKG_LINE_INFO); - } + Checks::exit_fail(VCPKG_LINE_INFO); + } auto installed_ipv = get_installed_ports(status_db); std::transform(installed_ipv.begin(), @@ -415,7 +415,7 @@ namespace vcpkg::Export // In manifest mode the entire installed directory is exported if (!options.command_arguments.empty()) { - msg::println_error(msgCmdExportInvalidOption, msg::option = ""); + msg::println_error(msgUnexpectedArgument, msg::option = options.command_arguments[0]); Checks::exit_fail(VCPKG_LINE_INFO); } } From a9a5bd263af096e1deb7e43222f9fd114ef277ea Mon Sep 17 00:00:00 2001 From: Javier Matos Denizac Date: Thu, 27 Jul 2023 12:35:57 -0700 Subject: [PATCH 4/6] reduce redundancy --- src/vcpkg/commands.export.cpp | 54 ++++++++++++++++------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/vcpkg/commands.export.cpp b/src/vcpkg/commands.export.cpp index 891489ee9f..cda604ebab 100644 --- a/src/vcpkg/commands.export.cpp +++ b/src/vcpkg/commands.export.cpp @@ -406,11 +406,8 @@ namespace vcpkg::Export Checks::exit_fail(VCPKG_LINE_INFO); } - auto installed_ipv = get_installed_ports(status_db); - std::transform(installed_ipv.begin(), - installed_ipv.end(), - std::back_inserter(ret.specs), - [](const auto& ipv) { return ipv.spec(); }); + // Force enable --all-installed in manifest mode + ret.all_installed = true; // In manifest mode the entire installed directory is exported if (!options.command_arguments.empty()) @@ -419,33 +416,32 @@ namespace vcpkg::Export Checks::exit_fail(VCPKG_LINE_INFO); } } + + ret.output_dir = ret.output_dir.empty() ? Util::lookup_value(options.settings, OPTION_OUTPUT_DIR) + .map([&](const Path& p) { return paths.original_cwd / p; }) + .value_or(paths.root) + : ret.output_dir; + + if (ret.all_installed) + { + auto installed_ipv = get_installed_ports(status_db); + std::transform(installed_ipv.begin(), + installed_ipv.end(), + std::back_inserter(ret.specs), + [](const auto& ipv) { return ipv.spec(); }); + } else { - ret.output_dir = Util::lookup_value(options.settings, OPTION_OUTPUT_DIR) - .map([&](const Path& p) { return paths.original_cwd / p; }) - .value_or(paths.root); - - if (ret.all_installed) + // input sanitization + bool default_triplet_used = false; + ret.specs = Util::fmap(options.command_arguments, [&](auto&& arg) { + return parse_package_spec( + arg, default_triplet, default_triplet_used, COMMAND_STRUCTURE.get_example_text()); + }); + + if (default_triplet_used) { - auto installed_ipv = get_installed_ports(status_db); - std::transform(installed_ipv.begin(), - installed_ipv.end(), - std::back_inserter(ret.specs), - [](const auto& ipv) { return ipv.spec(); }); - } - else - { - // input sanitization - bool default_triplet_used = false; - ret.specs = Util::fmap(options.command_arguments, [&](auto&& arg) { - return parse_package_spec( - arg, default_triplet, default_triplet_used, COMMAND_STRUCTURE.get_example_text()); - }); - - if (default_triplet_used) - { - print_default_triplet_warning(args, paths.get_triplet_db()); - } + print_default_triplet_warning(args, paths.get_triplet_db()); } } From 9fa9ef138574effb3154da2656c0a048b24cd163 Mon Sep 17 00:00:00 2001 From: Javier Matos Denizac Date: Thu, 27 Jul 2023 12:50:24 -0700 Subject: [PATCH 5/6] add end2end test file --- azure-pipelines/end-to-end-tests-dir/commands.export.ps1 | 1 + 1 file changed, 1 insertion(+) create mode 100644 azure-pipelines/end-to-end-tests-dir/commands.export.ps1 diff --git a/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 b/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 new file mode 100644 index 0000000000..25dd119669 --- /dev/null +++ b/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 @@ -0,0 +1 @@ +. $PSScriptRoot/../end-to-end-tests-prelude.ps1 From faaa59e07bc2f129770a2037e6dca3625d286638 Mon Sep 17 00:00:00 2001 From: Javier Matos Denizac Date: Fri, 28 Jul 2023 12:33:52 -0700 Subject: [PATCH 6/6] add test cases for raw based exports in manifest mode --- .../e2e-projects/export-project/vcpkg.json | 12 ++++ .../end-to-end-tests-dir/commands.export.ps1 | 66 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 azure-pipelines/e2e-projects/export-project/vcpkg.json diff --git a/azure-pipelines/e2e-projects/export-project/vcpkg.json b/azure-pipelines/e2e-projects/export-project/vcpkg.json new file mode 100644 index 0000000000..c51d510762 --- /dev/null +++ b/azure-pipelines/e2e-projects/export-project/vcpkg.json @@ -0,0 +1,12 @@ +{ + "name": "my-project", + "version-string": "0.1.0", + "dependencies": [ + { + "name": "zlib" + }, + { + "name": "fmt" + } + ] +} diff --git a/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 b/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 index 25dd119669..e5c8c0d265 100644 --- a/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 +++ b/azure-pipelines/end-to-end-tests-dir/commands.export.ps1 @@ -1 +1,67 @@ . $PSScriptRoot/../end-to-end-tests-prelude.ps1 + + +$manifestPath = "$PSScriptRoot/../e2e-projects/export-project" +$outputDir = "$manifestPath/output" +Run-Vcpkg install --x-manifest-root=$manifestPath +Throw-IfFailed + +Run-Vcpkg export --zip --x-manifest-root=$manifestPath --output-dir=$outputDir +Throw-IfFailed + +Run-Vcpkg export --nuget --x-manifest-root=$manifestPath --output-dir=$outputDir +Throw-IfFailed + +Run-Vcpkg export --7zip --x-manifest-root=$manifestPath --output-dir=$outputDir +Throw-IfFailed + +# Check existence of zip file(s) +$zipFilesExist = Test-Path "$outputDir/*.zip" +if (-Not $zipFilesExist) +{ + throw "No zip files found in $outputDir" +} + +# Check existence of nuget file(s) +$nugetFilesExist = Test-Path "$outputDir/*.nupkg" +if (-Not $nugetFilesExist) +{ + throw "No nuget files found in $outputDir" +} + +# Check existence of 7zip file(s) +$sevenZipFilesExist = Test-Path "$outputDir/*.7z" +if (-Not $sevenZipFilesExist) +{ + throw "No 7zip files found in $outputDir" +} + +# Cleanup exported packages +Get-ChildItem -Path $manifestPath | Where-Object { $_.Name -ne "vcpkg.json" -and $_.Name -ne "vcpkg_installed" } | Remove-Item -Recurse -Force + +# Test export with invalid argument +$out = Run-VcpkgAndCaptureOutput export zlib:x64-windows --zip --x-manifest-root=$manifestPath --output-dir=$manifestPath +Throw-IfNotFailed +if ($out -notmatch "unexpected argument: zlib:x64-windows") +{ + throw "Expected to fail and print warning about unexpected argument" +} + +# Test export with missing --output-dir argument +$out = Run-VcpkgAndCaptureOutput export --zip --x-manifest-root=$manifestPath +Throw-IfNotFailed +if ($out -notmatch "This command requires --output-dir") +{ + throw "Expected to fail and print warning about missing argument" +} + +# Test export with empty export plan +Remove-Item -Path "$manifestPath/vcpkg_installed" -Recurse -Force +$out = Run-VcpkgAndCaptureOutput export --zip --x-manifest-root=$manifestPath --output-dir=$manifestPath +Throw-IfNotFailed +if ($out -notmatch "Refusing to create an export of zero packages. Install packages before exporting.") +{ + throw "Expected to fail and print warning about empty export plan." +} + +