From ba0cc3f1d7edb7e2fee271761b2f37d0c740604f Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Mon, 28 Aug 2017 13:54:19 +0300 Subject: WIP: Export IFW Add export to binary crossplatform repository/installer with GUI based on QtIFW: http://doc.qt.io/qtinstallerframework/ifw-overview.html For correct operation of these changes, you must use the corrected QtIFW: https://codereview.qt-project.org/#/c/203958 --- toolsrc/src/commands_export.cpp | 57 ++++++- toolsrc/src/commands_export_ifw.cpp | 319 ++++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+), 7 deletions(-) create mode 100644 toolsrc/src/commands_export_ifw.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp index b416a6f3c..139f19026 100644 --- a/toolsrc/src/commands_export.cpp +++ b/toolsrc/src/commands_export.cpp @@ -2,6 +2,7 @@ #include "Paragraphs.h" #include "vcpkg_Commands.h" +#include "vcpkg_Commands_Export_IFW.h" #include "vcpkg_Dependencies.h" #include "vcpkg_Input.h" #include "vcpkg_System.h" @@ -215,10 +216,12 @@ namespace vcpkg::Commands::Export static const std::string OPTION_DRY_RUN = "--dry-run"; static const std::string OPTION_RAW = "--raw"; static const std::string OPTION_NUGET = "--nuget"; + static const std::string OPTION_IFW = "--ifw"; static const std::string OPTION_ZIP = "--zip"; static const std::string OPTION_SEVEN_ZIP = "--7zip"; static const std::string OPTION_NUGET_ID = "--nuget-id"; static const std::string OPTION_NUGET_VERSION = "--nuget-version"; + static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url"; // input sanitization static const std::string EXAMPLE = @@ -236,22 +239,25 @@ namespace vcpkg::Commands::Export OPTION_DRY_RUN, OPTION_RAW, OPTION_NUGET, + OPTION_IFW, OPTION_ZIP, OPTION_SEVEN_ZIP, }, { OPTION_NUGET_ID, OPTION_NUGET_VERSION, + OPTION_IFW_REPOSITORY_URL, }); const bool dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); const bool raw = options.switches.find(OPTION_RAW) != options.switches.cend(); const bool nuget = options.switches.find(OPTION_NUGET) != options.switches.cend(); + const bool ifw = options.switches.find(OPTION_IFW) != options.switches.cend(); const bool zip = options.switches.find(OPTION_ZIP) != options.switches.cend(); const bool seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend(); - if (!raw && !nuget && !zip && !seven_zip && !dry_run) + if (!raw && !nuget && !ifw && !zip && !seven_zip && !dry_run) { - System::println(System::Color::error, "Must provide at least one export type: --raw --nuget --zip --7zip"); + System::println(System::Color::error, "Must provide at least one export type: --raw --nuget --ifw --zip --7zip"); System::print(EXAMPLE); Checks::exit_fail(VCPKG_LINE_INFO); } @@ -263,6 +269,10 @@ namespace vcpkg::Commands::Export Checks::check_exit( VCPKG_LINE_INFO, !maybe_nuget_version || nuget, "--nuget-version is only valid with --nuget"); + auto maybe_ifw_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); + + Checks::check_exit(VCPKG_LINE_INFO, !maybe_ifw_repository_url || ifw, "--ifw-repository-url is only valid with --ifw"); + // create the plan const StatusParagraphs status_db = database_load_check(paths); std::vector export_plan = Dependencies::create_export_plan(paths, specs, status_db); @@ -305,7 +315,11 @@ namespace vcpkg::Commands::Export Checks::exit_success(VCPKG_LINE_INFO); } - const std::string export_id = create_export_id(); + std::string export_id = create_export_id(); + if (ifw) + { + export_id = "vcpkg-export"; // TODO: Remove after debugging + } Files::Filesystem& fs = paths.get_filesystem(); const fs::path export_to_path = paths.root; @@ -315,6 +329,8 @@ namespace vcpkg::Commands::Export fs.create_directory(raw_exported_dir_path, ec); // execute the plan + std::map unique_packages; + std::set unique_triplets; for (const ExportPlanAction& action : export_plan) { if (action.plan_type != ExportPlanType::ALREADY_BUILT) @@ -327,10 +343,21 @@ namespace vcpkg::Commands::Export const BinaryParagraph& binary_paragraph = action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + unique_packages[action.spec.name()] = &action; + unique_triplets.insert(action.spec.triplet().canonical_name()); + + fs::path spec_exported_dir_path = raw_exported_dir_path / "installed"; + if (ifw) + { + // Export real package and return data dir for installation + spec_exported_dir_path = IFW::export_real_package(raw_exported_dir_path, action, fs); + } + const InstallDir dirs = InstallDir::from_destination_root( - raw_exported_dir_path / "installed", + spec_exported_dir_path, action.spec.triplet().to_string(), - raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); + spec_exported_dir_path / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); System::println(System::Color::success, "Exporting package %s... done", display_name); @@ -351,13 +378,29 @@ namespace vcpkg::Commands::Export for (const fs::path& file : integration_files_relative_to_root) { const fs::path source = paths.root / file; - const fs::path destination = raw_exported_dir_path / file; + fs::path destination = raw_exported_dir_path / file; + if (ifw) + { + destination = raw_exported_dir_path / "integration" / "data" / file; + } fs.create_directories(destination.parent_path(), ec); Checks::check_exit(VCPKG_LINE_INFO, !ec); fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec); Checks::check_exit(VCPKG_LINE_INFO, !ec); } + if (ifw) + { + // Unigue packages + IFW::export_unique_packages(raw_exported_dir_path, unique_packages, fs); + // Unigue triplets + IFW::export_unique_triplets(raw_exported_dir_path, unique_triplets, fs); + // Integration + IFW::export_integration(raw_exported_dir_path, fs); + // Configuration + IFW::export_config(raw_exported_dir_path, maybe_ifw_repository_url.value_or(""), fs); + } + const auto print_next_step_info = [](const fs::path& prefix) { const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; const CMakeVariable cmake_variable = @@ -417,7 +460,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console print_next_step_info("[...]"); } - if (!raw) + if (!raw && !ifw) { fs.remove_all(raw_exported_dir_path, ec); } diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp new file mode 100644 index 000000000..ceedac9a3 --- /dev/null +++ b/toolsrc/src/commands_export_ifw.cpp @@ -0,0 +1,319 @@ +#include "pch.h" + +#include "vcpkg_Commands_Export_IFW.h" + +namespace vcpkg::Commands::Export::IFW +{ + using Dependencies::ExportPlanAction; + + fs::path export_real_package(const fs::path &raw_exported_dir_path, const ExportPlanAction& action, Files::Filesystem& fs) + { + std::error_code ec; + + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + // Prepare meta dir + const fs::path package_xml_file_path = raw_exported_dir_path / Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" / "package.xml"; + const fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); + std::vector lines; + std::string line; std::string skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += action.spec.to_string(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += binary_paragraph.version; // TODO: Check IFW version format + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "2017-08-31"; // TODO: Get real package release date + line += ""; + //if (!binary_paragraph.depends.empty()) + //{ + // lines.push_back(line); line = skip; + // line += ""; + //// line += Strings::format("triplets.%s:", action.spec.triplet().canonical_name()); + // line += Strings::format("packages.%s.%s:", binary_paragraph.depends[0], action.spec.triplet().canonical_name()); + // for (size_t i = 1; i < binary_paragraph.depends.size(); ++i) + // { + // line += Strings::format(",packages.%s.%s:", binary_paragraph.depends[i], action.spec.triplet().canonical_name()); + // } + // line += ""; + //} + lines.push_back(line); line = skip; + line += ""; + line += Strings::format("packages.%s:,triplets.%s:", action.spec.name(), action.spec.triplet().canonical_name()); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "true"; // NOTE: hide real package + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "true"; + line += ""; + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(package_xml_file_path, lines); + + // Return dir path for export package data + return raw_exported_dir_path / Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" / "installed"; + } + + void export_unique_packages(const fs::path &raw_exported_dir_path, std::map unique_packages, Files::Filesystem& fs) + { + std::error_code ec; + + // packages + + fs::path package_xml_file_path = raw_exported_dir_path / "packages" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); + std::vector lines; + std::string line; std::string skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "Packages"; + line += ""; + lines.push_back(line); line = skip; + line = ""; + line += "1.0.0"; // TODO: Get real packages package version + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "2017-08-31"; // TODO: Get real package release date + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "true"; + line += ""; + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(package_xml_file_path, lines); + + for (auto package = unique_packages.begin(); package != unique_packages.end(); ++package) + { + const ExportPlanAction& action = *(package->second); + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + package_xml_file_path = raw_exported_dir_path / Strings::format("packages.%s", package->first) / "meta" / "package.xml"; + package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); + lines.clear(); + line.clear(); + skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += action.spec.name(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += binary_paragraph.description; + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += binary_paragraph.version; // TODO: Check IFW version format + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "2017-08-31"; // TODO: Get real package release date + line += ""; + if (!binary_paragraph.depends.empty()) + { + lines.push_back(line); line = skip; + line += ""; + line += Strings::format("packages.%s:", binary_paragraph.depends[0]); + for (size_t i = 1; i < binary_paragraph.depends.size(); ++i) + { + line += Strings::format(",packages.%s:", binary_paragraph.depends[i]); + } + line += ""; + } + lines.push_back(line); line = skip; + line += ""; + line += "true"; + line += ""; + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(package_xml_file_path, lines); + } + } + + void export_unique_triplets(const fs::path &raw_exported_dir_path, std::set unique_triplets, Files::Filesystem& fs) + { + std::error_code ec; + + // triplets + + fs::path package_xml_file_path = raw_exported_dir_path / "triplets" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); + std::vector lines; + std::string line; std::string skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "Triplets"; + line += ""; + lines.push_back(line); line = skip; + line = ""; + line += "1.0.0"; // TODO: Get real triplits package version + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "2017-08-31"; // TODO: Get real package release date + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "true"; + line += ""; + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(package_xml_file_path, lines); + + for (const std::string &triplet : unique_triplets) + { + package_xml_file_path = raw_exported_dir_path / Strings::format("triplets.%s", triplet) / "meta" / "package.xml"; + package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); + lines.clear(); + line.clear(); + skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += triplet; + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "1.0.0"; // TODO: Get real package version + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "2017-08-31"; // TODO: Get real package release date + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "true"; + line += ""; + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(package_xml_file_path, lines); + } + } + + void export_integration(const fs::path &raw_exported_dir_path, Files::Filesystem& fs) + { + std::error_code ec; + + // integration + fs::path package_xml_file_path = raw_exported_dir_path / "integration" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); + std::vector lines; + std::string line; std::string skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "Integration"; + line += ""; + lines.push_back(line); line = skip; + line = ""; + line += "1.0.0"; // TODO: Get real integration package version + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "2017-08-31"; // TODO: Get real package release date + line += ""; + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(package_xml_file_path, lines); + } + + void export_config(const fs::path &raw_exported_dir_path, const std::string ifw_repository_url, Files::Filesystem& fs) + { + std::error_code ec; + + // config.xml + fs::path config_xml_file_path = raw_exported_dir_path / "config.xml"; + fs::path config_xml_dir_path = config_xml_file_path.parent_path(); + fs.create_directories(config_xml_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for configuration file %s", config_xml_file_path.generic_string()); + std::vector lines; + std::string line; std::string skip = " "; + line = ""; + lines.push_back(line); line.clear(); + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "vcpkg"; + line += ""; + lines.push_back(line); line = skip; + line += ""; + line += "1.0.0"; // TODO: Get real vcpkg installer version + line += ""; + //lines.push_back(line); line = skip; + //line += "true"; + lines.push_back(line); line = skip; + line += ""; + line += "@RootDir@/src/vcpkg"; + line += ""; + if (!ifw_repository_url.empty()) + { + lines.push_back(line); line = skip; + line += ""; + lines.push_back(line); line = skip + skip; + line += ""; + lines.push_back(line); line = skip + skip + skip; + line += ""; + line += ifw_repository_url; + line += ""; + lines.push_back(line); line = skip + skip; + line += ""; + lines.push_back(line); line = skip; + line += ""; + } + lines.push_back(line); line.clear(); + line = ""; + lines.push_back(line); line.clear(); + fs.write_lines(config_xml_file_path, lines); + } +} -- cgit v1.2.3 From 5199507a5892ab997b1beee2f1b7d2a6c7e75115 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Thu, 21 Sep 2017 02:27:00 -0700 Subject: [vcpkg-export-ifw] Use template approach for xml instead of line-by-line --- toolsrc/src/commands_export.cpp | 18 +- toolsrc/src/commands_export_ifw.cpp | 397 ++++++++++++++---------------------- 2 files changed, 161 insertions(+), 254 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp index 139f19026..c143fb9c7 100644 --- a/toolsrc/src/commands_export.cpp +++ b/toolsrc/src/commands_export.cpp @@ -257,7 +257,8 @@ namespace vcpkg::Commands::Export if (!raw && !nuget && !ifw && !zip && !seven_zip && !dry_run) { - System::println(System::Color::error, "Must provide at least one export type: --raw --nuget --ifw --zip --7zip"); + System::println(System::Color::error, + "Must provide at least one export type: --raw --nuget --ifw --zip --7zip"); System::print(EXAMPLE); Checks::exit_fail(VCPKG_LINE_INFO); } @@ -271,7 +272,8 @@ namespace vcpkg::Commands::Export auto maybe_ifw_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); - Checks::check_exit(VCPKG_LINE_INFO, !maybe_ifw_repository_url || ifw, "--ifw-repository-url is only valid with --ifw"); + Checks::check_exit( + VCPKG_LINE_INFO, !maybe_ifw_repository_url || ifw, "--ifw-repository-url is only valid with --ifw"); // create the plan const StatusParagraphs status_db = database_load_check(paths); @@ -354,10 +356,10 @@ namespace vcpkg::Commands::Export spec_exported_dir_path = IFW::export_real_package(raw_exported_dir_path, action, fs); } - const InstallDir dirs = InstallDir::from_destination_root( - spec_exported_dir_path, - action.spec.triplet().to_string(), - spec_exported_dir_path / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); + const InstallDir dirs = InstallDir::from_destination_root(spec_exported_dir_path, + action.spec.triplet().to_string(), + spec_exported_dir_path / "vcpkg" / "info" / + (binary_paragraph.fullstem() + ".list")); Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); System::println(System::Color::success, "Exporting package %s... done", display_name); @@ -391,9 +393,9 @@ namespace vcpkg::Commands::Export if (ifw) { - // Unigue packages + // Unique packages IFW::export_unique_packages(raw_exported_dir_path, unique_packages, fs); - // Unigue triplets + // Unique triplets IFW::export_unique_triplets(raw_exported_dir_path, unique_triplets, fs); // Integration IFW::export_integration(raw_exported_dir_path, fs); diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index ceedac9a3..4b5bdb902 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -6,7 +6,9 @@ namespace vcpkg::Commands::Export::IFW { using Dependencies::ExportPlanAction; - fs::path export_real_package(const fs::path &raw_exported_dir_path, const ExportPlanAction& action, Files::Filesystem& fs) + fs::path export_real_package(const fs::path& raw_exported_dir_path, + const ExportPlanAction& action, + Files::Filesystem& fs) { std::error_code ec; @@ -14,97 +16,64 @@ namespace vcpkg::Commands::Export::IFW action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; // Prepare meta dir - const fs::path package_xml_file_path = raw_exported_dir_path / Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" / "package.xml"; + const fs::path package_xml_file_path = + raw_exported_dir_path / + Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" / + "package.xml"; const fs::path package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - std::vector lines; - std::string line; std::string skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += action.spec.to_string(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += binary_paragraph.version; // TODO: Check IFW version format - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "2017-08-31"; // TODO: Get real package release date - line += ""; - //if (!binary_paragraph.depends.empty()) - //{ - // lines.push_back(line); line = skip; - // line += ""; - //// line += Strings::format("triplets.%s:", action.spec.triplet().canonical_name()); - // line += Strings::format("packages.%s.%s:", binary_paragraph.depends[0], action.spec.triplet().canonical_name()); - // for (size_t i = 1; i < binary_paragraph.depends.size(); ++i) - // { - // line += Strings::format(",packages.%s.%s:", binary_paragraph.depends[i], action.spec.triplet().canonical_name()); - // } - // line += ""; - //} - lines.push_back(line); line = skip; - line += ""; - line += Strings::format("packages.%s:,triplets.%s:", action.spec.name(), action.spec.triplet().canonical_name()); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "true"; // NOTE: hide real package - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "true"; - line += ""; - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(package_xml_file_path, lines); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + + fs.write_contents(package_xml_file_path, + Strings::format(R"###( + + + %s + %s + 2017-08-31 + packages.%s:,triplets.%s: + true + true + +)###", + action.spec.to_string(), + binary_paragraph.version, + action.spec.name(), + action.spec.triplet().canonical_name())); // Return dir path for export package data - return raw_exported_dir_path / Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" / "installed"; + return raw_exported_dir_path / + Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" / + "installed"; } - void export_unique_packages(const fs::path &raw_exported_dir_path, std::map unique_packages, Files::Filesystem& fs) - { - std::error_code ec; - + void export_unique_packages(const fs::path& raw_exported_dir_path, + std::map unique_packages, + Files::Filesystem& fs) + { + std::error_code ec; + // packages fs::path package_xml_file_path = raw_exported_dir_path / "packages" / "meta" / "package.xml"; fs::path package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - std::vector lines; - std::string line; std::string skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "Packages"; - line += ""; - lines.push_back(line); line = skip; - line = ""; - line += "1.0.0"; // TODO: Get real packages package version - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "2017-08-31"; // TODO: Get real package release date - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "true"; - line += ""; - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(package_xml_file_path, lines); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, R"###( + + + Packages + 1.0.0 + 2017-08-31 + true + +)###"); for (auto package = unique_packages.begin(); package != unique_packages.end(); ++package) { @@ -112,56 +81,41 @@ namespace vcpkg::Commands::Export::IFW const BinaryParagraph& binary_paragraph = action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - package_xml_file_path = raw_exported_dir_path / Strings::format("packages.%s", package->first) / "meta" / "package.xml"; + package_xml_file_path = + raw_exported_dir_path / Strings::format("packages.%s", package->first) / "meta" / "package.xml"; package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - lines.clear(); - line.clear(); - skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += action.spec.name(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += binary_paragraph.description; - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += binary_paragraph.version; // TODO: Check IFW version format - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "2017-08-31"; // TODO: Get real package release date - line += ""; - if (!binary_paragraph.depends.empty()) - { - lines.push_back(line); line = skip; - line += ""; - line += Strings::format("packages.%s:", binary_paragraph.depends[0]); - for (size_t i = 1; i < binary_paragraph.depends.size(); ++i) - { - line += Strings::format(",packages.%s:", binary_paragraph.depends[i]); - } - line += ""; - } - lines.push_back(line); line = skip; - line += ""; - line += "true"; - line += ""; - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(package_xml_file_path, lines); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + + auto deps = Strings::join( + ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); + + if (!deps.empty()) deps = "\n " + deps; + + fs.write_contents(package_xml_file_path, + Strings::format(R"###( + + + %s + %s + %s + 2017-08-31%s + true + +)###", + action.spec.name(), + binary_paragraph.description, + binary_paragraph.version, + deps)); } } - void export_unique_triplets(const fs::path &raw_exported_dir_path, std::set unique_triplets, Files::Filesystem& fs) + void export_unique_triplets(const fs::path& raw_exported_dir_path, + std::set unique_triplets, + Files::Filesystem& fs) { std::error_code ec; @@ -170,71 +124,43 @@ namespace vcpkg::Commands::Export::IFW fs::path package_xml_file_path = raw_exported_dir_path / "triplets" / "meta" / "package.xml"; fs::path package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - std::vector lines; - std::string line; std::string skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "Triplets"; - line += ""; - lines.push_back(line); line = skip; - line = ""; - line += "1.0.0"; // TODO: Get real triplits package version - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "2017-08-31"; // TODO: Get real package release date - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "true"; - line += ""; - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(package_xml_file_path, lines); - - for (const std::string &triplet : unique_triplets) + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, R"###( + + + Triplets + 1.0.0 + 2017-08-31 + true + +)###"); + + for (const std::string& triplet : unique_triplets) { - package_xml_file_path = raw_exported_dir_path / Strings::format("triplets.%s", triplet) / "meta" / "package.xml"; + package_xml_file_path = + raw_exported_dir_path / Strings::format("triplets.%s", triplet) / "meta" / "package.xml"; package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - lines.clear(); - line.clear(); - skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += triplet; - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "1.0.0"; // TODO: Get real package version - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "2017-08-31"; // TODO: Get real package release date - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "true"; - line += ""; - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(package_xml_file_path, lines); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, Strings::format(R"###( + + + %s + 1.0.0 + 2017-08-31 + true + +)###", triplet)); } } - void export_integration(const fs::path &raw_exported_dir_path, Files::Filesystem& fs) + void export_integration(const fs::path& raw_exported_dir_path, Files::Filesystem& fs) { std::error_code ec; @@ -242,32 +168,25 @@ namespace vcpkg::Commands::Export::IFW fs::path package_xml_file_path = raw_exported_dir_path / "integration" / "meta" / "package.xml"; fs::path package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - std::vector lines; - std::string line; std::string skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "Integration"; - line += ""; - lines.push_back(line); line = skip; - line = ""; - line += "1.0.0"; // TODO: Get real integration package version - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "2017-08-31"; // TODO: Get real package release date - line += ""; - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(package_xml_file_path, lines); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + + fs.write_contents(package_xml_file_path, R"###( + + + Integration + 1.0.0 + 2017-08-31 + true + +)###"); } - void export_config(const fs::path &raw_exported_dir_path, const std::string ifw_repository_url, Files::Filesystem& fs) + void export_config(const fs::path& raw_exported_dir_path, + const std::string ifw_repository_url, + Files::Filesystem& fs) { std::error_code ec; @@ -275,45 +194,31 @@ namespace vcpkg::Commands::Export::IFW fs::path config_xml_file_path = raw_exported_dir_path / "config.xml"; fs::path config_xml_dir_path = config_xml_file_path.parent_path(); fs.create_directories(config_xml_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for configuration file %s", config_xml_file_path.generic_string()); - std::vector lines; - std::string line; std::string skip = " "; - line = ""; - lines.push_back(line); line.clear(); - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "vcpkg"; - line += ""; - lines.push_back(line); line = skip; - line += ""; - line += "1.0.0"; // TODO: Get real vcpkg installer version - line += ""; - //lines.push_back(line); line = skip; - //line += "true"; - lines.push_back(line); line = skip; - line += ""; - line += "@RootDir@/src/vcpkg"; - line += ""; + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for configuration file %s", + config_xml_file_path.generic_string()); + std::string formatted_repo_url; if (!ifw_repository_url.empty()) { - lines.push_back(line); line = skip; - line += ""; - lines.push_back(line); line = skip + skip; - line += ""; - lines.push_back(line); line = skip + skip + skip; - line += ""; - line += ifw_repository_url; - line += ""; - lines.push_back(line); line = skip + skip; - line += ""; - lines.push_back(line); line = skip; - line += ""; + formatted_repo_url = Strings::format(R"###( + + + %s + + +)###", + formatted_repo_url); } - lines.push_back(line); line.clear(); - line = ""; - lines.push_back(line); line.clear(); - fs.write_lines(config_xml_file_path, lines); + + fs.write_contents(config_xml_file_path, Strings::format(R"###( + + + vcpkg + 1.0.0 + 2017-08-31 + @RootDir@/src/vcpkg%s + +)###", formatted_repo_url)); } } -- cgit v1.2.3 From 68b9c2d8b9119acb48643447a7561a5c2b733d25 Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Fri, 22 Sep 2017 02:16:14 +0300 Subject: [vcpkg-export-ifw] Separate IFW loop Separate IFW loop compatible with main export loop Fixed mistakes in templates Set current date to ReleaseDate tag --- toolsrc/src/commands_export.cpp | 253 ++++++++++++++++++------------------ toolsrc/src/commands_export_ifw.cpp | 183 ++++++++++++++++++++------ 2 files changed, 268 insertions(+), 168 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp index c143fb9c7..6632dfe67 100644 --- a/toolsrc/src/commands_export.cpp +++ b/toolsrc/src/commands_export.cpp @@ -211,6 +211,32 @@ namespace vcpkg::Commands::Export return nullopt; } + void export_integration_files(const fs::path &raw_exported_dir_path, const VcpkgPaths& paths) + { + const std::vector integration_files_relative_to_root = { + { ".vcpkg-root" }, + { fs::path{ "scripts" } / "buildsystems" / "msbuild" / "applocal.ps1" }, + { fs::path{ "scripts" } / "buildsystems" / "msbuild" / "vcpkg.targets" }, + { fs::path{ "scripts" } / "buildsystems" / "vcpkg.cmake" }, + { fs::path{ "scripts" } / "cmake" / "vcpkg_get_windows_sdk.cmake" }, + { fs::path{ "scripts" } / "getWindowsSDK.ps1" }, + { fs::path{ "scripts" } / "getProgramFilesPlatformBitness.ps1" }, + { fs::path{ "scripts" } / "getProgramFiles32bit.ps1" }, + }; + + for (const fs::path& file : integration_files_relative_to_root) + { + const fs::path source = paths.root / file; + fs::path destination = raw_exported_dir_path / file; + Files::Filesystem& fs = paths.get_filesystem(); + std::error_code ec; + fs.create_directories(destination.parent_path(), ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec); + fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec); + } + } + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) { static const std::string OPTION_DRY_RUN = "--dry-run"; @@ -222,6 +248,8 @@ namespace vcpkg::Commands::Export static const std::string OPTION_NUGET_ID = "--nuget-id"; static const std::string OPTION_NUGET_VERSION = "--nuget-version"; static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url"; + static const std::string OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path"; + static const std::string OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path"; // input sanitization static const std::string EXAMPLE = @@ -247,6 +275,8 @@ namespace vcpkg::Commands::Export OPTION_NUGET_ID, OPTION_NUGET_VERSION, OPTION_IFW_REPOSITORY_URL, + OPTION_IFW_PACKAGES_DIR_PATH, + OPTION_IFW_CONFIG_FILE_PATH }); const bool dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); const bool raw = options.switches.find(OPTION_RAW) != options.switches.cend(); @@ -270,10 +300,19 @@ namespace vcpkg::Commands::Export Checks::check_exit( VCPKG_LINE_INFO, !maybe_nuget_version || nuget, "--nuget-version is only valid with --nuget"); - auto maybe_ifw_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); + IFW::Options ifw_options; + + ifw_options.maybe_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); + Checks::check_exit( + VCPKG_LINE_INFO, !ifw_options.maybe_repository_url || ifw, "--ifw-repository-url is only valid with --ifw"); + + ifw_options.maybe_packages_dir_path = maybe_lookup(options.settings, OPTION_IFW_PACKAGES_DIR_PATH); + Checks::check_exit( + VCPKG_LINE_INFO, !ifw_options.maybe_packages_dir_path || ifw, "--ifw-packages-directory-path is only valid with --ifw"); + ifw_options.maybe_config_file_path = maybe_lookup(options.settings, OPTION_IFW_CONFIG_FILE_PATH); Checks::check_exit( - VCPKG_LINE_INFO, !maybe_ifw_repository_url || ifw, "--ifw-repository-url is only valid with --ifw"); + VCPKG_LINE_INFO, !ifw_options.maybe_config_file_path || ifw, "--ifw-configuration-file-path is only valid with --ifw"); // create the plan const StatusParagraphs status_db = database_load_check(paths); @@ -318,153 +357,117 @@ namespace vcpkg::Commands::Export } std::string export_id = create_export_id(); - if (ifw) - { - export_id = "vcpkg-export"; // TODO: Remove after debugging - } - Files::Filesystem& fs = paths.get_filesystem(); - const fs::path export_to_path = paths.root; - const fs::path raw_exported_dir_path = export_to_path / export_id; - std::error_code ec; - fs.remove_all(raw_exported_dir_path, ec); - fs.create_directory(raw_exported_dir_path, ec); + const auto print_next_step_info = [](const fs::path& prefix) { + const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; + const CMakeVariable cmake_variable = + CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); + System::println("\n" + "To use the exported libraries in CMake projects use:" + "\n" + " %s" + "\n", + Strings::to_utf8(cmake_variable.s)); + }; - // execute the plan - std::map unique_packages; - std::set unique_triplets; - for (const ExportPlanAction& action : export_plan) + // Main loop + if (raw || nuget || zip || seven_zip) { - if (action.plan_type != ExportPlanType::ALREADY_BUILT) + Files::Filesystem& fs = paths.get_filesystem(); + const fs::path export_to_path = paths.root; + const fs::path raw_exported_dir_path = export_to_path / export_id; + std::error_code ec; + fs.remove_all(raw_exported_dir_path, ec); + fs.create_directory(raw_exported_dir_path, ec); + + // execute the plan + for (const ExportPlanAction& action : export_plan) { - Checks::unreachable(VCPKG_LINE_INFO); - } + if (action.plan_type != ExportPlanType::ALREADY_BUILT) + { + Checks::unreachable(VCPKG_LINE_INFO); + } - const std::string display_name = action.spec.to_string(); - System::println("Exporting package %s... ", display_name); + const std::string display_name = action.spec.to_string(); + System::println("Exporting package %s... ", display_name); - const BinaryParagraph& binary_paragraph = - action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - unique_packages[action.spec.name()] = &action; - unique_triplets.insert(action.spec.triplet().canonical_name()); + const InstallDir dirs = InstallDir::from_destination_root( + raw_exported_dir_path / "installed", + action.spec.triplet().to_string(), + raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); - fs::path spec_exported_dir_path = raw_exported_dir_path / "installed"; - if (ifw) - { - // Export real package and return data dir for installation - spec_exported_dir_path = IFW::export_real_package(raw_exported_dir_path, action, fs); + Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); + System::println(System::Color::success, "Exporting package %s... done", display_name); } - const InstallDir dirs = InstallDir::from_destination_root(spec_exported_dir_path, - action.spec.triplet().to_string(), - spec_exported_dir_path / "vcpkg" / "info" / - (binary_paragraph.fullstem() + ".list")); - - Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); - System::println(System::Color::success, "Exporting package %s... done", display_name); - } - - // Copy files needed for integration - const std::vector integration_files_relative_to_root = { - {".vcpkg-root"}, - {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"}, - {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, - {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"}, - {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, - {fs::path{"scripts"} / "getWindowsSDK.ps1"}, - {fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"}, - {fs::path{"scripts"} / "getProgramFiles32bit.ps1"}, - }; + // Copy files needed for integration + export_integration_files(raw_exported_dir_path, paths); - for (const fs::path& file : integration_files_relative_to_root) - { - const fs::path source = paths.root / file; - fs::path destination = raw_exported_dir_path / file; - if (ifw) + if (raw) { - destination = raw_exported_dir_path / "integration" / "data" / file; + System::println( + System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string()); + print_next_step_info(export_to_path); } - fs.create_directories(destination.parent_path(), ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec); - fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec); - } - if (ifw) - { - // Unique packages - IFW::export_unique_packages(raw_exported_dir_path, unique_packages, fs); - // Unique triplets - IFW::export_unique_triplets(raw_exported_dir_path, unique_triplets, fs); - // Integration - IFW::export_integration(raw_exported_dir_path, fs); - // Configuration - IFW::export_config(raw_exported_dir_path, maybe_ifw_repository_url.value_or(""), fs); - } - - const auto print_next_step_info = [](const fs::path& prefix) { - const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; - const CMakeVariable cmake_variable = - CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); - System::println("\n" - "To use the exported libraries in CMake projects use:" - "\n" - " %s" - "\n", - Strings::to_utf8(cmake_variable.s)); - }; - - if (raw) - { - System::println( - System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string()); - print_next_step_info(export_to_path); - } - - if (nuget) - { - System::println("Creating nuget package... "); + if (nuget) + { + System::println("Creating nuget package... "); - const std::string nuget_id = maybe_nuget_id.value_or(raw_exported_dir_path.filename().string()); - const std::string nuget_version = maybe_nuget_version.value_or("1.0.0"); - const fs::path output_path = - do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path); - System::println(System::Color::success, "Creating nuget package... done"); - System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string()); + const std::string nuget_id = maybe_nuget_id.value_or(raw_exported_dir_path.filename().string()); + const std::string nuget_version = maybe_nuget_version.value_or("1.0.0"); + const fs::path output_path = + do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path); + System::println(System::Color::success, "Creating nuget package... done"); + System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string()); - System::println(R"( + System::println(R"( With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: Install-Package %s -Source "%s" )" - "\n", - nuget_id, - output_path.parent_path().u8string()); - } + "\n", + nuget_id, + output_path.parent_path().u8string()); + } - if (zip) - { - System::println("Creating zip archive... "); - const fs::path output_path = - do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP); - System::println(System::Color::success, "Creating zip archive... done"); - System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string()); - print_next_step_info("[...]"); - } + if (zip) + { + System::println("Creating zip archive... "); + const fs::path output_path = + do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP); + System::println(System::Color::success, "Creating zip archive... done"); + System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string()); + print_next_step_info("[...]"); + } - if (seven_zip) - { - System::println("Creating 7zip archive... "); - const fs::path output_path = - do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP); - System::println(System::Color::success, "Creating 7zip archive... done"); - System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string()); - print_next_step_info("[...]"); + if (seven_zip) + { + System::println("Creating 7zip archive... "); + const fs::path output_path = + do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP); + System::println(System::Color::success, "Creating 7zip archive... done"); + System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string()); + print_next_step_info("[...]"); + } + + if (!raw) + { + fs.remove_all(raw_exported_dir_path, ec); + } } - if (!raw && !ifw) + // IFW loop + if (ifw) { - fs.remove_all(raw_exported_dir_path, ec); + IFW::do_export(export_plan, export_id, ifw_options, paths); + + // TODO: Download corrected QtIFW tools and automate installer creation + System::println("Use corrected QtIFW tools (for more info see: https://codereview.qt-project.org/#/c/203958) to create installer..."); + + print_next_step_info("[...]"); } Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index 4b5bdb902..e2499b75d 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -1,10 +1,30 @@ #include "pch.h" +#include "vcpkg_Commands.h" +#include "vcpkg_Commands_Export.h" #include "vcpkg_Commands_Export_IFW.h" namespace vcpkg::Commands::Export::IFW { using Dependencies::ExportPlanAction; + using Dependencies::ExportPlanType; + using Install::InstallDir; + + static std::string create_release_date() + { + const tm date_time = System::get_current_date_time(); + + // Format is: YYYY-mm-dd + // 10 characters + 1 null terminating character will be written for a total of 11 chars + char mbstr[11]; + const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d", &date_time); + Checks::check_exit(VCPKG_LINE_INFO, + bytes_written == 10, + "Expected 10 bytes to be written, but %u were written", + bytes_written); + const std::string date_time_as_string(mbstr); + return date_time_as_string; + } fs::path export_real_package(const fs::path& raw_exported_dir_path, const ExportPlanAction& action, @@ -28,19 +48,19 @@ namespace vcpkg::Commands::Export::IFW package_xml_file_path.generic_string()); fs.write_contents(package_xml_file_path, - Strings::format(R"###( - + Strings::format( +R"###( %s %s - 2017-08-31 - packages.%s:,triplets.%s: + %s + packages.%s:,triplets.%s: true - true )###", action.spec.to_string(), binary_paragraph.version, + create_release_date(), action.spec.name(), action.spec.triplet().canonical_name())); @@ -65,15 +85,15 @@ namespace vcpkg::Commands::Export::IFW !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, R"###( - + fs.write_contents(package_xml_file_path, Strings::format( +R"###( Packages 1.0.0 - 2017-08-31 - true + %s -)###"); +)###", + create_release_date())); for (auto package = unique_packages.begin(); package != unique_packages.end(); ++package) { @@ -93,22 +113,22 @@ namespace vcpkg::Commands::Export::IFW auto deps = Strings::join( ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); - if (!deps.empty()) deps = "\n " + deps; + if (!deps.empty()) deps = "\n " + deps + ""; fs.write_contents(package_xml_file_path, - Strings::format(R"###( - + Strings::format( +R"###( %s %s %s - 2017-08-31%s - true + %s%s )###", action.spec.name(), binary_paragraph.description, binary_paragraph.version, + create_release_date(), deps)); } } @@ -128,15 +148,15 @@ namespace vcpkg::Commands::Export::IFW !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, R"###( - + fs.write_contents(package_xml_file_path, Strings::format( +R"###( Triplets 1.0.0 - 2017-08-31 - true + %s -)###"); +)###", + create_release_date())); for (const std::string& triplet : unique_triplets) { @@ -148,15 +168,16 @@ namespace vcpkg::Commands::Export::IFW !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, Strings::format(R"###( - + fs.write_contents(package_xml_file_path, Strings::format( +R"###( %s 1.0.0 - 2017-08-31 - true + %s -)###", triplet)); +)###", + triplet, + create_release_date())); } } @@ -173,52 +194,128 @@ namespace vcpkg::Commands::Export::IFW "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, R"###( - + fs.write_contents(package_xml_file_path, Strings::format( +R"###( Integration 1.0.0 - 2017-08-31 - true + %s -)###"); +)###", + create_release_date())); } - void export_config(const fs::path& raw_exported_dir_path, - const std::string ifw_repository_url, - Files::Filesystem& fs) + void export_config(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) { std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + const fs::path config_xml_file_path = ifw_options.maybe_config_file_path.has_value() ? + fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-configuration") / "config.xml"; - // config.xml - fs::path config_xml_file_path = raw_exported_dir_path / "config.xml"; fs::path config_xml_dir_path = config_xml_file_path.parent_path(); fs.create_directories(config_xml_dir_path, ec); Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not create directory for configuration file %s", config_xml_file_path.generic_string()); + std::string formatted_repo_url; - if (!ifw_repository_url.empty()) + std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); + if (!ifw_repo_url.empty()) { formatted_repo_url = Strings::format(R"###( %s - -)###", - formatted_repo_url); + )###", + ifw_repo_url); } - fs.write_contents(config_xml_file_path, Strings::format(R"###( - + fs.write_contents(config_xml_file_path, Strings::format( +R"###( vcpkg 1.0.0 - 2017-08-31 @RootDir@/src/vcpkg%s -)###", formatted_repo_url)); +)###", + formatted_repo_url)); + + System::println("Created ifw configuration file: %s", config_xml_file_path.generic_string()); + } + + void do_export(const std::vector &export_plan, const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + System::println("Creating ifw packages... "); + + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + const fs::path ifw_packages_dir_path = ifw_options.maybe_packages_dir_path.has_value() ? + fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-packages"); + + fs.remove_all(ifw_packages_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not remove outdated packages directory %s", + ifw_packages_dir_path.generic_string()); + + fs.create_directory(ifw_packages_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create packages directory %s", + ifw_packages_dir_path.generic_string()); + + // execute the plan + std::map unique_packages; + std::set unique_triplets; + for (const ExportPlanAction& action : export_plan) + { + if (action.plan_type != ExportPlanType::ALREADY_BUILT) + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + const std::string display_name = action.spec.to_string(); + System::println("Exporting package %s... ", display_name); + + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + unique_packages[action.spec.name()] = &action; + unique_triplets.insert(action.spec.triplet().canonical_name()); + + // Export real package and return data dir for installation + fs::path ifw_package_dir_path = export_real_package(ifw_packages_dir_path, action, fs); + + // Copy package data + const InstallDir dirs = InstallDir::from_destination_root(ifw_package_dir_path, + action.spec.triplet().to_string(), + ifw_package_dir_path / "vcpkg" / "info" / + (binary_paragraph.fullstem() + ".list")); + + Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); + System::println(System::Color::success, "Exporting package %s... done", display_name); + } + + // Unique packages + export_unique_packages(ifw_packages_dir_path, unique_packages, fs); + + // Unique triplets + export_unique_triplets(ifw_packages_dir_path, unique_triplets, fs); + + // Copy files needed for integration + export_integration_files(ifw_packages_dir_path / "integration" / "data", paths); + // Integration + export_integration(ifw_packages_dir_path, fs); + + System::println("Created ifw packages directory: %s", ifw_packages_dir_path.generic_string()); + + // Configuration + export_config(export_id, ifw_options, paths); } } -- cgit v1.2.3 From c6149fae2f9f33d9ed363650aee6aea642574b0a Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Wed, 27 Sep 2017 02:57:51 +0300 Subject: [vcpkg-export-ifw] Usage QtIFW tools Download and use tools to make repository and installer --- toolsrc/src/VcpkgPaths.cpp | 40 ++++++++++ toolsrc/src/commands_export.cpp | 19 +++-- toolsrc/src/commands_export_ifw.cpp | 145 +++++++++++++++++++++++++++++------- 3 files changed, 174 insertions(+), 30 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index 02e238b3f..89435cd38 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -164,6 +164,31 @@ namespace vcpkg return fetch_dependency(scripts_folder, L"git", downloaded_copy, EXPECTED_VERSION); } + static fs::path get_ifw_installerbase_path(const fs::path& downloads_folder, const fs::path& scripts_folder) + { + static constexpr std::array EXPECTED_VERSION = { 3, 1, 81 }; + static const std::wstring VERSION_CHECK_ARGUMENTS = L"--framework-version"; + + const fs::path downloaded_copy = downloads_folder / "QtInstallerFramework-win-x86" / "bin" / "installerbase.exe"; + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + // TODO: Uncomment later + //const std::vector from_path = Files::find_from_PATH(L"installerbase"); + //candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + //candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe"); + //candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / "QtIFW-3.1.0" / "bin" / "installerbase.exe"); + + const Optional path = + find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); + if (const auto p = path.get()) + { + return *p; + } + + return fetch_dependency(scripts_folder, L"installerbase", downloaded_copy, EXPECTED_VERSION); + } + Expected VcpkgPaths::create(const fs::path& vcpkg_root_dir) { std::error_code ec; @@ -247,6 +272,21 @@ namespace vcpkg return this->nuget_exe.get_lazy([this]() { return get_nuget_path(this->downloads, this->scripts); }); } + const fs::path& VcpkgPaths::get_ifw_installerbase_exe() const + { + return this->ifw_installerbase_exe.get_lazy([this]() { return get_ifw_installerbase_path(this->downloads, this->scripts); }); + } + + const fs::path& VcpkgPaths::get_ifw_binarycreator_exe() const + { + return this->ifw_binarycreator_exe.get_lazy([this]() { return get_ifw_installerbase_exe().parent_path() / "binarycreator.exe"; }); + } + + const fs::path& VcpkgPaths::get_ifw_repogen_exe() const + { + return this->ifw_repogen_exe.get_lazy([this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; }); + } + static std::vector get_vs2017_installation_instances(const VcpkgPaths& paths) { const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp index 6632dfe67..3662a46d8 100644 --- a/toolsrc/src/commands_export.cpp +++ b/toolsrc/src/commands_export.cpp @@ -249,7 +249,9 @@ namespace vcpkg::Commands::Export static const std::string OPTION_NUGET_VERSION = "--nuget-version"; static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url"; static const std::string OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path"; + static const std::string OPTION_IFW_REPOSITORY_DIR_PATH = "--ifw-repository-directory-path"; static const std::string OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path"; + static const std::string OPTION_IFW_INSTALLER_FILE_PATH = "--ifw-installer-file-path"; // input sanitization static const std::string EXAMPLE = @@ -276,7 +278,9 @@ namespace vcpkg::Commands::Export OPTION_NUGET_VERSION, OPTION_IFW_REPOSITORY_URL, OPTION_IFW_PACKAGES_DIR_PATH, - OPTION_IFW_CONFIG_FILE_PATH + OPTION_IFW_REPOSITORY_DIR_PATH, + OPTION_IFW_CONFIG_FILE_PATH, + OPTION_IFW_INSTALLER_FILE_PATH }); const bool dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); const bool raw = options.switches.find(OPTION_RAW) != options.switches.cend(); @@ -310,10 +314,18 @@ namespace vcpkg::Commands::Export Checks::check_exit( VCPKG_LINE_INFO, !ifw_options.maybe_packages_dir_path || ifw, "--ifw-packages-directory-path is only valid with --ifw"); + ifw_options.maybe_repository_dir_path = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_DIR_PATH); + Checks::check_exit( + VCPKG_LINE_INFO, !ifw_options.maybe_repository_dir_path || ifw, "--ifw-repository-directory-path is only valid with --ifw"); + ifw_options.maybe_config_file_path = maybe_lookup(options.settings, OPTION_IFW_CONFIG_FILE_PATH); Checks::check_exit( VCPKG_LINE_INFO, !ifw_options.maybe_config_file_path || ifw, "--ifw-configuration-file-path is only valid with --ifw"); + ifw_options.maybe_installer_file_path = maybe_lookup(options.settings, OPTION_IFW_INSTALLER_FILE_PATH); + Checks::check_exit( + VCPKG_LINE_INFO, !ifw_options.maybe_installer_file_path || ifw, "--ifw-installer-file-path is only valid with --ifw"); + // create the plan const StatusParagraphs status_db = database_load_check(paths); std::vector export_plan = Dependencies::create_export_plan(paths, specs, status_db); @@ -464,10 +476,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console { IFW::do_export(export_plan, export_id, ifw_options, paths); - // TODO: Download corrected QtIFW tools and automate installer creation - System::println("Use corrected QtIFW tools (for more info see: https://codereview.qt-project.org/#/c/203958) to create installer..."); - - print_next_step_info("[...]"); + print_next_step_info("@RootDir@/src/vcpkg"); } Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index e2499b75d..d1428f5d1 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -26,7 +26,35 @@ namespace vcpkg::Commands::Export::IFW return date_time_as_string; } - fs::path export_real_package(const fs::path& raw_exported_dir_path, + fs::path get_packages_dir_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_packages_dir_path.has_value() ? + fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-packages"); + } + + fs::path get_repository_dir_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_packages_dir_path.has_value() ? + fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-repository"); + } + + fs::path get_config_file_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_config_file_path.has_value() ? + fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-configuration.xml"); + } + + fs::path get_installer_file_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_config_file_path.has_value() ? + fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-installer.exe"); + } + + fs::path export_real_package(const fs::path& ifw_packages_dir_path, const ExportPlanAction& action, Files::Filesystem& fs) { @@ -37,7 +65,7 @@ namespace vcpkg::Commands::Export::IFW // Prepare meta dir const fs::path package_xml_file_path = - raw_exported_dir_path / + ifw_packages_dir_path / Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" / "package.xml"; const fs::path package_xml_dir_path = package_xml_file_path.parent_path(); @@ -47,6 +75,11 @@ namespace vcpkg::Commands::Export::IFW "Could not create directory for package file %s", package_xml_file_path.generic_string()); + auto deps = Strings::join( + ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); + + if (!deps.empty()) deps = "\n " + deps + ""; + fs.write_contents(package_xml_file_path, Strings::format( R"###( @@ -54,7 +87,7 @@ R"###( %s %s %s - packages.%s:,triplets.%s: + packages.%s:,triplets.%s:%s true )###", @@ -62,10 +95,11 @@ R"###( binary_paragraph.version, create_release_date(), action.spec.name(), - action.spec.triplet().canonical_name())); + action.spec.triplet().canonical_name(), + deps)); // Return dir path for export package data - return raw_exported_dir_path / + return ifw_packages_dir_path / Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" / "installed"; } @@ -110,11 +144,6 @@ R"###( "Could not create directory for package file %s", package_xml_file_path.generic_string()); - auto deps = Strings::join( - ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); - - if (!deps.empty()) deps = "\n " + deps + ""; - fs.write_contents(package_xml_file_path, Strings::format( R"###( @@ -122,14 +151,13 @@ R"###( %s %s %s - %s%s + %s )###", action.spec.name(), binary_paragraph.description, binary_paragraph.version, - create_release_date(), - deps)); + create_release_date())); } } @@ -210,9 +238,7 @@ R"###( std::error_code ec; Files::Filesystem& fs = paths.get_filesystem(); - const fs::path config_xml_file_path = ifw_options.maybe_config_file_path.has_value() ? - fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-configuration") / "config.xml"; + const fs::path config_xml_file_path = get_config_file_path(export_id, ifw_options, paths); fs::path config_xml_dir_path = config_xml_file_path.parent_path(); fs.create_directories(config_xml_dir_path, ec); @@ -243,21 +269,76 @@ R"###( )###", formatted_repo_url)); + } + + void do_repository(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + const fs::path& repogen_exe = paths.get_ifw_repogen_exe(); + const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); + const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths); + + System::println("Generating repository %s...", repository_dir.generic_string()); - System::println("Created ifw configuration file: %s", config_xml_file_path.generic_string()); + const std::wstring cmd_line = + Strings::wformat(LR"("%s" --packages "%s" "%s" > nul)", + repogen_exe.native(), + packages_dir.native(), + repository_dir.native()); + + const int exit_code = System::cmd_execute_clean(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW repository generating failed"); + + System::println(System::Color::success, "Generating repository %s... done.", repository_dir.generic_string()); + } + + void do_installer(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + { + const fs::path& binarycreator_exe = paths.get_ifw_binarycreator_exe(); + const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); + const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); + const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths); + const fs::path installer_file = get_installer_file_path(export_id, ifw_options, paths); + + System::println("Generating installer %s...", installer_file.generic_string()); + + std::wstring cmd_line; + + std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); + if (!ifw_repo_url.empty()) + { + cmd_line = + Strings::wformat(LR"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)", + binarycreator_exe.native(), + config_file.native(), + repository_dir.native(), + installer_file.native()); + } + else + { + cmd_line = + Strings::wformat(LR"("%s" --config "%s" --packages "%s" "%s" > nul)", + binarycreator_exe.native(), + config_file.native(), + packages_dir.native(), + installer_file.native()); + } + + const int exit_code = System::cmd_execute_clean(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW installer generating failed"); + + System::println(System::Color::success, "Generating installer %s... done.", installer_file.generic_string()); } void do_export(const std::vector &export_plan, const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) { - System::println("Creating ifw packages... "); + const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); + const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); + + System::println("Exporting packages %s... ", ifw_packages_dir_path.generic_string()); std::error_code ec; Files::Filesystem& fs = paths.get_filesystem(); - const fs::path ifw_packages_dir_path = ifw_options.maybe_packages_dir_path.has_value() ? - fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-packages"); - fs.remove_all(ifw_packages_dir_path, ec); Checks::check_exit(VCPKG_LINE_INFO, !ec, @@ -299,9 +380,13 @@ R"###( (binary_paragraph.fullstem() + ".list")); Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); - System::println(System::Color::success, "Exporting package %s... done", display_name); + System::println("Exporting package %s... done", display_name); } + System::println("Exporting packages %s... done", ifw_packages_dir_path.generic_string()); + + System::println("Generating configuration %s...", config_file.generic_string()); + // Unique packages export_unique_packages(ifw_packages_dir_path, unique_packages, fs); @@ -313,9 +398,19 @@ R"###( // Integration export_integration(ifw_packages_dir_path, fs); - System::println("Created ifw packages directory: %s", ifw_packages_dir_path.generic_string()); - // Configuration export_config(export_id, ifw_options, paths); + + System::println("Generating configuration %s... done.", config_file.generic_string()); + + // Do repository (optional) + std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); + if (!ifw_repo_url.empty()) + { + do_repository(export_id, ifw_options, paths); + } + + // Do installer + do_installer(export_id, ifw_options, paths); } } -- cgit v1.2.3 From d25fd5c7b3b0f21581dd9c44a915c9156683877a Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Thu, 28 Sep 2017 01:00:40 +0300 Subject: [vcpkg-export-ifw] Some improvements Improvements: - fix typos; - remove outdated repository directory. --- toolsrc/src/commands_export_ifw.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index d1428f5d1..a0692c11e 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -35,7 +35,7 @@ namespace vcpkg::Commands::Export::IFW fs::path get_repository_dir_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) { - return ifw_options.maybe_packages_dir_path.has_value() ? + return ifw_options.maybe_repository_dir_path.has_value() ? fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO)) : paths.root / (export_id + "-ifw-repository"); } @@ -49,7 +49,7 @@ namespace vcpkg::Commands::Export::IFW fs::path get_installer_file_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) { - return ifw_options.maybe_config_file_path.has_value() ? + return ifw_options.maybe_installer_file_path.has_value() ? fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO)) : paths.root / (export_id + "-ifw-installer.exe"); } @@ -279,6 +279,15 @@ R"###( System::println("Generating repository %s...", repository_dir.generic_string()); + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + fs.remove_all(repository_dir, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not remove outdated repository directory %s", + repository_dir.generic_string()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" --packages "%s" "%s" > nul)", repogen_exe.native(), -- cgit v1.2.3 From b3e06443eab560d5de848f2a066e1baa477fa57b Mon Sep 17 00:00:00 2001 From: Maria Tavlaki Date: Sun, 1 Oct 2017 22:17:33 +0300 Subject: Create stub autocomplete function --- toolsrc/src/commands_autocomplete.cpp | 22 ++++++++++++++++++++++ toolsrc/src/commands_available_commands.cpp | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 toolsrc/src/commands_autocomplete.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp new file mode 100644 index 000000000..71154445d --- /dev/null +++ b/toolsrc/src/commands_autocomplete.cpp @@ -0,0 +1,22 @@ +#include "pch.h" + +#include "Paragraphs.h" +#include "SortedVector.h" +#include "vcpkg_Commands.h" +#include "vcpkg_Maps.h" +#include "vcpkg_System.h" + +namespace vcpkg::Commands::Autocomplete +{ + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = + Strings::format("The argument should be a command line to autocomplete.\n%s", + Commands::Help::create_example_string("autocomplete install z")); + + args.check_max_arg_count(1, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/commands_available_commands.cpp b/toolsrc/src/commands_available_commands.cpp index 87cc43dca..d3280e6d7 100644 --- a/toolsrc/src/commands_available_commands.cpp +++ b/toolsrc/src/commands_available_commands.cpp @@ -34,7 +34,7 @@ namespace vcpkg::Commands {"import", &Import::perform_and_exit}, {"cache", &Cache::perform_and_exit}, {"portsdiff", &PortsDiff::perform_and_exit}, - }; + {"autocomplete", &Autocomplete::perform_and_exit}}; return t; } -- cgit v1.2.3 From fe89e72e9565f0515834b925f015fe19324c38da Mon Sep 17 00:00:00 2001 From: Maria Tavlaki Date: Sun, 1 Oct 2017 23:26:36 +0300 Subject: Autocomplete: handle arguments --- toolsrc/src/commands_autocomplete.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp index 71154445d..a67113aff 100644 --- a/toolsrc/src/commands_autocomplete.cpp +++ b/toolsrc/src/commands_autocomplete.cpp @@ -14,9 +14,34 @@ namespace vcpkg::Commands::Autocomplete Strings::format("The argument should be a command line to autocomplete.\n%s", Commands::Help::create_example_string("autocomplete install z")); - args.check_max_arg_count(1, EXAMPLE); + args.check_min_arg_count(1, EXAMPLE); + args.check_max_arg_count(2, EXAMPLE); args.check_and_get_optional_command_arguments({}); + const std::string requested_command = args.command_arguments.at(0); + const std::string start_with = + args.command_arguments.size() > 1 ? args.command_arguments.at(1) : Strings::EMPTY; + + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + auto& source_paragraphs = sources_and_errors.paragraphs; + + const auto& istartswith = Strings::case_insensitive_ascii_starts_with; + + std::vector results; + for (const auto& source_control_file : source_paragraphs) + { + auto&& sp = *source_control_file->core_paragraph; + + if (istartswith(sp.name, start_with)) + { + results.push_back(sp.name); + } + } + + System::println(Strings::join(" ", results)); + + auto all_ports = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + Checks::exit_success(VCPKG_LINE_INFO); } } -- cgit v1.2.3 From 7c2239f980197096a13e8fb4b96297b08616eb78 Mon Sep 17 00:00:00 2001 From: Maria Tavlaki Date: Mon, 2 Oct 2017 00:03:39 +0300 Subject: Autocomplete: check if first argument is "install" --- toolsrc/src/commands_autocomplete.cpp | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp index a67113aff..013cea320 100644 --- a/toolsrc/src/commands_autocomplete.cpp +++ b/toolsrc/src/commands_autocomplete.cpp @@ -8,6 +8,23 @@ namespace vcpkg::Commands::Autocomplete { + std::vector autocomplete_install( + const std::vector>& source_paragraphs, const std::string& start_with) + { + std::vector results; + const auto& istartswith = Strings::case_insensitive_ascii_starts_with; + + for (const auto& source_control_file : source_paragraphs) + { + auto&& sp = *source_control_file->core_paragraph; + + if (istartswith(sp.name, start_with)) + { + results.push_back(sp.name); + } + } + return results; + } void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) { static const std::string EXAMPLE = @@ -21,27 +38,15 @@ namespace vcpkg::Commands::Autocomplete const std::string requested_command = args.command_arguments.at(0); const std::string start_with = args.command_arguments.size() > 1 ? args.command_arguments.at(1) : Strings::EMPTY; - - auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); - auto& source_paragraphs = sources_and_errors.paragraphs; - - const auto& istartswith = Strings::case_insensitive_ascii_starts_with; - std::vector results; - for (const auto& source_control_file : source_paragraphs) + if (requested_command == "install") { - auto&& sp = *source_control_file->core_paragraph; + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + auto& source_paragraphs = sources_and_errors.paragraphs; - if (istartswith(sp.name, start_with)) - { - results.push_back(sp.name); - } + results = autocomplete_install(source_paragraphs, start_with); } - System::println(Strings::join(" ", results)); - - auto all_ports = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); - Checks::exit_success(VCPKG_LINE_INFO); } } -- cgit v1.2.3 From 016c53e231f353bdfa35f8e265b281e8d56031ed Mon Sep 17 00:00:00 2001 From: Maria Tavlaki Date: Mon, 2 Oct 2017 00:38:53 +0300 Subject: Autocomplete: remove --- toolsrc/src/commands_autocomplete.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp index 013cea320..3963f904b 100644 --- a/toolsrc/src/commands_autocomplete.cpp +++ b/toolsrc/src/commands_autocomplete.cpp @@ -5,6 +5,7 @@ #include "vcpkg_Commands.h" #include "vcpkg_Maps.h" #include "vcpkg_System.h" +#include "vcpkglib.h" namespace vcpkg::Commands::Autocomplete { @@ -25,6 +26,25 @@ namespace vcpkg::Commands::Autocomplete } return results; } + + std::vector autocomplete_remove(std::vector installed_packages, + const std::string& start_with) + { + std::vector results; + const auto& istartswith = Strings::case_insensitive_ascii_starts_with; + + for (const auto& installed_package : installed_packages) + { + auto sp = installed_package->package.displayname(); + + if (istartswith(sp, start_with)) + { + results.push_back(sp); + } + } + return results; + } + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) { static const std::string EXAMPLE = @@ -46,6 +66,13 @@ namespace vcpkg::Commands::Autocomplete results = autocomplete_install(source_paragraphs, start_with); } + else if (requested_command == "remove") + { + const StatusParagraphs status_db = database_load_check(paths); + std::vector installed_packages = get_installed_ports(status_db); + results = autocomplete_remove(installed_packages, start_with); + } + System::println(Strings::join(" ", results)); Checks::exit_success(VCPKG_LINE_INFO); } -- cgit v1.2.3 From 1b71053ad9954ffb0cb3c2955f42a86f62f66372 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Wed, 4 Oct 2017 16:01:29 -0700 Subject: [vcpkg] Reformat and refactor to reduce function size --- toolsrc/src/VcpkgPaths.cpp | 24 ++- toolsrc/src/commands_export.cpp | 324 ++++++++++++++++++++---------------- toolsrc/src/commands_export_ifw.cpp | 167 ++++++++++--------- 3 files changed, 278 insertions(+), 237 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index 19210facf..9f85992be 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -175,18 +175,21 @@ namespace vcpkg static fs::path get_ifw_installerbase_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { - static constexpr std::array EXPECTED_VERSION = { 3, 1, 81 }; + static constexpr std::array EXPECTED_VERSION = {3, 1, 81}; static const std::wstring VERSION_CHECK_ARGUMENTS = L"--framework-version"; - const fs::path downloaded_copy = downloads_folder / "QtInstallerFramework-win-x86" / "bin" / "installerbase.exe"; + const fs::path downloaded_copy = + downloads_folder / "QtInstallerFramework-win-x86" / "bin" / "installerbase.exe"; std::vector candidate_paths; candidate_paths.push_back(downloaded_copy); // TODO: Uncomment later - //const std::vector from_path = Files::find_from_PATH(L"installerbase"); - //candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); - //candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe"); - //candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / "QtIFW-3.1.0" / "bin" / "installerbase.exe"); + // const std::vector from_path = Files::find_from_PATH(L"installerbase"); + // candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + // candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / + // "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe"); + // candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / + // "QtIFW-3.1.0" / "bin" / "installerbase.exe"); const Optional path = find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); @@ -283,17 +286,20 @@ namespace vcpkg const fs::path& VcpkgPaths::get_ifw_installerbase_exe() const { - return this->ifw_installerbase_exe.get_lazy([this]() { return get_ifw_installerbase_path(this->downloads, this->scripts); }); + return this->ifw_installerbase_exe.get_lazy( + [this]() { return get_ifw_installerbase_path(this->downloads, this->scripts); }); } const fs::path& VcpkgPaths::get_ifw_binarycreator_exe() const { - return this->ifw_binarycreator_exe.get_lazy([this]() { return get_ifw_installerbase_exe().parent_path() / "binarycreator.exe"; }); + return this->ifw_binarycreator_exe.get_lazy( + [this]() { return get_ifw_installerbase_exe().parent_path() / "binarycreator.exe"; }); } const fs::path& VcpkgPaths::get_ifw_repogen_exe() const { - return this->ifw_repogen_exe.get_lazy([this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; }); + return this->ifw_repogen_exe.get_lazy( + [this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; }); } static std::vector get_vs2017_installation_instances(const VcpkgPaths& paths) diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp index 3662a46d8..a24f2eac7 100644 --- a/toolsrc/src/commands_export.cpp +++ b/toolsrc/src/commands_export.cpp @@ -211,17 +211,17 @@ namespace vcpkg::Commands::Export return nullopt; } - void export_integration_files(const fs::path &raw_exported_dir_path, const VcpkgPaths& paths) + void export_integration_files(const fs::path& raw_exported_dir_path, const VcpkgPaths& paths) { const std::vector integration_files_relative_to_root = { - { ".vcpkg-root" }, - { fs::path{ "scripts" } / "buildsystems" / "msbuild" / "applocal.ps1" }, - { fs::path{ "scripts" } / "buildsystems" / "msbuild" / "vcpkg.targets" }, - { fs::path{ "scripts" } / "buildsystems" / "vcpkg.cmake" }, - { fs::path{ "scripts" } / "cmake" / "vcpkg_get_windows_sdk.cmake" }, - { fs::path{ "scripts" } / "getWindowsSDK.ps1" }, - { fs::path{ "scripts" } / "getProgramFilesPlatformBitness.ps1" }, - { fs::path{ "scripts" } / "getProgramFiles32bit.ps1" }, + {".vcpkg-root"}, + {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"}, + {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, + {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"}, + {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, + {fs::path{"scripts"} / "getWindowsSDK.ps1"}, + {fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"}, + {fs::path{"scripts"} / "getProgramFiles32bit.ps1"}, }; for (const fs::path& file : integration_files_relative_to_root) @@ -237,8 +237,27 @@ namespace vcpkg::Commands::Export } } - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + struct ExportArguments + { + bool dry_run; + bool raw; + bool nuget; + bool ifw; + bool zip; + bool seven_zip; + + Optional maybe_nuget_id; + Optional maybe_nuget_version; + + IFW::Options ifw_options; + std::vector specs; + }; + + static ExportArguments handle_export_command_arguments(const VcpkgCmdArguments& args, + const Triplet& default_triplet) { + ExportArguments ret; + static const std::string OPTION_DRY_RUN = "--dry-run"; static const std::string OPTION_RAW = "--raw"; static const std::string OPTION_NUGET = "--nuget"; @@ -258,11 +277,9 @@ namespace vcpkg::Commands::Export Commands::Help::create_example_string("export zlib zlib:x64-windows boost --nuget"); args.check_min_arg_count(1, EXAMPLE); - const std::vector specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + ret.specs = Util::fmap(args.command_arguments, [&](auto&& arg) { return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE); }); - for (auto&& spec : specs) - Input::check_triplet(spec.triplet(), paths); const auto options = args.check_and_get_optional_command_arguments( { @@ -280,16 +297,16 @@ namespace vcpkg::Commands::Export OPTION_IFW_PACKAGES_DIR_PATH, OPTION_IFW_REPOSITORY_DIR_PATH, OPTION_IFW_CONFIG_FILE_PATH, - OPTION_IFW_INSTALLER_FILE_PATH + OPTION_IFW_INSTALLER_FILE_PATH, }); - const bool dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); - const bool raw = options.switches.find(OPTION_RAW) != options.switches.cend(); - const bool nuget = options.switches.find(OPTION_NUGET) != options.switches.cend(); - const bool ifw = options.switches.find(OPTION_IFW) != options.switches.cend(); - const bool zip = options.switches.find(OPTION_ZIP) != options.switches.cend(); - const bool seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend(); - - if (!raw && !nuget && !ifw && !zip && !seven_zip && !dry_run) + ret.dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); + ret.raw = options.switches.find(OPTION_RAW) != options.switches.cend(); + ret.nuget = options.switches.find(OPTION_NUGET) != options.switches.cend(); + ret.ifw = options.switches.find(OPTION_IFW) != options.switches.cend(); + ret.zip = options.switches.find(OPTION_ZIP) != options.switches.cend(); + ret.seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend(); + + if (!ret.raw && !ret.nuget && !ret.ifw && !ret.zip && !ret.seven_zip && !ret.dry_run) { System::println(System::Color::error, "Must provide at least one export type: --raw --nuget --ifw --zip --7zip"); @@ -297,38 +314,152 @@ namespace vcpkg::Commands::Export Checks::exit_fail(VCPKG_LINE_INFO); } - auto maybe_nuget_id = maybe_lookup(options.settings, OPTION_NUGET_ID); - auto maybe_nuget_version = maybe_lookup(options.settings, OPTION_NUGET_VERSION); + ret.maybe_nuget_id = maybe_lookup(options.settings, OPTION_NUGET_ID); + ret.maybe_nuget_version = maybe_lookup(options.settings, OPTION_NUGET_VERSION); - Checks::check_exit(VCPKG_LINE_INFO, !maybe_nuget_id || nuget, "--nuget-id is only valid with --nuget"); + Checks::check_exit(VCPKG_LINE_INFO, !ret.maybe_nuget_id || ret.nuget, "--nuget-id is only valid with --nuget"); Checks::check_exit( - VCPKG_LINE_INFO, !maybe_nuget_version || nuget, "--nuget-version is only valid with --nuget"); + VCPKG_LINE_INFO, !ret.maybe_nuget_version || ret.nuget, "--nuget-version is only valid with --nuget"); - IFW::Options ifw_options; + ret.ifw_options.maybe_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); + Checks::check_exit(VCPKG_LINE_INFO, + !ret.ifw_options.maybe_repository_url || ret.ifw, + "--ifw-repository-url is only valid with --ifw"); - ifw_options.maybe_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); - Checks::check_exit( - VCPKG_LINE_INFO, !ifw_options.maybe_repository_url || ifw, "--ifw-repository-url is only valid with --ifw"); + ret.ifw_options.maybe_packages_dir_path = maybe_lookup(options.settings, OPTION_IFW_PACKAGES_DIR_PATH); + Checks::check_exit(VCPKG_LINE_INFO, + !ret.ifw_options.maybe_packages_dir_path || ret.ifw, + "--ifw-packages-directory-path is only valid with --ifw"); - ifw_options.maybe_packages_dir_path = maybe_lookup(options.settings, OPTION_IFW_PACKAGES_DIR_PATH); - Checks::check_exit( - VCPKG_LINE_INFO, !ifw_options.maybe_packages_dir_path || ifw, "--ifw-packages-directory-path is only valid with --ifw"); + ret.ifw_options.maybe_repository_dir_path = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_DIR_PATH); + Checks::check_exit(VCPKG_LINE_INFO, + !ret.ifw_options.maybe_repository_dir_path || ret.ifw, + "--ifw-repository-directory-path is only valid with --ifw"); - ifw_options.maybe_repository_dir_path = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_DIR_PATH); - Checks::check_exit( - VCPKG_LINE_INFO, !ifw_options.maybe_repository_dir_path || ifw, "--ifw-repository-directory-path is only valid with --ifw"); + ret.ifw_options.maybe_config_file_path = maybe_lookup(options.settings, OPTION_IFW_CONFIG_FILE_PATH); + Checks::check_exit(VCPKG_LINE_INFO, + !ret.ifw_options.maybe_config_file_path || ret.ifw, + "--ifw-configuration-file-path is only valid with --ifw"); - ifw_options.maybe_config_file_path = maybe_lookup(options.settings, OPTION_IFW_CONFIG_FILE_PATH); - Checks::check_exit( - VCPKG_LINE_INFO, !ifw_options.maybe_config_file_path || ifw, "--ifw-configuration-file-path is only valid with --ifw"); + ret.ifw_options.maybe_installer_file_path = maybe_lookup(options.settings, OPTION_IFW_INSTALLER_FILE_PATH); + Checks::check_exit(VCPKG_LINE_INFO, + !ret.ifw_options.maybe_installer_file_path || ret.ifw, + "--ifw-installer-file-path is only valid with --ifw"); + return ret; + } - ifw_options.maybe_installer_file_path = maybe_lookup(options.settings, OPTION_IFW_INSTALLER_FILE_PATH); - Checks::check_exit( - VCPKG_LINE_INFO, !ifw_options.maybe_installer_file_path || ifw, "--ifw-installer-file-path is only valid with --ifw"); + static void print_next_step_info(const fs::path& prefix) + { + const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; + const CMakeVariable cmake_variable = CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); + System::println("\n" + "To use the exported libraries in CMake projects use:" + "\n" + " %s" + "\n", + Strings::to_utf8(cmake_variable.s)); + }; + + static void handle_raw_based_export(Span export_plan, + const ExportArguments& opts, + const std::string& export_id, + const VcpkgPaths& paths) + { + Files::Filesystem& fs = paths.get_filesystem(); + const fs::path export_to_path = paths.root; + const fs::path raw_exported_dir_path = export_to_path / export_id; + std::error_code ec; + fs.remove_all(raw_exported_dir_path, ec); + fs.create_directory(raw_exported_dir_path, ec); + + // execute the plan + for (const ExportPlanAction& action : export_plan) + { + if (action.plan_type != ExportPlanType::ALREADY_BUILT) + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + const std::string display_name = action.spec.to_string(); + System::println("Exporting package %s... ", display_name); + + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + const InstallDir dirs = InstallDir::from_destination_root( + raw_exported_dir_path / "installed", + action.spec.triplet().to_string(), + raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); + + Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); + System::println(System::Color::success, "Exporting package %s... done", display_name); + } + + // Copy files needed for integration + export_integration_files(raw_exported_dir_path, paths); + + if (opts.raw) + { + System::println( + System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string()); + print_next_step_info(export_to_path); + } + + if (opts.nuget) + { + System::println("Creating nuget package... "); + + const std::string nuget_id = opts.maybe_nuget_id.value_or(raw_exported_dir_path.filename().string()); + const std::string nuget_version = opts.maybe_nuget_version.value_or("1.0.0"); + const fs::path output_path = + do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path); + System::println(System::Color::success, "Creating nuget package... done"); + System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string()); + + System::println(R"( +With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: + Install-Package %s -Source "%s" +)" + "\n", + nuget_id, + output_path.parent_path().u8string()); + } + + if (opts.zip) + { + System::println("Creating zip archive... "); + const fs::path output_path = + do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP); + System::println(System::Color::success, "Creating zip archive... done"); + System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string()); + print_next_step_info("[...]"); + } + + if (opts.seven_zip) + { + System::println("Creating 7zip archive... "); + const fs::path output_path = + do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP); + System::println(System::Color::success, "Creating 7zip archive... done"); + System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string()); + print_next_step_info("[...]"); + } + + if (!opts.raw) + { + fs.remove_all(raw_exported_dir_path, ec); + } + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + const auto opts = handle_export_command_arguments(args, default_triplet); + for (auto&& spec : opts.specs) + Input::check_triplet(spec.triplet(), paths); // create the plan const StatusParagraphs status_db = database_load_check(paths); - std::vector export_plan = Dependencies::create_export_plan(paths, specs, status_db); + std::vector export_plan = Dependencies::create_export_plan(paths, opts.specs, status_db); Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty"); std::map> group_by_plan_type; @@ -363,118 +494,21 @@ namespace vcpkg::Commands::Export Checks::exit_fail(VCPKG_LINE_INFO); } - if (dry_run) + if (opts.dry_run) { Checks::exit_success(VCPKG_LINE_INFO); } std::string export_id = create_export_id(); - const auto print_next_step_info = [](const fs::path& prefix) { - const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; - const CMakeVariable cmake_variable = - CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); - System::println("\n" - "To use the exported libraries in CMake projects use:" - "\n" - " %s" - "\n", - Strings::to_utf8(cmake_variable.s)); - }; - - // Main loop - if (raw || nuget || zip || seven_zip) + if (opts.raw || opts.nuget || opts.zip || opts.seven_zip) { - Files::Filesystem& fs = paths.get_filesystem(); - const fs::path export_to_path = paths.root; - const fs::path raw_exported_dir_path = export_to_path / export_id; - std::error_code ec; - fs.remove_all(raw_exported_dir_path, ec); - fs.create_directory(raw_exported_dir_path, ec); - - // execute the plan - for (const ExportPlanAction& action : export_plan) - { - if (action.plan_type != ExportPlanType::ALREADY_BUILT) - { - Checks::unreachable(VCPKG_LINE_INFO); - } - - const std::string display_name = action.spec.to_string(); - System::println("Exporting package %s... ", display_name); - - const BinaryParagraph& binary_paragraph = - action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - - const InstallDir dirs = InstallDir::from_destination_root( - raw_exported_dir_path / "installed", - action.spec.triplet().to_string(), - raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); - - Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); - System::println(System::Color::success, "Exporting package %s... done", display_name); - } - - // Copy files needed for integration - export_integration_files(raw_exported_dir_path, paths); - - if (raw) - { - System::println( - System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string()); - print_next_step_info(export_to_path); - } - - if (nuget) - { - System::println("Creating nuget package... "); - - const std::string nuget_id = maybe_nuget_id.value_or(raw_exported_dir_path.filename().string()); - const std::string nuget_version = maybe_nuget_version.value_or("1.0.0"); - const fs::path output_path = - do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path); - System::println(System::Color::success, "Creating nuget package... done"); - System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string()); - - System::println(R"( -With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: - Install-Package %s -Source "%s" -)" - "\n", - nuget_id, - output_path.parent_path().u8string()); - } - - if (zip) - { - System::println("Creating zip archive... "); - const fs::path output_path = - do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP); - System::println(System::Color::success, "Creating zip archive... done"); - System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string()); - print_next_step_info("[...]"); - } - - if (seven_zip) - { - System::println("Creating 7zip archive... "); - const fs::path output_path = - do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP); - System::println(System::Color::success, "Creating 7zip archive... done"); - System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string()); - print_next_step_info("[...]"); - } - - if (!raw) - { - fs.remove_all(raw_exported_dir_path, ec); - } + handle_raw_based_export(export_plan, opts, export_id, paths); } - // IFW loop - if (ifw) + if (opts.ifw) { - IFW::do_export(export_plan, export_id, ifw_options, paths); + IFW::do_export(export_plan, export_id, opts.ifw_options, paths); print_next_step_info("@RootDir@/src/vcpkg"); } diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index a0692c11e..0dc8a7d31 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -19,39 +19,39 @@ namespace vcpkg::Commands::Export::IFW char mbstr[11]; const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d", &date_time); Checks::check_exit(VCPKG_LINE_INFO, - bytes_written == 10, - "Expected 10 bytes to be written, but %u were written", - bytes_written); + bytes_written == 10, + "Expected 10 bytes to be written, but %u were written", + bytes_written); const std::string date_time_as_string(mbstr); return date_time_as_string; } - fs::path get_packages_dir_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + fs::path get_packages_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { - return ifw_options.maybe_packages_dir_path.has_value() ? - fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-packages"); + return ifw_options.maybe_packages_dir_path.has_value() + ? fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-packages"); } - fs::path get_repository_dir_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + fs::path get_repository_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { - return ifw_options.maybe_repository_dir_path.has_value() ? - fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-repository"); + return ifw_options.maybe_repository_dir_path.has_value() + ? fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-repository"); } - fs::path get_config_file_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + fs::path get_config_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { - return ifw_options.maybe_config_file_path.has_value() ? - fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-configuration.xml"); + return ifw_options.maybe_config_file_path.has_value() + ? fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-configuration.xml"); } - fs::path get_installer_file_path(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + fs::path get_installer_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { - return ifw_options.maybe_installer_file_path.has_value() ? - fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-installer.exe"); + return ifw_options.maybe_installer_file_path.has_value() + ? fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-installer.exe"); } fs::path export_real_package(const fs::path& ifw_packages_dir_path, @@ -82,7 +82,7 @@ namespace vcpkg::Commands::Export::IFW fs.write_contents(package_xml_file_path, Strings::format( -R"###( + R"###( %s %s @@ -91,12 +91,12 @@ R"###( true )###", - action.spec.to_string(), - binary_paragraph.version, - create_release_date(), - action.spec.name(), - action.spec.triplet().canonical_name(), - deps)); + action.spec.to_string(), + binary_paragraph.version, + create_release_date(), + action.spec.name(), + action.spec.triplet().canonical_name(), + deps)); // Return dir path for export package data return ifw_packages_dir_path / @@ -119,15 +119,16 @@ R"###( !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, Strings::format( -R"###( + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( Packages 1.0.0 %s )###", - create_release_date())); + create_release_date())); for (auto package = unique_packages.begin(); package != unique_packages.end(); ++package) { @@ -146,7 +147,7 @@ R"###( fs.write_contents(package_xml_file_path, Strings::format( -R"###( + R"###( %s %s @@ -154,10 +155,10 @@ R"###( %s )###", - action.spec.name(), - binary_paragraph.description, - binary_paragraph.version, - create_release_date())); + action.spec.name(), + binary_paragraph.description, + binary_paragraph.version, + create_release_date())); } } @@ -176,15 +177,16 @@ R"###( !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, Strings::format( -R"###( + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( Triplets 1.0.0 %s )###", - create_release_date())); + create_release_date())); for (const std::string& triplet : unique_triplets) { @@ -196,16 +198,17 @@ R"###( !ec, "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, Strings::format( -R"###( + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( %s 1.0.0 %s )###", - triplet, - create_release_date())); + triplet, + create_release_date())); } } @@ -222,18 +225,19 @@ R"###( "Could not create directory for package file %s", package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, Strings::format( -R"###( + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( Integration 1.0.0 %s )###", - create_release_date())); + create_release_date())); } - void export_config(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + void export_config(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { std::error_code ec; Files::Filesystem& fs = paths.get_filesystem(); @@ -257,21 +261,22 @@ R"###( %s )###", - ifw_repo_url); + ifw_repo_url); } - fs.write_contents(config_xml_file_path, Strings::format( -R"###( + fs.write_contents(config_xml_file_path, + Strings::format( + R"###( vcpkg 1.0.0 @RootDir@/src/vcpkg%s )###", - formatted_repo_url)); + formatted_repo_url)); } - void do_repository(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + void do_repository(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { const fs::path& repogen_exe = paths.get_ifw_repogen_exe(); const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); @@ -283,16 +288,13 @@ R"###( Files::Filesystem& fs = paths.get_filesystem(); fs.remove_all(repository_dir, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not remove outdated repository directory %s", - repository_dir.generic_string()); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not remove outdated repository directory %s", repository_dir.generic_string()); - const std::wstring cmd_line = - Strings::wformat(LR"("%s" --packages "%s" "%s" > nul)", - repogen_exe.native(), - packages_dir.native(), - repository_dir.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" --packages "%s" "%s" > nul)", + repogen_exe.native(), + packages_dir.native(), + repository_dir.native()); const int exit_code = System::cmd_execute_clean(cmd_line); Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW repository generating failed"); @@ -300,7 +302,7 @@ R"###( System::println(System::Color::success, "Generating repository %s... done.", repository_dir.generic_string()); } - void do_installer(const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + void do_installer(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { const fs::path& binarycreator_exe = paths.get_ifw_binarycreator_exe(); const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); @@ -315,21 +317,19 @@ R"###( std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); if (!ifw_repo_url.empty()) { - cmd_line = - Strings::wformat(LR"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)", - binarycreator_exe.native(), - config_file.native(), - repository_dir.native(), - installer_file.native()); + cmd_line = Strings::wformat(LR"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)", + binarycreator_exe.native(), + config_file.native(), + repository_dir.native(), + installer_file.native()); } else { - cmd_line = - Strings::wformat(LR"("%s" --config "%s" --packages "%s" "%s" > nul)", - binarycreator_exe.native(), - config_file.native(), - packages_dir.native(), - installer_file.native()); + cmd_line = Strings::wformat(LR"("%s" --config "%s" --packages "%s" "%s" > nul)", + binarycreator_exe.native(), + config_file.native(), + packages_dir.native(), + installer_file.native()); } const int exit_code = System::cmd_execute_clean(cmd_line); @@ -338,7 +338,10 @@ R"###( System::println(System::Color::success, "Generating installer %s... done.", installer_file.generic_string()); } - void do_export(const std::vector &export_plan, const std::string &export_id, const Options &ifw_options, const VcpkgPaths& paths) + void do_export(const std::vector& export_plan, + const std::string& export_id, + const Options& ifw_options, + const VcpkgPaths& paths) { const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); @@ -350,15 +353,13 @@ R"###( fs.remove_all(ifw_packages_dir_path, ec); Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not remove outdated packages directory %s", - ifw_packages_dir_path.generic_string()); + !ec, + "Could not remove outdated packages directory %s", + ifw_packages_dir_path.generic_string()); fs.create_directory(ifw_packages_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create packages directory %s", - ifw_packages_dir_path.generic_string()); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create packages directory %s", ifw_packages_dir_path.generic_string()); // execute the plan std::map unique_packages; @@ -384,9 +385,9 @@ R"###( // Copy package data const InstallDir dirs = InstallDir::from_destination_root(ifw_package_dir_path, - action.spec.triplet().to_string(), - ifw_package_dir_path / "vcpkg" / "info" / - (binary_paragraph.fullstem() + ".list")); + action.spec.triplet().to_string(), + ifw_package_dir_path / "vcpkg" / "info" / + (binary_paragraph.fullstem() + ".list")); Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); System::println("Exporting package %s... done", display_name); -- cgit v1.2.3 From c98db7541594eadccf6823d4fdde2ff8c53c5fe9 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Wed, 4 Oct 2017 16:24:03 -0700 Subject: [vcpkg] Refactor out implication in option parsing for export --- toolsrc/src/commands_export.cpp | 70 +++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp index a24f2eac7..20838f5a5 100644 --- a/toolsrc/src/commands_export.cpp +++ b/toolsrc/src/commands_export.cpp @@ -314,37 +314,45 @@ namespace vcpkg::Commands::Export Checks::exit_fail(VCPKG_LINE_INFO); } - ret.maybe_nuget_id = maybe_lookup(options.settings, OPTION_NUGET_ID); - ret.maybe_nuget_version = maybe_lookup(options.settings, OPTION_NUGET_VERSION); - - Checks::check_exit(VCPKG_LINE_INFO, !ret.maybe_nuget_id || ret.nuget, "--nuget-id is only valid with --nuget"); - Checks::check_exit( - VCPKG_LINE_INFO, !ret.maybe_nuget_version || ret.nuget, "--nuget-version is only valid with --nuget"); - - ret.ifw_options.maybe_repository_url = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_URL); - Checks::check_exit(VCPKG_LINE_INFO, - !ret.ifw_options.maybe_repository_url || ret.ifw, - "--ifw-repository-url is only valid with --ifw"); - - ret.ifw_options.maybe_packages_dir_path = maybe_lookup(options.settings, OPTION_IFW_PACKAGES_DIR_PATH); - Checks::check_exit(VCPKG_LINE_INFO, - !ret.ifw_options.maybe_packages_dir_path || ret.ifw, - "--ifw-packages-directory-path is only valid with --ifw"); - - ret.ifw_options.maybe_repository_dir_path = maybe_lookup(options.settings, OPTION_IFW_REPOSITORY_DIR_PATH); - Checks::check_exit(VCPKG_LINE_INFO, - !ret.ifw_options.maybe_repository_dir_path || ret.ifw, - "--ifw-repository-directory-path is only valid with --ifw"); - - ret.ifw_options.maybe_config_file_path = maybe_lookup(options.settings, OPTION_IFW_CONFIG_FILE_PATH); - Checks::check_exit(VCPKG_LINE_INFO, - !ret.ifw_options.maybe_config_file_path || ret.ifw, - "--ifw-configuration-file-path is only valid with --ifw"); - - ret.ifw_options.maybe_installer_file_path = maybe_lookup(options.settings, OPTION_IFW_INSTALLER_FILE_PATH); - Checks::check_exit(VCPKG_LINE_INFO, - !ret.ifw_options.maybe_installer_file_path || ret.ifw, - "--ifw-installer-file-path is only valid with --ifw"); + struct OptionPair + { + const std::string& name; + Optional& out_opt; + }; + const auto options_implies = + [&](const std::string& main_opt_name, bool main_opt, Span implying_opts) { + if (main_opt) + { + for (auto&& opt : implying_opts) + opt.out_opt = maybe_lookup(options.settings, opt.name); + } + else + { + for (auto&& opt : implying_opts) + Checks::check_exit(VCPKG_LINE_INFO, + !maybe_lookup(options.settings, opt.name), + "%s is only valid with %s", + opt.name, + main_opt_name); + } + }; + + options_implies(OPTION_NUGET, + ret.nuget, + { + {OPTION_NUGET_ID, ret.maybe_nuget_id}, + {OPTION_NUGET_VERSION, ret.maybe_nuget_version}, + }); + + options_implies(OPTION_IFW, + ret.ifw, + { + {OPTION_IFW_REPOSITORY_URL, ret.ifw_options.maybe_repository_url}, + {OPTION_IFW_PACKAGES_DIR_PATH, ret.ifw_options.maybe_packages_dir_path}, + {OPTION_IFW_REPOSITORY_DIR_PATH, ret.ifw_options.maybe_repository_dir_path}, + {OPTION_IFW_CONFIG_FILE_PATH, ret.ifw_options.maybe_config_file_path}, + {OPTION_IFW_INSTALLER_FILE_PATH, ret.ifw_options.maybe_installer_file_path}, + }); return ret; } -- cgit v1.2.3 From 6f763c5ca830c9e461e106c63959a60d33eb5e5b Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Thu, 5 Oct 2017 21:24:32 +0300 Subject: [vcpkg-export-ifw] Maintenance Tool Install maintenance tool and create Start menu shortcuts to manage, update and uninstall packages. --- toolsrc/src/commands_export_ifw.cpp | 69 ++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index 0dc8a7d31..4ab0ae537 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -270,12 +270,66 @@ namespace vcpkg::Commands::Export::IFW vcpkg 1.0.0 + vcpkg @RootDir@/src/vcpkg%s )###", formatted_repo_url)); } + void export_maintenance_tool(const fs::path& ifw_packages_dir_path, const VcpkgPaths& paths) + { + System::println("Exporting maintenance tool... "); + + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + const fs::path& installerbase_exe = paths.get_ifw_installerbase_exe(); + fs::path tempmaintenancetool = ifw_packages_dir_path / "maintenance" / "data" / "tempmaintenancetool.exe"; + fs.create_directories(tempmaintenancetool.parent_path(), ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + tempmaintenancetool.generic_string()); + fs.copy_file(installerbase_exe, tempmaintenancetool, fs::copy_options::overwrite_existing, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not write package file %s", + tempmaintenancetool.generic_string()); + + fs::path package_xml_file_path = ifw_packages_dir_path / "maintenance" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + Maintenance Tool + Maintenance Tool + 1.0.0 + %s + + true + true + true + +)###", +create_release_date())); + const fs::path script_source = paths.root / "scripts" / "ifw" / "maintenance.qs"; + const fs::path script_destination = ifw_packages_dir_path / "maintenance" / "meta" / "maintenance.qs"; + fs.copy_file(script_source, script_destination, fs::copy_options::overwrite_existing, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not write package file %s", + script_destination.generic_string()); + + System::println("Exporting maintenance tool... done"); + } + void do_repository(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { const fs::path& repogen_exe = paths.get_ifw_repogen_exe(); @@ -343,14 +397,12 @@ namespace vcpkg::Commands::Export::IFW const Options& ifw_options, const VcpkgPaths& paths) { - const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); - const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); - - System::println("Exporting packages %s... ", ifw_packages_dir_path.generic_string()); - std::error_code ec; Files::Filesystem& fs = paths.get_filesystem(); + // Prepare packages directory + const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); + fs.remove_all(ifw_packages_dir_path, ec); Checks::check_exit(VCPKG_LINE_INFO, !ec, @@ -361,6 +413,11 @@ namespace vcpkg::Commands::Export::IFW Checks::check_exit( VCPKG_LINE_INFO, !ec, "Could not create packages directory %s", ifw_packages_dir_path.generic_string()); + // Export maintenance tool + export_maintenance_tool(ifw_packages_dir_path, paths); + + System::println("Exporting packages %s... ", ifw_packages_dir_path.generic_string()); + // execute the plan std::map unique_packages; std::set unique_triplets; @@ -395,6 +452,8 @@ namespace vcpkg::Commands::Export::IFW System::println("Exporting packages %s... done", ifw_packages_dir_path.generic_string()); + const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); + System::println("Generating configuration %s...", config_file.generic_string()); // Unique packages -- cgit v1.2.3 From a518ded266259141edd278e3f4025a3ab3211215 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Thu, 5 Oct 2017 14:36:29 -0700 Subject: [vcpkg] Check size and don't perform stripping if string is too small. --- toolsrc/src/vcpkg_System.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp index 4d2e88b73..7a6e7f028 100644 --- a/toolsrc/src/vcpkg_System.cpp +++ b/toolsrc/src/vcpkg_System.cpp @@ -179,6 +179,7 @@ namespace vcpkg::System // On Win7, output from powershell calls contain a byte order mark, so we strip it out if it is present static void remove_byte_order_mark(std::wstring* s) { + if (s->size() < 3) return; const wchar_t* a = s->c_str(); // This is the UTF-8 byte-order mark if (a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF) -- cgit v1.2.3 From 9b0c2cb2524ef3fa1e8e7e8f2512c32e40eb5815 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Thu, 5 Oct 2017 18:07:29 -0700 Subject: [vcpkg] Remove multiple byte order marks --- toolsrc/src/vcpkg_System.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp index 7a6e7f028..3b4c440d8 100644 --- a/toolsrc/src/vcpkg_System.cpp +++ b/toolsrc/src/vcpkg_System.cpp @@ -177,12 +177,11 @@ namespace vcpkg::System } // On Win7, output from powershell calls contain a byte order mark, so we strip it out if it is present - static void remove_byte_order_mark(std::wstring* s) + static void remove_byte_order_marks(std::wstring* s) { - if (s->size() < 3) return; const wchar_t* a = s->c_str(); // This is the UTF-8 byte-order mark - if (a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF) + while (s->size() >= 3 && a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF) { s->erase(0, 3); } @@ -214,7 +213,7 @@ namespace vcpkg::System const auto ec = _pclose(pipe); Debug::println("_pclose() returned %d", ec); - remove_byte_order_mark(&output); + remove_byte_order_marks(&output); return {ec, Strings::to_utf8(output)}; } -- cgit v1.2.3 From 13385293e62561166c490671630e92458e630b1b Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Thu, 5 Oct 2017 18:14:08 -0700 Subject: [vcpkg] Clang-format on commands_export_ifw --- toolsrc/src/commands_export_ifw.cpp | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index 4ab0ae537..191dbb763 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -288,25 +288,23 @@ namespace vcpkg::Commands::Export::IFW fs::path tempmaintenancetool = ifw_packages_dir_path / "maintenance" / "data" / "tempmaintenancetool.exe"; fs.create_directories(tempmaintenancetool.parent_path(), ec); Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - tempmaintenancetool.generic_string()); + !ec, + "Could not create directory for package file %s", + tempmaintenancetool.generic_string()); fs.copy_file(installerbase_exe, tempmaintenancetool, fs::copy_options::overwrite_existing, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not write package file %s", - tempmaintenancetool.generic_string()); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not write package file %s", tempmaintenancetool.generic_string()); fs::path package_xml_file_path = ifw_packages_dir_path / "maintenance" / "meta" / "package.xml"; fs::path package_xml_dir_path = package_xml_file_path.parent_path(); fs.create_directories(package_xml_dir_path, ec); Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); fs.write_contents(package_xml_file_path, - Strings::format( - R"###( + Strings::format( + R"###( Maintenance Tool Maintenance Tool @@ -318,14 +316,12 @@ namespace vcpkg::Commands::Export::IFW true )###", -create_release_date())); + create_release_date())); const fs::path script_source = paths.root / "scripts" / "ifw" / "maintenance.qs"; const fs::path script_destination = ifw_packages_dir_path / "maintenance" / "meta" / "maintenance.qs"; fs.copy_file(script_source, script_destination, fs::copy_options::overwrite_existing, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not write package file %s", - script_destination.generic_string()); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not write package file %s", script_destination.generic_string()); System::println("Exporting maintenance tool... done"); } -- cgit v1.2.3 From 9a963f7eff1981d4e894ea8b297d092cda60b764 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 5 Oct 2017 13:51:28 -0700 Subject: Overhaul VS selection. Add triplet option to specify VS instance --- toolsrc/src/PostBuildLint.cpp | 2 +- toolsrc/src/VcpkgPaths.cpp | 120 ++++++++++++++++++++++-------------------- toolsrc/src/commands_env.cpp | 4 +- toolsrc/src/vcpkg_Build.cpp | 12 ++++- 4 files changed, 75 insertions(+), 63 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/PostBuildLint.cpp b/toolsrc/src/PostBuildLint.cpp index 416d4a636..a0699e15c 100644 --- a/toolsrc/src/PostBuildLint.cpp +++ b/toolsrc/src/PostBuildLint.cpp @@ -722,7 +722,7 @@ namespace vcpkg::PostBuildLint const auto& fs = paths.get_filesystem(); // for dumpbin - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset); + const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); const fs::path package_dir = paths.package_dir(spec); size_t error_count = 0; diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index 9f85992be..f080783c4 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -337,14 +337,14 @@ namespace vcpkg // VS2015 const Optional vs_2015_installation_instance = get_vs2015_installation_instance(); - if (const auto v = vs_2015_installation_instance.get()) + if (const auto vs_2015_root_path = vs_2015_installation_instance.get()) { - const fs::path vs2015_vcvarsall_bat = *v / "VC" / "vcvarsall.bat"; + const fs::path vs2015_vcvarsall_bat = *vs_2015_root_path / "VC" / "vcvarsall.bat"; paths_examined.push_back(vs2015_vcvarsall_bat); if (fs.exists(vs2015_vcvarsall_bat)) { - const fs::path vs2015_dumpbin_exe = *v / "VC" / "bin" / "dumpbin.exe"; + const fs::path vs2015_dumpbin_exe = *vs_2015_root_path / "VC" / "bin" / "dumpbin.exe"; paths_examined.push_back(vs2015_dumpbin_exe); const fs::path vs2015_bin_dir = vs2015_vcvarsall_bat.parent_path() / "bin"; @@ -364,19 +364,25 @@ namespace vcpkg if (fs.exists(vs2015_dumpbin_exe)) { - found_toolsets.push_back( - {vs2015_dumpbin_exe, vs2015_vcvarsall_bat, {}, V_140, supported_architectures}); + found_toolsets.push_back({*vs_2015_root_path, + vs2015_dumpbin_exe, + vs2015_vcvarsall_bat, + {}, + V_140, + supported_architectures}); } } } + const bool v140_is_available = !found_toolsets.empty(); + const std::vector vs2017_installation_instances = get_vs2017_installation_instances(paths); // VS2017 - Optional vs2017_toolset; for (const std::string& instance : vs2017_installation_instances) { - const fs::path vc_dir = fs::path{instance} / "VC"; + const fs::path vs_root_path = fs::path{instance}; + const fs::path vc_dir = vs_root_path / "VC"; // Skip any instances that do not have vcvarsall. const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build"; @@ -415,14 +421,22 @@ namespace vcpkg paths_examined.push_back(dumpbin_path); if (fs.exists(dumpbin_path)) { - vs2017_toolset = Toolset{dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}; + found_toolsets.push_back( + Toolset{vs_root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}); + + if (v140_is_available) + { + found_toolsets.push_back(Toolset{vs_root_path, + dumpbin_path, + vcvarsall_bat, + {L"-vcvars_ver=14.0"}, + V_140, + supported_architectures}); + } + break; } } - if (const auto value = vs2017_toolset.get()) - { - found_toolsets.push_back(*value); - } } if (found_toolsets.empty()) @@ -439,64 +453,54 @@ namespace vcpkg return found_toolsets; } - static std::vector create_vs2017_v140_toolset_instances(const std::vector& vs_toolsets) - { - std::vector vs2017_v140_toolsets; - - // In constrast to v141 and above, there can only be a single instance of v140 (VS2017 vs VS2015). - const auto it = Util::find_if(vs_toolsets, [&](const Toolset& t) { return t.version == V_140; }); - - // If v140 is not available, then VS2017 cant use them. Return empty. - if (it == vs_toolsets.cend()) - { - return vs2017_v140_toolsets; - } - - // If it does exist, then create a matching v140 toolset for each v141 toolset - const Toolset v140_toolset = *it; - for (const Toolset& toolset : vs_toolsets) - { - if (toolset.version != V_141) - { - continue; - } - - Toolset t = Toolset{ - toolset.dumpbin, toolset.vcvarsall, {L"-vcvars_ver=14.0"}, V_140, toolset.supported_architectures}; - vs2017_v140_toolsets.push_back(std::move(t)); - } - - return vs2017_v140_toolsets; - } - - const Toolset& VcpkgPaths::get_toolset(const std::string& toolset_version) const + const Toolset& VcpkgPaths::get_toolset(const Optional& toolset_version, + const Optional& visual_studio_path) const { - const std::wstring& w_toolset_version = Strings::to_utf16(toolset_version); - // Invariant: toolsets are non-empty and sorted with newest at back() const std::vector& vs_toolsets = this->toolsets.get_lazy([this]() { return find_toolset_instances(*this); }); - if (w_toolset_version.empty()) + std::vector candidates = Util::to_vector_of_const_pointers(vs_toolsets); + const auto tsv = toolset_version.get(); + const auto vsp = visual_studio_path.get(); + + if (tsv && vsp) { - return vs_toolsets.back(); + const std::wstring w_toolset_version = Strings::to_utf16(*tsv); + const fs::path vs_root_path = *vsp; + Util::stable_keep_if(candidates, [&](const Toolset* t) { + return w_toolset_version == t->version && vs_root_path == t->visual_studio_root_path; + }); + Checks::check_exit(VCPKG_LINE_INFO, + !candidates.empty(), + "Could not find Visual Studio instace at %s with %s toolset.", + vs_root_path.generic_string(), + *tsv); + + Checks::check_exit(VCPKG_LINE_INFO, candidates.size() == 1); + return *candidates.back(); } - const auto toolset = - Util::find_if(vs_toolsets, [&](const Toolset& t) { return w_toolset_version == t.version; }); - Checks::check_exit( - VCPKG_LINE_INFO, toolset != vs_toolsets.end(), "Could not find toolset '%s'", toolset_version); + if (tsv) + { + const std::wstring w_toolset_version = Strings::to_utf16(*tsv); + Util::stable_keep_if(candidates, [&](const Toolset* t) { return w_toolset_version == t->version; }); + Checks::check_exit( + VCPKG_LINE_INFO, !candidates.empty(), "Could not find Visual Studio instace with %s toolset.", *tsv); + } - // If v140 is the selected toolset and VS2017 is available, then use VS2017's vcvarsall with the - // -vcvars_ver=14.0 option - const std::vector& vs2017_v140_toolsets = this->toolsets_vs2017_v140.get_lazy( - [&vs_toolsets]() { return create_vs2017_v140_toolset_instances(vs_toolsets); }); - if (w_toolset_version == V_140 && !vs2017_v140_toolsets.empty()) + if (vsp) { - return vs2017_v140_toolsets.back(); + const fs::path vs_root_path = *vsp; + Util::stable_keep_if(candidates, + [&](const Toolset* t) { return vs_root_path == t->visual_studio_root_path; }); + Checks::check_exit(VCPKG_LINE_INFO, + !candidates.empty(), + "Could not find Visual Studio instace at %s.", + vs_root_path.generic_string()); } - return *toolset; + return *candidates.back(); } Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); } diff --git a/toolsrc/src/commands_env.cpp b/toolsrc/src/commands_env.cpp index 073c501f5..6dad3e882 100644 --- a/toolsrc/src/commands_env.cpp +++ b/toolsrc/src/commands_env.cpp @@ -13,8 +13,8 @@ namespace vcpkg::Commands::Env args.check_and_get_optional_command_arguments({}); const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet); - System::cmd_execute_clean( - Build::make_build_env_cmd(pre_build_info, paths.get_toolset(pre_build_info.platform_toolset)) + L" && cmd"); + const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); + System::cmd_execute_clean(Build::make_build_env_cmd(pre_build_info, toolset) + L" && cmd"); Checks::exit_success(VCPKG_LINE_INFO); } diff --git a/toolsrc/src/vcpkg_Build.cpp b/toolsrc/src/vcpkg_Build.cpp index 853f84998..9be20629d 100644 --- a/toolsrc/src/vcpkg_Build.cpp +++ b/toolsrc/src/vcpkg_Build.cpp @@ -138,7 +138,7 @@ namespace vcpkg::Build const fs::path ports_cmake_script_path = paths.ports_cmake; const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet); - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset); + const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); const auto cmd_set_environment = make_build_env_cmd(pre_build_info, toolset); std::string features; @@ -386,7 +386,15 @@ namespace vcpkg::Build if (variable_name == "VCPKG_PLATFORM_TOOLSET") { - pre_build_info.platform_toolset = variable_value; + pre_build_info.platform_toolset = + variable_value.empty() ? nullopt : Optional{variable_value}; + continue; + } + + if (variable_name == "VCPKG_VISUAL_STUDIO_PATH") + { + pre_build_info.visual_studio_path = + variable_value.empty() ? nullopt : Optional{variable_value}; continue; } -- cgit v1.2.3 From 86f3a9dbbdc360db3716b878dd37f2dbec8e983c Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 5 Oct 2017 18:12:17 -0700 Subject: Fix Strings::split() to handle delimiters of arbitrary size --- toolsrc/src/vcpkg_Strings.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg_Strings.cpp b/toolsrc/src/vcpkg_Strings.cpp index 21df2c309..bbe6b29cd 100644 --- a/toolsrc/src/vcpkg_Strings.cpp +++ b/toolsrc/src/vcpkg_Strings.cpp @@ -154,11 +154,13 @@ namespace vcpkg::Strings return output; } + const size_t delimiter_length = delimiter.length(); size_t i = 0; for (size_t pos = s.find(delimiter); pos != std::string::npos; pos = s.find(delimiter, pos)) { output.push_back(s.substr(i, pos - i)); - i = ++pos; + pos += delimiter_length; + i = pos; } // Add the rest of the string after the last delimiter, unless there is nothing after it -- cgit v1.2.3 From f0c23aeb6b238ee0ba2dc272ee4c193f2f777460 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 5 Oct 2017 18:25:34 -0700 Subject: Completely rework Visual Studio detection - Now using vswhere.exe to detect all VS instance (2015 + 2017) - Default version preference order is now: stable, prerelease, legacy - Within each preference weight, the latest one is chosen - findVisualStudioInstallationInstances.ps1 now has a parameter to choose VS instance --- toolsrc/src/VcpkgPaths.cpp | 209 ++++++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 96 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index f080783c4..8264ec293 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -302,26 +302,33 @@ namespace vcpkg [this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; }); } - static std::vector get_vs2017_installation_instances(const VcpkgPaths& paths) + struct VisualStudioInstance + { + fs::path root_path; + std::string version; + std::string release_type; + std::string preference_weight; // Mostly unused, just for verification that order is as intended + + std::string major_version() const { return version.substr(0, 2); } + }; + + static std::vector get_visual_studio_instances(const VcpkgPaths& paths) { const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; const std::wstring cmd = System::create_powershell_script_cmd(script); const System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd); - Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Could not run script to detect VS 2017 instances"); - return Strings::split(ec_data.output, "\n"); - } + Checks::check_exit( + VCPKG_LINE_INFO, ec_data.exit_code == 0, "Could not run script to detect Visual Studio instances"); - static Optional get_vs2015_installation_instance() - { - const Optional vs2015_cmntools_optional = System::get_environment_variable(L"VS140COMNTOOLS"); - if (const auto v = vs2015_cmntools_optional.get()) + const std::vector instances_as_strings = Strings::split(ec_data.output, "\n"); + std::vector output; + for (const std::string& instance_as_string : instances_as_strings) { - const fs::path vs2015_cmntools = fs::path(*v).parent_path(); // The call to parent_path() is needed because - // the env variable has a trailing backslash - return vs2015_cmntools.parent_path().parent_path(); + const std::vector split = Strings::split(instance_as_string, "::"); + output.push_back({split.at(3), split.at(2), split.at(1), split.at(0)}); } - return nullopt; + return output; } static std::vector find_toolset_instances(const VcpkgPaths& paths) @@ -335,108 +342,118 @@ namespace vcpkg std::vector found_toolsets; - // VS2015 - const Optional vs_2015_installation_instance = get_vs2015_installation_instance(); - if (const auto vs_2015_root_path = vs_2015_installation_instance.get()) - { - const fs::path vs2015_vcvarsall_bat = *vs_2015_root_path / "VC" / "vcvarsall.bat"; + const std::vector vs_instances = get_visual_studio_instances(paths); + const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) { + return vs_instance.major_version() == "14"; + }) != vs_instances.cend(); - paths_examined.push_back(vs2015_vcvarsall_bat); - if (fs.exists(vs2015_vcvarsall_bat)) + // VS2017 + for (const VisualStudioInstance& vs_instance : vs_instances) + { + const std::string major_version = vs_instance.major_version(); + if (major_version == "15") { - const fs::path vs2015_dumpbin_exe = *vs_2015_root_path / "VC" / "bin" / "dumpbin.exe"; - paths_examined.push_back(vs2015_dumpbin_exe); + const fs::path vc_dir = vs_instance.root_path / "VC"; + + // Skip any instances that do not have vcvarsall. + const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build"; + const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat"; + paths_examined.push_back(vcvarsall_bat); + if (!fs.exists(vcvarsall_bat)) continue; - const fs::path vs2015_bin_dir = vs2015_vcvarsall_bat.parent_path() / "bin"; + // Get all supported architectures std::vector supported_architectures; - if (fs.exists(vs2015_bin_dir / "vcvars32.bat")) + if (fs.exists(vcvarsall_dir / "vcvars32.bat")) supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); - if (fs.exists(vs2015_bin_dir / "amd64\\vcvars64.bat")) - supported_architectures.push_back({L"x64", CPU::X64, CPU::X64}); - if (fs.exists(vs2015_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat")) + if (fs.exists(vcvarsall_dir / "vcvars64.bat")) + supported_architectures.push_back({L"amd64", CPU::X64, CPU::X64}); + if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat")) supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); - if (fs.exists(vs2015_bin_dir / "x86_arm\\vcvarsx86_arm.bat")) + if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat")) supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); - if (fs.exists(vs2015_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat")) + if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat")) supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); - if (fs.exists(vs2015_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat")) + if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat")) supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); - if (fs.exists(vs2015_dumpbin_exe)) + // Locate the "best" MSVC toolchain version + const fs::path msvc_path = vc_dir / "Tools" / "MSVC"; + std::vector msvc_subdirectories = fs.get_files_non_recursive(msvc_path); + Util::unstable_keep_if(msvc_subdirectories, + [&fs](const fs::path& path) { return fs.is_directory(path); }); + + // Sort them so that latest comes first + std::sort( + msvc_subdirectories.begin(), + msvc_subdirectories.end(), + [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); }); + + for (const fs::path& subdir : msvc_subdirectories) { - found_toolsets.push_back({*vs_2015_root_path, - vs2015_dumpbin_exe, - vs2015_vcvarsall_bat, - {}, - V_140, - supported_architectures}); + const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe"; + paths_examined.push_back(dumpbin_path); + if (fs.exists(dumpbin_path)) + { + found_toolsets.push_back(Toolset{ + vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}); + + if (v140_is_available) + { + found_toolsets.push_back(Toolset{vs_instance.root_path, + dumpbin_path, + vcvarsall_bat, + {L"-vcvars_ver=14.0"}, + V_140, + supported_architectures}); + } + + break; + } } - } - } - const bool v140_is_available = !found_toolsets.empty(); - - const std::vector vs2017_installation_instances = get_vs2017_installation_instances(paths); + continue; + } - // VS2017 - for (const std::string& instance : vs2017_installation_instances) - { - const fs::path vs_root_path = fs::path{instance}; - const fs::path vc_dir = vs_root_path / "VC"; - - // Skip any instances that do not have vcvarsall. - const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build"; - const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat"; - paths_examined.push_back(vcvarsall_bat); - if (!fs.exists(vcvarsall_bat)) continue; - - // Get all supported architectures - std::vector supported_architectures; - if (fs.exists(vcvarsall_dir / "vcvars32.bat")) - supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); - if (fs.exists(vcvarsall_dir / "vcvars64.bat")) - supported_architectures.push_back({L"amd64", CPU::X64, CPU::X64}); - if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat")) - supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); - if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat")) - supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); - if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat")) - supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); - if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat")) - supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); - - // Locate the "best" MSVC toolchain version - const fs::path msvc_path = vc_dir / "Tools" / "MSVC"; - std::vector msvc_subdirectories = fs.get_files_non_recursive(msvc_path); - Util::unstable_keep_if(msvc_subdirectories, [&fs](const fs::path& path) { return fs.is_directory(path); }); - - // Sort them so that latest comes first - std::sort(msvc_subdirectories.begin(), - msvc_subdirectories.end(), - [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); }); - - for (const fs::path& subdir : msvc_subdirectories) + if (major_version == "14") { - const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe"; - paths_examined.push_back(dumpbin_path); - if (fs.exists(dumpbin_path)) - { - found_toolsets.push_back( - Toolset{vs_root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}); + const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat"; - if (v140_is_available) + paths_examined.push_back(vcvarsall_bat); + if (fs.exists(vcvarsall_bat)) + { + const fs::path vs2015_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe"; + paths_examined.push_back(vs2015_dumpbin_exe); + + const fs::path vs2015_bin_dir = vcvarsall_bat.parent_path() / "bin"; + std::vector supported_architectures; + if (fs.exists(vs2015_bin_dir / "vcvars32.bat")) + supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); + if (fs.exists(vs2015_bin_dir / "amd64\\vcvars64.bat")) + supported_architectures.push_back({L"x64", CPU::X64, CPU::X64}); + if (fs.exists(vs2015_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat")) + supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); + if (fs.exists(vs2015_bin_dir / "x86_arm\\vcvarsx86_arm.bat")) + supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); + if (fs.exists(vs2015_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat")) + supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); + if (fs.exists(vs2015_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat")) + supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); + + if (fs.exists(vs2015_dumpbin_exe)) { - found_toolsets.push_back(Toolset{vs_root_path, - dumpbin_path, - vcvarsall_bat, - {L"-vcvars_ver=14.0"}, - V_140, - supported_architectures}); + found_toolsets.push_back({vs_instance.root_path, + vs2015_dumpbin_exe, + vcvarsall_bat, + {}, + V_140, + supported_architectures}); } - - break; } + + continue; } + + Checks::exit_with_message(VCPKG_LINE_INFO, "Expected major version 14 or 15 but got: %s", major_version); } if (found_toolsets.empty()) @@ -460,7 +477,7 @@ namespace vcpkg const std::vector& vs_toolsets = this->toolsets.get_lazy([this]() { return find_toolset_instances(*this); }); - std::vector candidates = Util::to_vector_of_const_pointers(vs_toolsets); + std::vector candidates = Util::element_pointers(vs_toolsets); const auto tsv = toolset_version.get(); const auto vsp = visual_studio_path.get(); @@ -500,7 +517,7 @@ namespace vcpkg vs_root_path.generic_string()); } - return *candidates.back(); + return *candidates.front(); } Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); } -- cgit v1.2.3 From 540046b564f95d262f5e4d2a4f988485e6469742 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Thu, 5 Oct 2017 22:16:43 -0700 Subject: [vcpkg] Remove assert that vswhere returns only 14 or 15. --- toolsrc/src/VcpkgPaths.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index 8264ec293..d84a2d281 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -452,8 +452,6 @@ namespace vcpkg continue; } - - Checks::exit_with_message(VCPKG_LINE_INFO, "Expected major version 14 or 15 but got: %s", major_version); } if (found_toolsets.empty()) -- cgit v1.2.3 From 3a6e34804a878ce0c3f74b208f9a5c0bdc75ec7b Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 5 Oct 2017 22:46:55 -0700 Subject: Remove stray comment --- toolsrc/src/VcpkgPaths.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index d84a2d281..08f21df34 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -347,7 +347,6 @@ namespace vcpkg return vs_instance.major_version() == "14"; }) != vs_instances.cend(); - // VS2017 for (const VisualStudioInstance& vs_instance : vs_instances) { const std::string major_version = vs_instance.major_version(); -- cgit v1.2.3 From 03c17e20268bb8bdbfec8710be80fd1310d38dff Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 5 Oct 2017 22:47:45 -0700 Subject: Remove unneeded "continue" --- toolsrc/src/VcpkgPaths.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index 08f21df34..e3e83af30 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -448,8 +448,6 @@ namespace vcpkg supported_architectures}); } } - - continue; } } -- cgit v1.2.3 From cd4142d3cac144c6f4e558cdfe8403e1987ff833 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Fri, 6 Oct 2017 14:06:02 -0700 Subject: Bump version of nuget to 4.3.0 (was 4.1.0) --- toolsrc/src/VcpkgPaths.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index e3e83af30..d14002e47 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -130,10 +130,10 @@ namespace vcpkg fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { - static constexpr std::array EXPECTED_VERSION = {4, 1, 0}; + static constexpr std::array EXPECTED_VERSION = {4, 3, 0}; static const std::wstring VERSION_CHECK_ARGUMENTS = Strings::WEMPTY; - const fs::path downloaded_copy = downloads_folder / "nuget-4.1.0" / "nuget.exe"; + const fs::path downloaded_copy = downloads_folder / "nuget-4.3.0" / "nuget.exe"; const std::vector from_path = Files::find_from_PATH(L"nuget"); std::vector candidate_paths; -- cgit v1.2.3 From 7cd465b8897bd9487db6d990e58dcac472b3d4d6 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Wed, 11 Oct 2017 17:56:01 -0700 Subject: `vcpkg install` Print summary when using the --keep-going flag --- toolsrc/src/commands_install.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp index d815332fe..4411dd3ae 100644 --- a/toolsrc/src/commands_install.cpp +++ b/toolsrc/src/commands_install.cpp @@ -556,6 +556,7 @@ namespace vcpkg::Commands::Install const bool no_downloads = options.find(OPTION_NO_DOWNLOADS) != options.cend(); const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); const KeepGoing keep_going = to_keep_going(options.find(OPTION_KEEP_GOING) != options.cend()); + const PrintSummary print_summary = to_print_summary(keep_going == KeepGoing::YES); // create the plan StatusParagraphs status_db = database_load_check(paths); @@ -606,7 +607,7 @@ namespace vcpkg::Commands::Install Checks::exit_success(VCPKG_LINE_INFO); } - perform_and_exit(action_plan, install_plan_options, keep_going, PrintSummary::NO, paths, status_db); + perform_and_exit(action_plan, install_plan_options, keep_going, print_summary, paths, status_db); Checks::exit_success(VCPKG_LINE_INFO); } -- cgit v1.2.3 From 00d89dbd66a658174233920d6be2a85f282ef29f Mon Sep 17 00:00:00 2001 From: Mikhail Paulyshka Date: Sun, 24 Sep 2017 16:01:37 +0300 Subject: vcpkg: initial ARM64 support --- toolsrc/src/PostBuildLint.cpp | 1 + toolsrc/src/VcpkgPaths.cpp | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'toolsrc/src') diff --git a/toolsrc/src/PostBuildLint.cpp b/toolsrc/src/PostBuildLint.cpp index a0699e15c..6d9a0da73 100644 --- a/toolsrc/src/PostBuildLint.cpp +++ b/toolsrc/src/PostBuildLint.cpp @@ -361,6 +361,7 @@ namespace vcpkg::PostBuildLint case MachineType::I386: return "x86"; case MachineType::ARM: case MachineType::ARMNT: return "arm"; + case MachineType::ARM64: return "arm64"; default: return "Machine Type Code = " + std::to_string(static_cast(machine_type)); } } diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp index d14002e47..dac9e40e7 100644 --- a/toolsrc/src/VcpkgPaths.cpp +++ b/toolsrc/src/VcpkgPaths.cpp @@ -370,10 +370,14 @@ namespace vcpkg supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat")) supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); + if (fs.exists(vcvarsall_dir / "vcvarsx86_arm64.bat")) + supported_architectures.push_back({L"x86_arm64", CPU::X86, CPU::ARM64}); if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat")) supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat")) supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); + if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm64.bat")) + supported_architectures.push_back({L"amd64_arm64", CPU::X64, CPU::ARM64}); // Locate the "best" MSVC toolchain version const fs::path msvc_path = vc_dir / "Tools" / "MSVC"; -- cgit v1.2.3 From dcfb9d45c13ba3adc22a47d62642f6acd2ee239b Mon Sep 17 00:00:00 2001 From: jasjuang Date: Thu, 12 Oct 2017 04:09:52 -0700 Subject: add NVCUDASAMPLES_ROOT to whitelist --- toolsrc/src/vcpkg_System.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp index 3b4c440d8..5293638e3 100644 --- a/toolsrc/src/vcpkg_System.cpp +++ b/toolsrc/src/vcpkg_System.cpp @@ -106,6 +106,8 @@ namespace vcpkg::System L"HTTPS_PROXY", // Enables find_package(CUDA) in CMake L"CUDA_PATH", + // Environmental variable generated automatically by CUDA after installation + L"NVCUDASAMPLES_ROOT", }; // Flush stdout before launching external process -- cgit v1.2.3 From 9e19c24d2911ff1ef648aeed0c596e3987eafebf Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 12 Oct 2017 14:03:01 -0700 Subject: `vcpkg autocomplete` More work on autocomplete --- toolsrc/src/commands_autocomplete.cpp | 83 +++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 22 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp index 3963f904b..abad19df7 100644 --- a/toolsrc/src/commands_autocomplete.cpp +++ b/toolsrc/src/commands_autocomplete.cpp @@ -1,9 +1,8 @@ #include "pch.h" #include "Paragraphs.h" -#include "SortedVector.h" +#include "metrics.h" #include "vcpkg_Commands.h" -#include "vcpkg_Maps.h" #include "vcpkg_System.h" #include "vcpkglib.h" @@ -35,7 +34,7 @@ namespace vcpkg::Commands::Autocomplete for (const auto& installed_package : installed_packages) { - auto sp = installed_package->package.displayname(); + const auto sp = installed_package->package.displayname(); if (istartswith(sp, start_with)) { @@ -47,33 +46,73 @@ namespace vcpkg::Commands::Autocomplete void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) { - static const std::string EXAMPLE = - Strings::format("The argument should be a command line to autocomplete.\n%s", - Commands::Help::create_example_string("autocomplete install z")); + Metrics::g_metrics.lock()->set_send_metrics(false); - args.check_min_arg_count(1, EXAMPLE); - args.check_max_arg_count(2, EXAMPLE); - args.check_and_get_optional_command_arguments({}); + if (args.command_arguments.size() != 1) + { + Checks::exit_success(VCPKG_LINE_INFO); + } - const std::string requested_command = args.command_arguments.at(0); - const std::string start_with = - args.command_arguments.size() > 1 ? args.command_arguments.at(1) : Strings::EMPTY; - std::vector results; - if (requested_command == "install") + const std::string to_autocomplete = args.command_arguments.at(0); + const std::vector tokens = Strings::split(to_autocomplete, " "); + if (tokens.size() == 1) { - auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); - auto& source_paragraphs = sources_and_errors.paragraphs; + const std::string requested_command = tokens[0]; + + std::vector valid_commands = {"install", + "search", + "remove", + "list", + "update", + "hash", + "help", + "integrate", + "export", + "edit", + "create", + "owns", + "cache", + "version", + "contact"}; + + Util::unstable_keep_if(valid_commands, [&](const std::string& s) { + return Strings::case_insensitive_ascii_starts_with(s, requested_command); + }); - results = autocomplete_install(source_paragraphs, start_with); + if (valid_commands.size() == 1) + { + System::println(valid_commands[0] + " "); + } + else + { + System::println(Strings::join("\n", valid_commands)); + } + + Checks::exit_success(VCPKG_LINE_INFO); } - else if (requested_command == "remove") + + if (tokens.size() == 2) { - const StatusParagraphs status_db = database_load_check(paths); - std::vector installed_packages = get_installed_ports(status_db); - results = autocomplete_remove(installed_packages, start_with); + const std::string requested_command = tokens[0]; + const std::string start_with = tokens[1]; + std::vector results; + if (requested_command == "install") + { + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + auto& source_paragraphs = sources_and_errors.paragraphs; + + results = autocomplete_install(source_paragraphs, start_with); + } + else if (requested_command == "remove") + { + const StatusParagraphs status_db = database_load_check(paths); + const std::vector installed_packages = get_installed_ports(status_db); + results = autocomplete_remove(installed_packages, start_with); + } + + System::println(Strings::join("\n", results)); } - System::println(Strings::join(" ", results)); Checks::exit_success(VCPKG_LINE_INFO); } } -- cgit v1.2.3 From 37fba399ef5c57573a1218206cb2c55f5f6bc13c Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Fri, 13 Oct 2017 05:33:32 +0300 Subject: [vcpkg-export-ifw] Safe description QtIFW support rich text for component description, bu some port has not safe ampersand symbol in description text (for example 'openexr' package), that should be replaced to '&' symbol name --- toolsrc/src/commands_export_ifw.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp index 191dbb763..c001a93ed 100644 --- a/toolsrc/src/commands_export_ifw.cpp +++ b/toolsrc/src/commands_export_ifw.cpp @@ -26,6 +26,14 @@ namespace vcpkg::Commands::Export::IFW return date_time_as_string; } + std::string safe_rich_from_plain_text(const std::string& text) + { + // match standalone ampersand, no HTML number or name + std::regex standalone_ampersand(R"###(&(?!(#[0-9]+|\w+);))###"); + + return std::regex_replace(text, standalone_ampersand, "&"); + } + fs::path get_packages_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) { return ifw_options.maybe_packages_dir_path.has_value() @@ -156,7 +164,7 @@ namespace vcpkg::Commands::Export::IFW )###", action.spec.name(), - binary_paragraph.description, + safe_rich_from_plain_text(binary_paragraph.description), binary_paragraph.version, create_release_date())); } -- cgit v1.2.3 From f6a0d78c82789af482eb3c885cbbb73aba7dfb38 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Fri, 13 Oct 2017 14:54:31 -0700 Subject: `autocomplete` Rework command, fixing a lot of corner cases in the process --- toolsrc/src/commands_autocomplete.cpp | 66 ++++++++++++++++------------------- 1 file changed, 30 insertions(+), 36 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp index abad19df7..478a3c478 100644 --- a/toolsrc/src/commands_autocomplete.cpp +++ b/toolsrc/src/commands_autocomplete.cpp @@ -5,6 +5,7 @@ #include "vcpkg_Commands.h" #include "vcpkg_System.h" #include "vcpkglib.h" +#include namespace vcpkg::Commands::Autocomplete { @@ -44,20 +45,26 @@ namespace vcpkg::Commands::Autocomplete return results; } + [[noreturn]] static void output_sorted_results_and_exit(const LineInfo& line_info, + std::vector&& results) + { + const SortedVector sorted_results(results); + System::println(Strings::join("\n", sorted_results)); + Checks::exit_success(line_info); + } + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) { Metrics::g_metrics.lock()->set_send_metrics(false); + const std::string to_autocomplete = Strings::join(" ", args.command_arguments); + const std::vector tokens = Strings::split(to_autocomplete, " "); - if (args.command_arguments.size() != 1) - { - Checks::exit_success(VCPKG_LINE_INFO); - } + std::smatch match; - const std::string to_autocomplete = args.command_arguments.at(0); - const std::vector tokens = Strings::split(to_autocomplete, " "); - if (tokens.size() == 1) + // Handles vcpkg + if (std::regex_match(to_autocomplete, match, std::regex{R"###(^(\S*)$)###"})) { - const std::string requested_command = tokens[0]; + const std::string requested_command = match[1].str(); std::vector valid_commands = {"install", "search", @@ -79,38 +86,25 @@ namespace vcpkg::Commands::Autocomplete return Strings::case_insensitive_ascii_starts_with(s, requested_command); }); - if (valid_commands.size() == 1) - { - System::println(valid_commands[0] + " "); - } - else - { - System::println(Strings::join("\n", valid_commands)); - } - - Checks::exit_success(VCPKG_LINE_INFO); + output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(valid_commands)); } - if (tokens.size() == 2) + // Handles vcpkg install + if (std::regex_match(to_autocomplete, match, std::regex{R"###(^install.* (\S*)$)###"})) { - const std::string requested_command = tokens[0]; - const std::string start_with = tokens[1]; - std::vector results; - if (requested_command == "install") - { - auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); - auto& source_paragraphs = sources_and_errors.paragraphs; - - results = autocomplete_install(source_paragraphs, start_with); - } - else if (requested_command == "remove") - { - const StatusParagraphs status_db = database_load_check(paths); - const std::vector installed_packages = get_installed_ports(status_db); - results = autocomplete_remove(installed_packages, start_with); - } + const std::string start_with = match[1].str(); + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + auto& source_paragraphs = sources_and_errors.paragraphs; + output_sorted_results_and_exit(VCPKG_LINE_INFO, autocomplete_install(source_paragraphs, start_with)); + } - System::println(Strings::join("\n", results)); + // Handles vcpkg remove + if (std::regex_match(to_autocomplete, match, std::regex{R"###(^remove.* (\S*)$)###"})) + { + const std::string start_with = match[1].str(); + const StatusParagraphs status_db = database_load_check(paths); + const std::vector installed_packages = get_installed_ports(status_db); + output_sorted_results_and_exit(VCPKG_LINE_INFO, autocomplete_remove(installed_packages, start_with)); } Checks::exit_success(VCPKG_LINE_INFO); -- cgit v1.2.3 From d46a992b3cbbbf3479ad3906aecd07e73174cd40 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Fri, 13 Oct 2017 15:17:11 -0700 Subject: Don't do function "aliasing" --- toolsrc/src/commands_autocomplete.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp index 478a3c478..6ded74f9b 100644 --- a/toolsrc/src/commands_autocomplete.cpp +++ b/toolsrc/src/commands_autocomplete.cpp @@ -13,13 +13,12 @@ namespace vcpkg::Commands::Autocomplete const std::vector>& source_paragraphs, const std::string& start_with) { std::vector results; - const auto& istartswith = Strings::case_insensitive_ascii_starts_with; for (const auto& source_control_file : source_paragraphs) { auto&& sp = *source_control_file->core_paragraph; - if (istartswith(sp.name, start_with)) + if (Strings::case_insensitive_ascii_starts_with(sp.name, start_with)) { results.push_back(sp.name); } @@ -31,13 +30,12 @@ namespace vcpkg::Commands::Autocomplete const std::string& start_with) { std::vector results; - const auto& istartswith = Strings::case_insensitive_ascii_starts_with; for (const auto& installed_package : installed_packages) { const auto sp = installed_package->package.displayname(); - if (istartswith(sp, start_with)) + if (Strings::case_insensitive_ascii_starts_with(sp, start_with)) { results.push_back(sp); } -- cgit v1.2.3 From e17de99599a2f114faab1bb4821fbaad4d266c95 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Fri, 13 Oct 2017 18:37:41 -0700 Subject: [vcpkg] Re-layout all files using new organization scheme. All filenames and directories are lowercase. Use dots for namespace separation. --- toolsrc/src/BinaryParagraph.cpp | 123 ---- toolsrc/src/LineInfo.cpp | 9 - toolsrc/src/MachineType.cpp | 41 -- toolsrc/src/PackageSpec.cpp | 175 ------ toolsrc/src/PackageSpecParseResult.cpp | 20 - toolsrc/src/ParagraphParseResult.cpp | 34 -- toolsrc/src/Paragraphs.cpp | 304 --------- toolsrc/src/PostBuildLint.cpp | 846 -------------------------- toolsrc/src/PostBuildLint_BuildType.cpp | 70 --- toolsrc/src/SourceParagraph.cpp | 262 -------- toolsrc/src/StatusParagraph.cpp | 86 --- toolsrc/src/StatusParagraphs.cpp | 90 --- toolsrc/src/VcpkgCmdArguments.cpp | 283 --------- toolsrc/src/VcpkgPaths.cpp | 519 ---------------- toolsrc/src/VersionT.cpp | 19 - toolsrc/src/coff_file_reader.cpp | 308 ---------- toolsrc/src/commands_autocomplete.cpp | 79 --- toolsrc/src/commands_available_commands.cpp | 50 -- toolsrc/src/commands_build.cpp | 102 ---- toolsrc/src/commands_build_external.cpp | 21 - toolsrc/src/commands_cache.cpp | 68 --- toolsrc/src/commands_ci.cpp | 60 -- toolsrc/src/commands_contact.cpp | 22 - toolsrc/src/commands_create.cpp | 38 -- toolsrc/src/commands_depends.cpp | 55 -- toolsrc/src/commands_edit.cpp | 86 --- toolsrc/src/commands_env.cpp | 21 - toolsrc/src/commands_export.cpp | 526 ---------------- toolsrc/src/commands_export_ifw.cpp | 481 --------------- toolsrc/src/commands_hash.cpp | 55 -- toolsrc/src/commands_help.cpp | 131 ---- toolsrc/src/commands_import.cpp | 119 ---- toolsrc/src/commands_install.cpp | 613 ------------------- toolsrc/src/commands_integrate.cpp | 342 ----------- toolsrc/src/commands_list.cpp | 74 --- toolsrc/src/commands_owns.cpp | 37 -- toolsrc/src/commands_portsdiff.cpp | 181 ------ toolsrc/src/commands_remove.cpp | 249 -------- toolsrc/src/commands_search.cpp | 145 ----- toolsrc/src/commands_update.cpp | 73 --- toolsrc/src/commands_version.cpp | 67 -- toolsrc/src/metrics.cpp | 395 ------------ toolsrc/src/test_install_plan.cpp | 486 --------------- toolsrc/src/tests.arguments.cpp | 58 ++ toolsrc/src/tests.dependencies.cpp | 110 ++++ toolsrc/src/tests.installplan.cpp | 487 +++++++++++++++ toolsrc/src/tests.packagespec.cpp | 120 ++++ toolsrc/src/tests.paragraph.cpp | 389 ++++++++++++ toolsrc/src/tests_arguments.cpp | 58 -- toolsrc/src/tests_dependencies.cpp | 110 ---- toolsrc/src/tests_package_spec.cpp | 119 ---- toolsrc/src/tests_paragraph.cpp | 388 ------------ toolsrc/src/triplet.cpp | 55 -- toolsrc/src/vcpkg.cpp | 56 +- toolsrc/src/vcpkg/base/checks.cpp | 81 +++ toolsrc/src/vcpkg/base/chrono.cpp | 61 ++ toolsrc/src/vcpkg/base/cofffilereader.cpp | 308 ++++++++++ toolsrc/src/vcpkg/base/enums.cpp | 14 + toolsrc/src/vcpkg/base/files.cpp | 208 +++++++ toolsrc/src/vcpkg/base/lineinfo.cpp | 9 + toolsrc/src/vcpkg/base/machinetype.cpp | 41 ++ toolsrc/src/vcpkg/base/strings.cpp | 174 ++++++ toolsrc/src/vcpkg/base/system.cpp | 350 +++++++++++ toolsrc/src/vcpkg/binaryparagraph.cpp | 123 ++++ toolsrc/src/vcpkg/build.cpp | 533 ++++++++++++++++ toolsrc/src/vcpkg/commands.autocomplete.cpp | 78 +++ toolsrc/src/vcpkg/commands.buildexternal.cpp | 23 + toolsrc/src/vcpkg/commands.cache.cpp | 69 +++ toolsrc/src/vcpkg/commands.ci.cpp | 62 ++ toolsrc/src/vcpkg/commands.contact.cpp | 22 + toolsrc/src/vcpkg/commands.cpp | 56 ++ toolsrc/src/vcpkg/commands.create.cpp | 39 ++ toolsrc/src/vcpkg/commands.dependinfo.cpp | 56 ++ toolsrc/src/vcpkg/commands.edit.cpp | 87 +++ toolsrc/src/vcpkg/commands.env.cpp | 22 + toolsrc/src/vcpkg/commands.exportifw.cpp | 482 +++++++++++++++ toolsrc/src/vcpkg/commands.hash.cpp | 55 ++ toolsrc/src/vcpkg/commands.import.cpp | 120 ++++ toolsrc/src/vcpkg/commands.integrate.cpp | 342 +++++++++++ toolsrc/src/vcpkg/commands.list.cpp | 75 +++ toolsrc/src/vcpkg/commands.owns.cpp | 38 ++ toolsrc/src/vcpkg/commands.portsdiff.cpp | 182 ++++++ toolsrc/src/vcpkg/commands.search.cpp | 146 +++++ toolsrc/src/vcpkg/commands.version.cpp | 67 ++ toolsrc/src/vcpkg/dependencies.cpp | 669 ++++++++++++++++++++ toolsrc/src/vcpkg/export.cpp | 528 ++++++++++++++++ toolsrc/src/vcpkg/globalstate.cpp | 13 + toolsrc/src/vcpkg/help.cpp | 131 ++++ toolsrc/src/vcpkg/input.cpp | 55 ++ toolsrc/src/vcpkg/install.cpp | 615 +++++++++++++++++++ toolsrc/src/vcpkg/metrics.cpp | 398 ++++++++++++ toolsrc/src/vcpkg/packagespec.cpp | 175 ++++++ toolsrc/src/vcpkg/packagespecparseresult.cpp | 21 + toolsrc/src/vcpkg/paragraphparseresult.cpp | 34 ++ toolsrc/src/vcpkg/paragraphs.cpp | 304 +++++++++ toolsrc/src/vcpkg/parse.cpp | 81 +++ toolsrc/src/vcpkg/postbuildlint.buildtype.cpp | 70 +++ toolsrc/src/vcpkg/postbuildlint.cpp | 846 ++++++++++++++++++++++++++ toolsrc/src/vcpkg/remove.cpp | 251 ++++++++ toolsrc/src/vcpkg/sourceparagraph.cpp | 264 ++++++++ toolsrc/src/vcpkg/statusparagraph.cpp | 86 +++ toolsrc/src/vcpkg/statusparagraphs.cpp | 89 +++ toolsrc/src/vcpkg/triplet.cpp | 55 ++ toolsrc/src/vcpkg/update.cpp | 75 +++ toolsrc/src/vcpkg/vcpkgcmdarguments.cpp | 284 +++++++++ toolsrc/src/vcpkg/vcpkglib.cpp | 247 ++++++++ toolsrc/src/vcpkg/vcpkgpaths.cpp | 519 ++++++++++++++++ toolsrc/src/vcpkg/versiont.cpp | 19 + toolsrc/src/vcpkg_Build.cpp | 406 ------------ toolsrc/src/vcpkg_Build_BuildPolicy.cpp | 40 -- toolsrc/src/vcpkg_Checks.cpp | 81 --- toolsrc/src/vcpkg_Chrono.cpp | 61 -- toolsrc/src/vcpkg_Dependencies.cpp | 669 -------------------- toolsrc/src/vcpkg_Enums.cpp | 14 - toolsrc/src/vcpkg_Files.cpp | 209 ------- toolsrc/src/vcpkg_GlobalState.cpp | 13 - toolsrc/src/vcpkg_Input.cpp | 54 -- toolsrc/src/vcpkg_Parse.cpp | 80 --- toolsrc/src/vcpkg_Strings.cpp | 174 ------ toolsrc/src/vcpkg_System.cpp | 349 ----------- toolsrc/src/vcpkg_metrics_uploader.cpp | 18 - toolsrc/src/vcpkglib.cpp | 247 -------- toolsrc/src/vcpkgmetricsuploader.cpp | 20 + 123 files changed, 10973 insertions(+), 10929 deletions(-) delete mode 100644 toolsrc/src/BinaryParagraph.cpp delete mode 100644 toolsrc/src/LineInfo.cpp delete mode 100644 toolsrc/src/MachineType.cpp delete mode 100644 toolsrc/src/PackageSpec.cpp delete mode 100644 toolsrc/src/PackageSpecParseResult.cpp delete mode 100644 toolsrc/src/ParagraphParseResult.cpp delete mode 100644 toolsrc/src/Paragraphs.cpp delete mode 100644 toolsrc/src/PostBuildLint.cpp delete mode 100644 toolsrc/src/PostBuildLint_BuildType.cpp delete mode 100644 toolsrc/src/SourceParagraph.cpp delete mode 100644 toolsrc/src/StatusParagraph.cpp delete mode 100644 toolsrc/src/StatusParagraphs.cpp delete mode 100644 toolsrc/src/VcpkgCmdArguments.cpp delete mode 100644 toolsrc/src/VcpkgPaths.cpp delete mode 100644 toolsrc/src/VersionT.cpp delete mode 100644 toolsrc/src/coff_file_reader.cpp delete mode 100644 toolsrc/src/commands_autocomplete.cpp delete mode 100644 toolsrc/src/commands_available_commands.cpp delete mode 100644 toolsrc/src/commands_build.cpp delete mode 100644 toolsrc/src/commands_build_external.cpp delete mode 100644 toolsrc/src/commands_cache.cpp delete mode 100644 toolsrc/src/commands_ci.cpp delete mode 100644 toolsrc/src/commands_contact.cpp delete mode 100644 toolsrc/src/commands_create.cpp delete mode 100644 toolsrc/src/commands_depends.cpp delete mode 100644 toolsrc/src/commands_edit.cpp delete mode 100644 toolsrc/src/commands_env.cpp delete mode 100644 toolsrc/src/commands_export.cpp delete mode 100644 toolsrc/src/commands_export_ifw.cpp delete mode 100644 toolsrc/src/commands_hash.cpp delete mode 100644 toolsrc/src/commands_help.cpp delete mode 100644 toolsrc/src/commands_import.cpp delete mode 100644 toolsrc/src/commands_install.cpp delete mode 100644 toolsrc/src/commands_integrate.cpp delete mode 100644 toolsrc/src/commands_list.cpp delete mode 100644 toolsrc/src/commands_owns.cpp delete mode 100644 toolsrc/src/commands_portsdiff.cpp delete mode 100644 toolsrc/src/commands_remove.cpp delete mode 100644 toolsrc/src/commands_search.cpp delete mode 100644 toolsrc/src/commands_update.cpp delete mode 100644 toolsrc/src/commands_version.cpp delete mode 100644 toolsrc/src/metrics.cpp delete mode 100644 toolsrc/src/test_install_plan.cpp create mode 100644 toolsrc/src/tests.arguments.cpp create mode 100644 toolsrc/src/tests.dependencies.cpp create mode 100644 toolsrc/src/tests.installplan.cpp create mode 100644 toolsrc/src/tests.packagespec.cpp create mode 100644 toolsrc/src/tests.paragraph.cpp delete mode 100644 toolsrc/src/tests_arguments.cpp delete mode 100644 toolsrc/src/tests_dependencies.cpp delete mode 100644 toolsrc/src/tests_package_spec.cpp delete mode 100644 toolsrc/src/tests_paragraph.cpp delete mode 100644 toolsrc/src/triplet.cpp create mode 100644 toolsrc/src/vcpkg/base/checks.cpp create mode 100644 toolsrc/src/vcpkg/base/chrono.cpp create mode 100644 toolsrc/src/vcpkg/base/cofffilereader.cpp create mode 100644 toolsrc/src/vcpkg/base/enums.cpp create mode 100644 toolsrc/src/vcpkg/base/files.cpp create mode 100644 toolsrc/src/vcpkg/base/lineinfo.cpp create mode 100644 toolsrc/src/vcpkg/base/machinetype.cpp create mode 100644 toolsrc/src/vcpkg/base/strings.cpp create mode 100644 toolsrc/src/vcpkg/base/system.cpp create mode 100644 toolsrc/src/vcpkg/binaryparagraph.cpp create mode 100644 toolsrc/src/vcpkg/build.cpp create mode 100644 toolsrc/src/vcpkg/commands.autocomplete.cpp create mode 100644 toolsrc/src/vcpkg/commands.buildexternal.cpp create mode 100644 toolsrc/src/vcpkg/commands.cache.cpp create mode 100644 toolsrc/src/vcpkg/commands.ci.cpp create mode 100644 toolsrc/src/vcpkg/commands.contact.cpp create mode 100644 toolsrc/src/vcpkg/commands.cpp create mode 100644 toolsrc/src/vcpkg/commands.create.cpp create mode 100644 toolsrc/src/vcpkg/commands.dependinfo.cpp create mode 100644 toolsrc/src/vcpkg/commands.edit.cpp create mode 100644 toolsrc/src/vcpkg/commands.env.cpp create mode 100644 toolsrc/src/vcpkg/commands.exportifw.cpp create mode 100644 toolsrc/src/vcpkg/commands.hash.cpp create mode 100644 toolsrc/src/vcpkg/commands.import.cpp create mode 100644 toolsrc/src/vcpkg/commands.integrate.cpp create mode 100644 toolsrc/src/vcpkg/commands.list.cpp create mode 100644 toolsrc/src/vcpkg/commands.owns.cpp create mode 100644 toolsrc/src/vcpkg/commands.portsdiff.cpp create mode 100644 toolsrc/src/vcpkg/commands.search.cpp create mode 100644 toolsrc/src/vcpkg/commands.version.cpp create mode 100644 toolsrc/src/vcpkg/dependencies.cpp create mode 100644 toolsrc/src/vcpkg/export.cpp create mode 100644 toolsrc/src/vcpkg/globalstate.cpp create mode 100644 toolsrc/src/vcpkg/help.cpp create mode 100644 toolsrc/src/vcpkg/input.cpp create mode 100644 toolsrc/src/vcpkg/install.cpp create mode 100644 toolsrc/src/vcpkg/metrics.cpp create mode 100644 toolsrc/src/vcpkg/packagespec.cpp create mode 100644 toolsrc/src/vcpkg/packagespecparseresult.cpp create mode 100644 toolsrc/src/vcpkg/paragraphparseresult.cpp create mode 100644 toolsrc/src/vcpkg/paragraphs.cpp create mode 100644 toolsrc/src/vcpkg/parse.cpp create mode 100644 toolsrc/src/vcpkg/postbuildlint.buildtype.cpp create mode 100644 toolsrc/src/vcpkg/postbuildlint.cpp create mode 100644 toolsrc/src/vcpkg/remove.cpp create mode 100644 toolsrc/src/vcpkg/sourceparagraph.cpp create mode 100644 toolsrc/src/vcpkg/statusparagraph.cpp create mode 100644 toolsrc/src/vcpkg/statusparagraphs.cpp create mode 100644 toolsrc/src/vcpkg/triplet.cpp create mode 100644 toolsrc/src/vcpkg/update.cpp create mode 100644 toolsrc/src/vcpkg/vcpkgcmdarguments.cpp create mode 100644 toolsrc/src/vcpkg/vcpkglib.cpp create mode 100644 toolsrc/src/vcpkg/vcpkgpaths.cpp create mode 100644 toolsrc/src/vcpkg/versiont.cpp delete mode 100644 toolsrc/src/vcpkg_Build.cpp delete mode 100644 toolsrc/src/vcpkg_Build_BuildPolicy.cpp delete mode 100644 toolsrc/src/vcpkg_Checks.cpp delete mode 100644 toolsrc/src/vcpkg_Chrono.cpp delete mode 100644 toolsrc/src/vcpkg_Dependencies.cpp delete mode 100644 toolsrc/src/vcpkg_Enums.cpp delete mode 100644 toolsrc/src/vcpkg_Files.cpp delete mode 100644 toolsrc/src/vcpkg_GlobalState.cpp delete mode 100644 toolsrc/src/vcpkg_Input.cpp delete mode 100644 toolsrc/src/vcpkg_Parse.cpp delete mode 100644 toolsrc/src/vcpkg_Strings.cpp delete mode 100644 toolsrc/src/vcpkg_System.cpp delete mode 100644 toolsrc/src/vcpkg_metrics_uploader.cpp delete mode 100644 toolsrc/src/vcpkglib.cpp create mode 100644 toolsrc/src/vcpkgmetricsuploader.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp deleted file mode 100644 index 1504912ab..000000000 --- a/toolsrc/src/BinaryParagraph.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "pch.h" - -#include "BinaryParagraph.h" -#include "vcpkg_Checks.h" -#include "vcpkg_Parse.h" - -namespace vcpkg -{ - namespace Fields - { - static const std::string PACKAGE = "Package"; - static const std::string VERSION = "Version"; - static const std::string ARCHITECTURE = "Architecture"; - static const std::string MULTI_ARCH = "Multi-Arch"; - } - - namespace Fields - { - static const std::string FEATURE = "Feature"; - static const std::string DESCRIPTION = "Description"; - static const std::string MAINTAINER = "Maintainer"; - static const std::string DEPENDS = "Depends"; - static const std::string DEFAULTFEATURES = "Default-Features"; - } - - BinaryParagraph::BinaryParagraph() = default; - - BinaryParagraph::BinaryParagraph(std::unordered_map fields) - { - using namespace vcpkg::Parse; - - ParagraphParser parser(std::move(fields)); - - { - std::string name; - parser.required_field(Fields::PACKAGE, name); - std::string architecture; - parser.required_field(Fields::ARCHITECTURE, architecture); - this->spec = PackageSpec::from_name_and_triplet(name, Triplet::from_canonical_name(architecture)) - .value_or_exit(VCPKG_LINE_INFO); - } - - // one or the other - this->version = parser.optional_field(Fields::VERSION); - this->feature = parser.optional_field(Fields::FEATURE); - - this->description = parser.optional_field(Fields::DESCRIPTION); - this->maintainer = parser.optional_field(Fields::MAINTAINER); - - std::string multi_arch; - parser.required_field(Fields::MULTI_ARCH, multi_arch); - - this->depends = parse_comma_list(parser.optional_field(Fields::DEPENDS)); - if (this->feature.empty()) - { - this->default_features = parse_comma_list(parser.optional_field(Fields::DEFAULTFEATURES)); - } - - if (const auto err = parser.error_info(this->spec.to_string())) - { - System::println( - System::Color::error, "Error: while parsing the Binary Paragraph for %s", this->spec.to_string()); - print_error_message(err); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - // prefer failing above when possible because it gives better information - Checks::check_exit(VCPKG_LINE_INFO, multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch); - } - - BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const Triplet& triplet) - { - this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO); - this->version = spgh.version; - this->description = spgh.description; - this->maintainer = spgh.maintainer; - this->depends = filter_dependencies(spgh.depends, triplet); - } - - BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const FeatureParagraph& fpgh, const Triplet& triplet) - { - this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO); - this->version = Strings::EMPTY; - this->feature = fpgh.name; - this->description = fpgh.description; - this->maintainer = Strings::EMPTY; - this->depends = filter_dependencies(fpgh.depends, triplet); - } - - std::string BinaryParagraph::displayname() const - { - const auto f = this->feature.empty() ? "core" : this->feature; - return Strings::format("%s[%s]:%s", this->spec.name(), f, this->spec.triplet()); - } - - std::string BinaryParagraph::dir() const { return this->spec.dir(); } - - std::string BinaryParagraph::fullstem() const - { - return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.triplet()); - } - - void serialize(const BinaryParagraph& pgh, std::string& out_str) - { - out_str.append("Package: ").append(pgh.spec.name()).push_back('\n'); - if (!pgh.version.empty()) - out_str.append("Version: ").append(pgh.version).push_back('\n'); - else if (!pgh.feature.empty()) - out_str.append("Feature: ").append(pgh.feature).push_back('\n'); - if (!pgh.depends.empty()) - { - out_str.append("Depends: "); - out_str.append(Strings::join(", ", pgh.depends)); - out_str.push_back('\n'); - } - - out_str.append("Architecture: ").append(pgh.spec.triplet().to_string()).push_back('\n'); - out_str.append("Multi-Arch: same\n"); - - if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n'); - if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n'); - } -} diff --git a/toolsrc/src/LineInfo.cpp b/toolsrc/src/LineInfo.cpp deleted file mode 100644 index d1bf9a4b1..000000000 --- a/toolsrc/src/LineInfo.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "pch.h" - -#include "LineInfo.h" -#include "vcpkg_Strings.h" - -namespace vcpkg -{ - std::string LineInfo::to_string() const { return Strings::format("%s(%d)", this->file_name, this->line_number); } -} diff --git a/toolsrc/src/MachineType.cpp b/toolsrc/src/MachineType.cpp deleted file mode 100644 index af6378c88..000000000 --- a/toolsrc/src/MachineType.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "pch.h" - -#include "MachineType.h" -#include "vcpkg_Checks.h" - -namespace vcpkg -{ - MachineType to_machine_type(const uint16_t value) - { - const MachineType t = static_cast(value); - switch (t) - { - case MachineType::UNKNOWN: - case MachineType::AM33: - case MachineType::AMD64: - case MachineType::ARM: - case MachineType::ARM64: - case MachineType::ARMNT: - case MachineType::EBC: - case MachineType::I386: - case MachineType::IA64: - case MachineType::M32R: - case MachineType::MIPS16: - case MachineType::MIPSFPU: - case MachineType::MIPSFPU16: - case MachineType::POWERPC: - case MachineType::POWERPCFP: - case MachineType::R4000: - case MachineType::RISCV32: - case MachineType::RISCV64: - case MachineType::RISCV128: - case MachineType::SH3: - case MachineType::SH3DSP: - case MachineType::SH4: - case MachineType::SH5: - case MachineType::THUMB: - case MachineType::WCEMIPSV2: return t; - default: Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown machine type code 0x%x", value); - } - } -} diff --git a/toolsrc/src/PackageSpec.cpp b/toolsrc/src/PackageSpec.cpp deleted file mode 100644 index 890de8899..000000000 --- a/toolsrc/src/PackageSpec.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "pch.h" - -#include "PackageSpec.h" -#include "vcpkg_Parse.h" -#include "vcpkg_Util.h" - -using vcpkg::Parse::parse_comma_list; - -namespace vcpkg -{ - static bool is_valid_package_spec_char(char c) - { - return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)) || (c == '[') || (c == ']'); - } - - std::string FeatureSpec::to_string() const - { - if (feature().empty()) return spec().to_string(); - return Strings::format("%s[%s]:%s", name(), feature(), triplet()); - } - - std::vector FeatureSpec::from_strings_and_triplet(const std::vector& depends, - const Triplet& triplet) - { - std::vector f_specs; - for (auto&& depend : depends) - { - auto maybe_spec = ParsedSpecifier::from_string(depend); - if (auto spec = maybe_spec.get()) - { - Checks::check_exit(VCPKG_LINE_INFO, - spec->triplet.empty(), - "error: triplets cannot currently be specified in this context: %s", - depend); - PackageSpec pspec = - PackageSpec::from_name_and_triplet(spec->name, triplet).value_or_exit(VCPKG_LINE_INFO); - - for (auto&& feature : spec->features) - f_specs.push_back(FeatureSpec{pspec, feature}); - - if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, Strings::EMPTY}); - } - else - { - Checks::exit_with_message(VCPKG_LINE_INFO, - "error while parsing feature list: %s: %s", - vcpkg::to_string(maybe_spec.error()), - depend); - } - } - return f_specs; - } - - std::vector FullPackageSpec::to_feature_specs(const std::vector& specs) - { - std::vector ret; - for (auto&& spec : specs) - { - ret.emplace_back(spec.package_spec, Strings::EMPTY); - for (auto&& feature : spec.features) - ret.emplace_back(spec.package_spec, feature); - } - return ret; - } - - ExpectedT FullPackageSpec::from_string(const std::string& spec_as_string, - const Triplet& default_triplet) - { - auto res = ParsedSpecifier::from_string(spec_as_string); - if (auto p = res.get()) - { - FullPackageSpec fspec; - Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(p->triplet); - fspec.package_spec = PackageSpec::from_name_and_triplet(p->name, t).value_or_exit(VCPKG_LINE_INFO); - fspec.features = std::move(p->features); - return fspec; - } - return res.error(); - } - - ExpectedT PackageSpec::from_name_and_triplet(const std::string& name, - const Triplet& triplet) - { - if (Util::find_if_not(name, is_valid_package_spec_char) != name.end()) - { - return PackageSpecParseResult::INVALID_CHARACTERS; - } - - PackageSpec p; - p.m_name = name; - p.m_triplet = triplet; - return p; - } - - const std::string& PackageSpec::name() const { return this->m_name; } - - const Triplet& PackageSpec::triplet() const { return this->m_triplet; } - - std::string PackageSpec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_triplet); } - - std::string PackageSpec::to_string() const { return Strings::format("%s:%s", this->name(), this->triplet()); } - - bool operator==(const PackageSpec& left, const PackageSpec& right) - { - return left.name() == right.name() && left.triplet() == right.triplet(); - } - - bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); } - - ExpectedT ParsedSpecifier::from_string(const std::string& input) - { - auto pos = input.find(':'); - auto pos_l_bracket = input.find('['); - auto pos_r_bracket = input.find(']'); - - ParsedSpecifier f; - if (pos == std::string::npos && pos_l_bracket == std::string::npos) - { - f.name = input; - return f; - } - else if (pos == std::string::npos) - { - if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket) - { - return PackageSpecParseResult::INVALID_CHARACTERS; - } - const std::string name = input.substr(0, pos_l_bracket); - f.name = name; - f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1)); - return f; - } - else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos) - { - const std::string name = input.substr(0, pos); - f.triplet = input.substr(pos + 1); - f.name = name; - } - else - { - if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket) - { - return PackageSpecParseResult::INVALID_CHARACTERS; - } - const std::string name = input.substr(0, pos_l_bracket); - f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1)); - f.triplet = input.substr(pos + 1); - f.name = name; - } - - auto pos2 = input.find(':', pos + 1); - if (pos2 != std::string::npos) - { - return PackageSpecParseResult::TOO_MANY_COLONS; - } - return f; - } - - ExpectedT Features::from_string(const std::string& name) - { - auto maybe_spec = ParsedSpecifier::from_string(name); - if (auto spec = maybe_spec.get()) - { - Checks::check_exit( - VCPKG_LINE_INFO, spec->triplet.empty(), "error: triplet not allowed in specifier: %s", name); - - Features f; - f.name = spec->name; - f.features = spec->features; - return f; - } - - return maybe_spec.error(); - } -} diff --git a/toolsrc/src/PackageSpecParseResult.cpp b/toolsrc/src/PackageSpecParseResult.cpp deleted file mode 100644 index 838c788ba..000000000 --- a/toolsrc/src/PackageSpecParseResult.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "pch.h" - -#include "PackageSpecParseResult.h" -#include "vcpkg_Checks.h" - -namespace vcpkg -{ - CStringView to_string(PackageSpecParseResult ev) noexcept - { - switch (ev) - { - case PackageSpecParseResult::SUCCESS: return "OK"; - case PackageSpecParseResult::TOO_MANY_COLONS: return "Too many colons"; - case PackageSpecParseResult::INVALID_CHARACTERS: - return "Contains invalid characters. Only alphanumeric lowercase ASCII characters and dashes are " - "allowed"; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } -} diff --git a/toolsrc/src/ParagraphParseResult.cpp b/toolsrc/src/ParagraphParseResult.cpp deleted file mode 100644 index 5c8c1d59d..000000000 --- a/toolsrc/src/ParagraphParseResult.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "pch.h" - -#include "ParagraphParseResult.h" -#include "vcpkg_Checks.h" - -namespace vcpkg -{ - const char* ParagraphParseResultCategoryImpl::name() const noexcept { return "ParagraphParseResult"; } - - std::string ParagraphParseResultCategoryImpl::message(int ev) const noexcept - { - switch (static_cast(ev)) - { - case ParagraphParseResult::SUCCESS: return "OK"; - case ParagraphParseResult::EXPECTED_ONE_PARAGRAPH: return "There should be exactly one paragraph"; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - const std::error_category& paragraph_parse_result_category() - { - static ParagraphParseResultCategoryImpl instance; - return instance; - } - - std::error_code make_error_code(ParagraphParseResult e) - { - return std::error_code(static_cast(e), paragraph_parse_result_category()); - } - - ParagraphParseResult to_paragraph_parse_result(int i) { return static_cast(i); } - - ParagraphParseResult to_paragraph_parse_result(std::error_code ec) { return to_paragraph_parse_result(ec.value()); } -} diff --git a/toolsrc/src/Paragraphs.cpp b/toolsrc/src/Paragraphs.cpp deleted file mode 100644 index 6a6f191df..000000000 --- a/toolsrc/src/Paragraphs.cpp +++ /dev/null @@ -1,304 +0,0 @@ -#include "pch.h" - -#include "ParagraphParseResult.h" -#include "Paragraphs.h" -#include "vcpkg_Files.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_Util.h" - -using namespace vcpkg::Parse; - -namespace vcpkg::Paragraphs -{ - struct Parser - { - Parser(const char* c, const char* e) : cur(c), end(e) {} - - private: - const char* cur; - const char* const end; - - void peek(char& ch) const - { - if (cur == end) - ch = 0; - else - ch = *cur; - } - - void next(char& ch) - { - if (cur == end) - ch = 0; - else - { - ++cur; - peek(ch); - } - } - - void skip_comment(char& ch) - { - while (ch != '\r' && ch != '\n' && ch != '\0') - next(ch); - if (ch == '\r') next(ch); - if (ch == '\n') next(ch); - } - - void skip_spaces(char& ch) - { - while (ch == ' ' || ch == '\t') - next(ch); - } - - static bool is_alphanum(char ch) - { - return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); - } - - static bool is_comment(char ch) { return (ch == '#'); } - - static bool is_lineend(char ch) { return ch == '\r' || ch == '\n' || ch == 0; } - - void get_fieldvalue(char& ch, std::string& fieldvalue) - { - fieldvalue.clear(); - - auto beginning_of_line = cur; - do - { - // scan to end of current line (it is part of the field value) - while (!is_lineend(ch)) - next(ch); - - fieldvalue.append(beginning_of_line, cur); - - if (ch == '\r') next(ch); - if (ch == '\n') next(ch); - - if (is_alphanum(ch) || is_comment(ch)) - { - // Line begins a new field. - return; - } - - beginning_of_line = cur; - - // Line may continue the current field with data or terminate the paragraph, - // depending on first nonspace character. - skip_spaces(ch); - - if (is_lineend(ch)) - { - // Line was whitespace or empty. - // This terminates the field and the paragraph. - // We leave the blank line's whitespace consumed, because it doesn't matter. - return; - } - - // First nonspace is not a newline. This continues the current field value. - // We forcibly convert all newlines into single '\n' for ease of text handling later on. - fieldvalue.push_back('\n'); - } while (true); - } - - void get_fieldname(char& ch, std::string& fieldname) - { - auto begin_fieldname = cur; - while (is_alphanum(ch) || ch == '-') - next(ch); - Checks::check_exit(VCPKG_LINE_INFO, ch == ':', "Expected ':'"); - fieldname = std::string(begin_fieldname, cur); - - // skip ': ' - next(ch); - skip_spaces(ch); - } - - void get_paragraph(char& ch, std::unordered_map& fields) - { - fields.clear(); - std::string fieldname; - std::string fieldvalue; - do - { - if (is_comment(ch)) - { - skip_comment(ch); - continue; - } - - get_fieldname(ch, fieldname); - - auto it = fields.find(fieldname); - Checks::check_exit(VCPKG_LINE_INFO, it == fields.end(), "Duplicate field"); - - get_fieldvalue(ch, fieldvalue); - - fields.emplace(fieldname, fieldvalue); - } while (!is_lineend(ch)); - } - - public: - std::vector> get_paragraphs() - { - std::vector> paragraphs; - - char ch; - peek(ch); - - while (ch != 0) - { - if (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t') - { - next(ch); - continue; - } - - paragraphs.emplace_back(); - get_paragraph(ch, paragraphs.back()); - } - - return paragraphs; - } - }; - - Expected> get_single_paragraph(const Files::Filesystem& fs, - const fs::path& control_path) - { - const Expected contents = fs.read_contents(control_path); - if (auto spgh = contents.get()) - { - return parse_single_paragraph(*spgh); - } - - return contents.error(); - } - - Expected>> get_paragraphs(const Files::Filesystem& fs, - const fs::path& control_path) - { - const Expected contents = fs.read_contents(control_path); - if (auto spgh = contents.get()) - { - return parse_paragraphs(*spgh); - } - - return contents.error(); - } - - Expected> parse_single_paragraph(const std::string& str) - { - const std::vector> p = - Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs(); - - if (p.size() == 1) - { - return p.at(0); - } - - return std::error_code(ParagraphParseResult::EXPECTED_ONE_PARAGRAPH); - } - - Expected>> parse_paragraphs(const std::string& str) - { - return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs(); - } - - ParseExpected try_load_port(const Files::Filesystem& fs, const fs::path& path) - { - Expected>> pghs = get_paragraphs(fs, path / "CONTROL"); - if (auto vector_pghs = pghs.get()) - { - auto csf = SourceControlFile::parse_control_file(std::move(*vector_pghs)); - if (!GlobalState::feature_packages) - { - if (auto ptr = csf.get()) - { - Checks::check_exit(VCPKG_LINE_INFO, ptr->get() != nullptr); - ptr->get()->core_paragraph->default_features.clear(); - ptr->get()->feature_paragraphs.clear(); - } - } - return csf; - } - auto error_info = std::make_unique(); - error_info->name = path.filename().generic_u8string(); - error_info->error = pghs.error(); - return error_info; - } - - Expected try_load_cached_control_package(const VcpkgPaths& paths, const PackageSpec& spec) - { - Expected>> pghs = - get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL"); - - if (auto p = pghs.get()) - { - BinaryControlFile bcf; - bcf.core_paragraph = BinaryParagraph(p->front()); - p->erase(p->begin()); - - bcf.features = - Util::fmap(*p, [&](auto&& raw_feature) -> BinaryParagraph { return BinaryParagraph(raw_feature); }); - - return bcf; - } - - return pghs.error(); - } - - LoadResults try_load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir) - { - LoadResults ret; - for (auto&& path : fs.get_files_non_recursive(ports_dir)) - { - auto maybe_spgh = try_load_port(fs, path); - if (const auto spgh = maybe_spgh.get()) - { - ret.paragraphs.emplace_back(std::move(*spgh)); - } - else - { - ret.errors.emplace_back(std::move(maybe_spgh).error()); - } - } - return ret; - } - - std::vector> load_all_ports(const Files::Filesystem& fs, - const fs::path& ports_dir) - { - auto results = try_load_all_ports(fs, ports_dir); - if (!results.errors.empty()) - { - if (GlobalState::debugging) - { - print_error_message(results.errors); - } - else - { - for (auto&& error : results.errors) - { - System::println( - System::Color::warning, "Warning: an error occurred while parsing '%s'", error->name); - } - System::println(System::Color::warning, - "Use '--debug' to get more information about the parse failures.\n"); - } - } - return std::move(results.paragraphs); - } - - std::map load_all_port_names_and_versions(const Files::Filesystem& fs, - const fs::path& ports_dir) - { - auto all_ports = load_all_ports(fs, ports_dir); - - std::map names_and_versions; - for (auto&& port : all_ports) - names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); - - return names_and_versions; - } -} diff --git a/toolsrc/src/PostBuildLint.cpp b/toolsrc/src/PostBuildLint.cpp deleted file mode 100644 index a0699e15c..000000000 --- a/toolsrc/src/PostBuildLint.cpp +++ /dev/null @@ -1,846 +0,0 @@ -#include "pch.h" - -#include "PackageSpec.h" -#include "PostBuildLint.h" -#include "PostBuildLint_BuildType.h" -#include "VcpkgPaths.h" -#include "coff_file_reader.h" -#include "vcpkg_Build.h" -#include "vcpkg_Files.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" - -using vcpkg::Build::BuildInfo; -using vcpkg::Build::BuildPolicy; -using vcpkg::Build::PreBuildInfo; - -namespace vcpkg::PostBuildLint -{ - static auto has_extension_pred(const Files::Filesystem& fs, const std::string& ext) - { - return [&fs, ext](const fs::path& path) { return !fs.is_directory(path) && path.extension() == ext; }; - } - - enum class LintStatus - { - SUCCESS = 0, - ERROR_DETECTED = 1 - }; - - struct OutdatedDynamicCrt - { - std::string name; - std::regex regex; - - OutdatedDynamicCrt(const std::string& name, const std::string& regex_as_string) - : name(name), regex(std::regex(regex_as_string, std::regex_constants::icase)) - { - } - }; - - Span get_outdated_dynamic_crts(CStringView toolset) - { - static const std::vector V_NO_MSVCRT = { - {"msvcp100.dll", R"(msvcp100\.dll)"}, - {"msvcp100d.dll", R"(msvcp100d\.dll)"}, - {"msvcp110.dll", R"(msvcp110\.dll)"}, - {"msvcp110_win.dll", R"(msvcp110_win\.dll)"}, - {"msvcp120.dll", R"(msvcp120\.dll)"}, - {"msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"}, - {"msvcp60.dll", R"(msvcp60\.dll)"}, - {"msvcp60.dll", R"(msvcp60\.dll)"}, - - {"msvcr100.dll", R"(msvcr100\.dll)"}, - {"msvcr100d.dll", R"(msvcr100d\.dll)"}, - {"msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)"}, - {"msvcr110.dll", R"(msvcr110\.dll)"}, - {"msvcr120.dll", R"(msvcr120\.dll)"}, - {"msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"}, - {"msvcrt20.dll", R"(msvcrt20\.dll)"}, - {"msvcrt40.dll", R"(msvcrt40\.dll)"}}; - - return V_NO_MSVCRT; - } - - static LintStatus check_for_files_in_include_directory(const Files::Filesystem& fs, - const Build::BuildPolicies& policies, - const fs::path& package_dir) - { - if (policies.is_enabled(BuildPolicy::EMPTY_INCLUDE_FOLDER)) - { - return LintStatus::SUCCESS; - } - - const fs::path include_dir = package_dir / "include"; - if (!fs.exists(include_dir) || fs.is_empty(include_dir)) - { - System::println(System::Color::warning, - "The folder /include is empty. This indicates the library was not correctly installed."); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_for_files_in_debug_include_directory(const Files::Filesystem& fs, - const fs::path& package_dir) - { - const fs::path debug_include_dir = package_dir / "debug" / "include"; - - std::vector files_found = fs.get_files_recursive(debug_include_dir); - - Util::unstable_keep_if( - files_found, [&fs](const fs::path& path) { return !fs.is_directory(path) && path.extension() != ".ifc"; }); - - if (!files_found.empty()) - { - System::println(System::Color::warning, - "Include files should not be duplicated into the /debug/include directory. If this cannot " - "be disabled in the project cmake, use\n" - " file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)"); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_for_files_in_debug_share_directory(const Files::Filesystem& fs, const fs::path& package_dir) - { - const fs::path debug_share = package_dir / "debug" / "share"; - - if (fs.exists(debug_share)) - { - System::println(System::Color::warning, - "/debug/share should not exist. Please reorganize any important files, then use\n" - " file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share)"); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_folder_lib_cmake(const Files::Filesystem& fs, - const fs::path& package_dir, - const PackageSpec& spec) - { - const fs::path lib_cmake = package_dir / "lib" / "cmake"; - if (fs.exists(lib_cmake)) - { - System::println( - System::Color::warning, - "The /lib/cmake folder should be merged with /debug/lib/cmake and moved to /share/%s/cmake.", - spec.name()); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_for_misplaced_cmake_files(const Files::Filesystem& fs, - const fs::path& package_dir, - const PackageSpec& spec) - { - std::vector dirs = { - package_dir / "cmake", - package_dir / "debug" / "cmake", - package_dir / "lib" / "cmake", - package_dir / "debug" / "lib" / "cmake", - }; - - std::vector misplaced_cmake_files; - for (auto&& dir : dirs) - { - auto files = fs.get_files_recursive(dir); - for (auto&& file : files) - { - if (!fs.is_directory(file) && file.extension() == ".cmake") - misplaced_cmake_files.push_back(std::move(file)); - } - } - - if (!misplaced_cmake_files.empty()) - { - System::println( - System::Color::warning, - "The following cmake files were found outside /share/%s. Please place cmake files in /share/%s.", - spec.name(), - spec.name()); - Files::print_paths(misplaced_cmake_files); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_folder_debug_lib_cmake(const Files::Filesystem& fs, - const fs::path& package_dir, - const PackageSpec& spec) - { - const fs::path lib_cmake_debug = package_dir / "debug" / "lib" / "cmake"; - if (fs.exists(lib_cmake_debug)) - { - System::println(System::Color::warning, - "The /debug/lib/cmake folder should be merged with /lib/cmake into /share/%s", - spec.name()); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_for_dlls_in_lib_dir(const Files::Filesystem& fs, const fs::path& package_dir) - { - std::vector dlls = fs.get_files_recursive(package_dir / "lib"); - Util::unstable_keep_if(dlls, has_extension_pred(fs, ".dll")); - - if (!dlls.empty()) - { - System::println(System::Color::warning, - "\nThe following dlls were found in /lib or /debug/lib. Please move them to /bin or " - "/debug/bin, respectively."); - Files::print_paths(dlls); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_for_copyright_file(const Files::Filesystem& fs, - const PackageSpec& spec, - const VcpkgPaths& paths) - { - const fs::path packages_dir = paths.packages / spec.dir(); - const fs::path copyright_file = packages_dir / "share" / spec.name() / "copyright"; - if (fs.exists(copyright_file)) - { - return LintStatus::SUCCESS; - } - const fs::path current_buildtrees_dir = paths.buildtrees / spec.name(); - const fs::path current_buildtrees_dir_src = current_buildtrees_dir / "src"; - - std::vector potential_copyright_files; - // We only search in the root of each unpacked source archive to reduce false positives - auto src_dirs = fs.get_files_non_recursive(current_buildtrees_dir_src); - for (auto&& src_dir : src_dirs) - { - if (!fs.is_directory(src_dir)) continue; - - for (auto&& src_file : fs.get_files_non_recursive(src_dir)) - { - const std::string filename = src_file.filename().string(); - - if (filename == "LICENSE" || filename == "LICENSE.txt" || filename == "COPYING") - { - potential_copyright_files.push_back(src_file); - } - } - } - - System::println(System::Color::warning, - "The software license must be available at ${CURRENT_PACKAGES_DIR}/share/%s/copyright", - spec.name()); - if (potential_copyright_files.size() == - 1) // if there is only one candidate, provide the cmake lines needed to place it in the proper location - { - const fs::path found_file = potential_copyright_files[0]; - const fs::path relative_path = found_file.string().erase( - 0, current_buildtrees_dir.string().size() + 1); // The +1 is needed to remove the "/" - System::println( - "\n file(COPY ${CURRENT_BUILDTREES_DIR}/%s DESTINATION ${CURRENT_PACKAGES_DIR}/share/%s)\n" - " file(RENAME ${CURRENT_PACKAGES_DIR}/share/%s/%s ${CURRENT_PACKAGES_DIR}/share/%s/copyright)", - relative_path.generic_string(), - spec.name(), - spec.name(), - found_file.filename().generic_string(), - spec.name()); - } - else if (potential_copyright_files.size() > 1) - { - System::println(System::Color::warning, "The following files are potential copyright files:"); - Files::print_paths(potential_copyright_files); - } - return LintStatus::ERROR_DETECTED; - } - - static LintStatus check_for_exes(const Files::Filesystem& fs, const fs::path& package_dir) - { - std::vector exes = fs.get_files_recursive(package_dir / "bin"); - Util::unstable_keep_if(exes, has_extension_pred(fs, ".exe")); - - if (!exes.empty()) - { - System::println( - System::Color::warning, - "The following EXEs were found in /bin or /debug/bin. EXEs are not valid distribution targets."); - Files::print_paths(exes); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_exports_of_dlls(const std::vector& dlls, const fs::path& dumpbin_exe) - { - std::vector dlls_with_no_exports; - for (const fs::path& dll : dlls) - { - const std::wstring cmd_line = - Strings::wformat(LR"("%s" /exports "%s")", dumpbin_exe.native(), dll.native()); - System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, - ec_data.exit_code == 0, - "Running command:\n %s\n failed", - Strings::to_utf8(cmd_line)); - - if (ec_data.output.find("ordinal hint RVA name") == std::string::npos) - { - dlls_with_no_exports.push_back(dll); - } - } - - if (!dlls_with_no_exports.empty()) - { - System::println(System::Color::warning, "The following DLLs have no exports:"); - Files::print_paths(dlls_with_no_exports); - System::println(System::Color::warning, "DLLs without any exports are likely a bug in the build script."); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_uwp_bit_of_dlls(const std::string& expected_system_name, - const std::vector& dlls, - const fs::path dumpbin_exe) - { - if (expected_system_name != "WindowsStore") - { - return LintStatus::SUCCESS; - } - - std::vector dlls_with_improper_uwp_bit; - for (const fs::path& dll : dlls) - { - const std::wstring cmd_line = - Strings::wformat(LR"("%s" /headers "%s")", dumpbin_exe.native(), dll.native()); - System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, - ec_data.exit_code == 0, - "Running command:\n %s\n failed", - Strings::to_utf8(cmd_line)); - - if (ec_data.output.find("App Container") == std::string::npos) - { - dlls_with_improper_uwp_bit.push_back(dll); - } - } - - if (!dlls_with_improper_uwp_bit.empty()) - { - System::println(System::Color::warning, "The following DLLs do not have the App Container bit set:"); - Files::print_paths(dlls_with_improper_uwp_bit); - System::println(System::Color::warning, "This bit is required for Windows Store apps."); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - struct FileAndArch - { - fs::path file; - std::string actual_arch; - }; - - static std::string get_actual_architecture(const MachineType& machine_type) - { - switch (machine_type) - { - case MachineType::AMD64: - case MachineType::IA64: return "x64"; - case MachineType::I386: return "x86"; - case MachineType::ARM: - case MachineType::ARMNT: return "arm"; - default: return "Machine Type Code = " + std::to_string(static_cast(machine_type)); - } - } - - static void print_invalid_architecture_files(const std::string& expected_architecture, - std::vector binaries_with_invalid_architecture) - { - System::println(System::Color::warning, "The following files were built for an incorrect architecture:"); - System::println(); - for (const FileAndArch& b : binaries_with_invalid_architecture) - { - System::println(" %s", b.file.generic_string()); - System::println("Expected %s, but was: %s", expected_architecture, b.actual_arch); - System::println(); - } - } - - static LintStatus check_dll_architecture(const std::string& expected_architecture, - const std::vector& files) - { - std::vector binaries_with_invalid_architecture; - - for (const fs::path& file : files) - { - Checks::check_exit(VCPKG_LINE_INFO, - file.extension() == ".dll", - "The file extension was not .dll: %s", - file.generic_string()); - const CoffFileReader::DllInfo info = CoffFileReader::read_dll(file); - const std::string actual_architecture = get_actual_architecture(info.machine_type); - - if (expected_architecture != actual_architecture) - { - binaries_with_invalid_architecture.push_back({file, actual_architecture}); - } - } - - if (!binaries_with_invalid_architecture.empty()) - { - print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_lib_architecture(const std::string& expected_architecture, - const std::vector& files) - { - std::vector binaries_with_invalid_architecture; - - for (const fs::path& file : files) - { - Checks::check_exit(VCPKG_LINE_INFO, - file.extension() == ".lib", - "The file extension was not .lib: %s", - file.generic_string()); - CoffFileReader::LibInfo info = CoffFileReader::read_lib(file); - - // This is zero for folly's debug library - // TODO: Why? - if (info.machine_types.size() == 0) return LintStatus::SUCCESS; - - Checks::check_exit(VCPKG_LINE_INFO, - info.machine_types.size() == 1, - "Found more than 1 architecture in file %s", - file.generic_string()); - - const std::string actual_architecture = get_actual_architecture(info.machine_types.at(0)); - if (expected_architecture != actual_architecture) - { - binaries_with_invalid_architecture.push_back({file, actual_architecture}); - } - } - - if (!binaries_with_invalid_architecture.empty()) - { - print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_no_dlls_present(const std::vector& dlls) - { - if (dlls.empty()) - { - return LintStatus::SUCCESS; - } - - System::println(System::Color::warning, - "DLLs should not be present in a static build, but the following DLLs were found:"); - Files::print_paths(dlls); - return LintStatus::ERROR_DETECTED; - } - - static LintStatus check_matching_debug_and_release_binaries(const std::vector& debug_binaries, - const std::vector& release_binaries) - { - const size_t debug_count = debug_binaries.size(); - const size_t release_count = release_binaries.size(); - if (debug_count == release_count) - { - return LintStatus::SUCCESS; - } - - System::println(System::Color::warning, - "Mismatching number of debug and release binaries. Found %d for debug but %d for release.", - debug_count, - release_count); - System::println("Debug binaries"); - Files::print_paths(debug_binaries); - - System::println("Release binaries"); - Files::print_paths(release_binaries); - - if (debug_count == 0) - { - System::println(System::Color::warning, "Debug binaries were not found"); - } - if (release_count == 0) - { - System::println(System::Color::warning, "Release binaries were not found"); - } - - System::println(); - - return LintStatus::ERROR_DETECTED; - } - - static LintStatus check_lib_files_are_available_if_dlls_are_available(const Build::BuildPolicies& policies, - const size_t lib_count, - const size_t dll_count, - const fs::path& lib_dir) - { - if (policies.is_enabled(BuildPolicy::DLLS_WITHOUT_LIBS)) return LintStatus::SUCCESS; - - if (lib_count == 0 && dll_count != 0) - { - System::println(System::Color::warning, "Import libs were not present in %s", lib_dir.u8string()); - System::println(System::Color::warning, - "If this is intended, add the following line in the portfile:\n" - " SET(%s enabled)", - to_cmake_variable(BuildPolicy::DLLS_WITHOUT_LIBS)); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_bin_folders_are_not_present_in_static_build(const Files::Filesystem& fs, - const fs::path& package_dir) - { - const fs::path bin = package_dir / "bin"; - const fs::path debug_bin = package_dir / "debug" / "bin"; - - if (!fs.exists(bin) && !fs.exists(debug_bin)) - { - return LintStatus::SUCCESS; - } - - if (fs.exists(bin)) - { - System::println(System::Color::warning, - R"(There should be no bin\ directory in a static build, but %s is present.)", - bin.u8string()); - } - - if (fs.exists(debug_bin)) - { - System::println(System::Color::warning, - R"(There should be no debug\bin\ directory in a static build, but %s is present.)", - debug_bin.u8string()); - } - - System::println( - System::Color::warning, - R"(If the creation of bin\ and/or debug\bin\ cannot be disabled, use this in the portfile to remove them)" - "\n" - "\n" - R"###( if(VCPKG_LIBRARY_LINKAGE STREQUAL static))###" - "\n" - R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin))###" - "\n" - R"###( endif())###" - "\n"); - - return LintStatus::ERROR_DETECTED; - } - - static LintStatus check_no_empty_folders(const Files::Filesystem& fs, const fs::path& dir) - { - std::vector empty_directories = fs.get_files_recursive(dir); - - Util::unstable_keep_if(empty_directories, [&fs](const fs::path& current) { - return fs.is_directory(current) && fs.is_empty(current); - }); - - if (!empty_directories.empty()) - { - System::println(System::Color::warning, "There should be no empty directories in %s", dir.generic_string()); - System::println("The following empty directories were found: "); - Files::print_paths(empty_directories); - System::println( - System::Color::warning, - "If a directory should be populated but is not, this might indicate an error in the portfile.\n" - "If the directories are not needed and their creation cannot be disabled, use something like this in " - "the portfile to remove them:\n" - "\n" - R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/a/dir ${CURRENT_PACKAGES_DIR}/some/other/dir))###" - "\n" - "\n"); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - struct BuildTypeAndFile - { - fs::path file; - BuildType build_type; - }; - - static LintStatus check_crt_linkage_of_libs(const BuildType& expected_build_type, - const std::vector& libs, - const fs::path dumpbin_exe) - { - std::vector bad_build_types(BuildTypeC::VALUES.cbegin(), BuildTypeC::VALUES.cend()); - bad_build_types.erase(std::remove(bad_build_types.begin(), bad_build_types.end(), expected_build_type), - bad_build_types.end()); - - std::vector libs_with_invalid_crt; - - for (const fs::path& lib : libs) - { - const std::wstring cmd_line = - Strings::wformat(LR"("%s" /directives "%s")", dumpbin_exe.native(), lib.native()); - System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, - ec_data.exit_code == 0, - "Running command:\n %s\n failed", - Strings::to_utf8(cmd_line)); - - for (const BuildType& bad_build_type : bad_build_types) - { - if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), bad_build_type.crt_regex())) - { - libs_with_invalid_crt.push_back({lib, bad_build_type}); - break; - } - } - } - - if (!libs_with_invalid_crt.empty()) - { - System::println(System::Color::warning, - "Expected %s crt linkage, but the following libs had invalid crt linkage:", - expected_build_type.to_string()); - System::println(); - for (const BuildTypeAndFile btf : libs_with_invalid_crt) - { - System::println(" %s: %s", btf.file.generic_string(), btf.build_type.to_string()); - } - System::println(); - - System::println(System::Color::warning, - "To inspect the lib files, use:\n dumpbin.exe /directives mylibfile.lib"); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - struct OutdatedDynamicCrtAndFile - { - fs::path file; - OutdatedDynamicCrt outdated_crt; - - OutdatedDynamicCrtAndFile() = delete; - }; - - static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector& dlls, - const fs::path dumpbin_exe, - const BuildInfo& build_info) - { - if (build_info.policies.is_enabled(BuildPolicy::ALLOW_OBSOLETE_MSVCRT)) return LintStatus::SUCCESS; - - std::vector dlls_with_outdated_crt; - - for (const fs::path& dll : dlls) - { - const std::wstring cmd_line = - Strings::wformat(LR"("%s" /dependents "%s")", dumpbin_exe.native(), dll.native()); - System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, - ec_data.exit_code == 0, - "Running command:\n %s\n failed", - Strings::to_utf8(cmd_line)); - - for (const OutdatedDynamicCrt& outdated_crt : get_outdated_dynamic_crts("v141")) - { - if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), outdated_crt.regex)) - { - dlls_with_outdated_crt.push_back({dll, outdated_crt}); - break; - } - } - } - - if (!dlls_with_outdated_crt.empty()) - { - System::println(System::Color::warning, "Detected outdated dynamic CRT in the following files:"); - System::println(); - for (const OutdatedDynamicCrtAndFile btf : dlls_with_outdated_crt) - { - System::println(" %s: %s", btf.file.generic_string(), btf.outdated_crt.name); - } - System::println(); - - System::println(System::Color::warning, - "To inspect the dll files, use:\n dumpbin.exe /dependents mydllfile.dll"); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static LintStatus check_no_files_in_dir(const Files::Filesystem& fs, const fs::path& dir) - { - std::vector misplaced_files = fs.get_files_non_recursive(dir); - Util::unstable_keep_if(misplaced_files, [&fs](const fs::path& path) { - const std::string filename = path.filename().generic_string(); - if (Strings::case_insensitive_ascii_compare(filename.c_str(), "CONTROL") == 0 || - Strings::case_insensitive_ascii_compare(filename.c_str(), "BUILD_INFO") == 0) - return false; - return !fs.is_directory(path); - }); - - if (!misplaced_files.empty()) - { - System::println(System::Color::warning, "The following files are placed in\n%s: ", dir.u8string()); - Files::print_paths(misplaced_files); - System::println(System::Color::warning, "Files cannot be present in those directories.\n"); - return LintStatus::ERROR_DETECTED; - } - - return LintStatus::SUCCESS; - } - - static void operator+=(size_t& left, const LintStatus& right) { left += static_cast(right); } - - static size_t perform_all_checks_and_return_error_count(const PackageSpec& spec, - const VcpkgPaths& paths, - const PreBuildInfo& pre_build_info, - const BuildInfo& build_info) - { - const auto& fs = paths.get_filesystem(); - - // for dumpbin - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); - const fs::path package_dir = paths.package_dir(spec); - - size_t error_count = 0; - - if (build_info.policies.is_enabled(BuildPolicy::EMPTY_PACKAGE)) - { - return error_count; - } - - error_count += check_for_files_in_include_directory(fs, build_info.policies, package_dir); - error_count += check_for_files_in_debug_include_directory(fs, package_dir); - error_count += check_for_files_in_debug_share_directory(fs, package_dir); - error_count += check_folder_lib_cmake(fs, package_dir, spec); - error_count += check_for_misplaced_cmake_files(fs, package_dir, spec); - error_count += check_folder_debug_lib_cmake(fs, package_dir, spec); - error_count += check_for_dlls_in_lib_dir(fs, package_dir); - error_count += check_for_dlls_in_lib_dir(fs, package_dir / "debug"); - error_count += check_for_copyright_file(fs, spec, paths); - error_count += check_for_exes(fs, package_dir); - error_count += check_for_exes(fs, package_dir / "debug"); - - const fs::path debug_lib_dir = package_dir / "debug" / "lib"; - const fs::path release_lib_dir = package_dir / "lib"; - const fs::path debug_bin_dir = package_dir / "debug" / "bin"; - const fs::path release_bin_dir = package_dir / "bin"; - - std::vector debug_libs = fs.get_files_recursive(debug_lib_dir); - Util::unstable_keep_if(debug_libs, has_extension_pred(fs, ".lib")); - std::vector release_libs = fs.get_files_recursive(release_lib_dir); - Util::unstable_keep_if(release_libs, has_extension_pred(fs, ".lib")); - - error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs); - - { - std::vector libs; - libs.insert(libs.cend(), debug_libs.cbegin(), debug_libs.cend()); - libs.insert(libs.cend(), release_libs.cbegin(), release_libs.cend()); - - error_count += check_lib_architecture(pre_build_info.target_architecture, libs); - } - - std::vector debug_dlls = fs.get_files_recursive(debug_bin_dir); - Util::unstable_keep_if(debug_dlls, has_extension_pred(fs, ".dll")); - std::vector release_dlls = fs.get_files_recursive(release_bin_dir); - Util::unstable_keep_if(release_dlls, has_extension_pred(fs, ".dll")); - - switch (build_info.library_linkage) - { - case Build::LinkageType::DYNAMIC: - { - error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); - - error_count += check_lib_files_are_available_if_dlls_are_available( - build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir); - error_count += check_lib_files_are_available_if_dlls_are_available( - build_info.policies, release_libs.size(), release_dlls.size(), release_lib_dir); - - std::vector dlls; - dlls.insert(dlls.cend(), debug_dlls.cbegin(), debug_dlls.cend()); - dlls.insert(dlls.cend(), release_dlls.cbegin(), release_dlls.cend()); - - error_count += check_exports_of_dlls(dlls, toolset.dumpbin); - error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls, toolset.dumpbin); - error_count += check_dll_architecture(pre_build_info.target_architecture, dlls); - - error_count += check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin, build_info); - break; - } - case Build::LinkageType::STATIC: - { - auto dlls = release_dlls; - dlls.insert(dlls.end(), debug_dlls.begin(), debug_dlls.end()); - error_count += check_no_dlls_present(dlls); - - error_count += check_bin_folders_are_not_present_in_static_build(fs, package_dir); - - if (!build_info.policies.is_enabled(BuildPolicy::ONLY_RELEASE_CRT)) - { - error_count += - check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::DEBUG, build_info.crt_linkage), - debug_libs, - toolset.dumpbin); - } - error_count += - check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::RELEASE, build_info.crt_linkage), - release_libs, - toolset.dumpbin); - break; - } - default: Checks::unreachable(VCPKG_LINE_INFO); - } - - error_count += check_no_empty_folders(fs, package_dir); - error_count += check_no_files_in_dir(fs, package_dir); - error_count += check_no_files_in_dir(fs, package_dir / "debug"); - - return error_count; - } - - size_t perform_all_checks(const PackageSpec& spec, - const VcpkgPaths& paths, - const PreBuildInfo& pre_build_info, - const BuildInfo& build_info) - { - System::println("-- Performing post-build validation"); - const size_t error_count = perform_all_checks_and_return_error_count(spec, paths, pre_build_info, build_info); - - if (error_count != 0) - { - const fs::path portfile = paths.ports / spec.name() / "portfile.cmake"; - System::println(System::Color::error, - "Found %u error(s). Please correct the portfile:\n %s", - error_count, - portfile.string()); - } - - System::println("-- Performing post-build validation done"); - - return error_count; - } -} diff --git a/toolsrc/src/PostBuildLint_BuildType.cpp b/toolsrc/src/PostBuildLint_BuildType.cpp deleted file mode 100644 index 649f0ccca..000000000 --- a/toolsrc/src/PostBuildLint_BuildType.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "pch.h" - -#include "PostBuildLint_BuildType.h" -#include "vcpkg_Checks.h" - -namespace vcpkg::PostBuildLint -{ - BuildType BuildType::value_of(const ConfigurationType& config, const Build::LinkageType& linkage) - { - if (config == ConfigurationType::DEBUG && linkage == Build::LinkageType::STATIC) - { - return BuildTypeC::DEBUG_STATIC; - } - - if (config == ConfigurationType::DEBUG && linkage == Build::LinkageType::DYNAMIC) - { - return BuildTypeC::DEBUG_DYNAMIC; - } - - if (config == ConfigurationType::RELEASE && linkage == Build::LinkageType::STATIC) - { - return BuildTypeC::RELEASE_STATIC; - } - - if (config == ConfigurationType::RELEASE && linkage == Build::LinkageType::DYNAMIC) - { - return BuildTypeC::RELEASE_DYNAMIC; - } - - Checks::unreachable(VCPKG_LINE_INFO); - } - - const ConfigurationType& BuildType::config() const { return this->m_config; } - - const Build::LinkageType& BuildType::linkage() const { return this->m_linkage; } - - const std::regex& BuildType::crt_regex() const - { - static const std::regex REGEX_DEBUG_STATIC(R"(/DEFAULTLIB:LIBCMTD)", std::regex_constants::icase); - static const std::regex REGEX_DEBUG_DYNAMIC(R"(/DEFAULTLIB:MSVCRTD)", std::regex_constants::icase); - static const std::regex REGEX_RELEASE_STATIC(R"(/DEFAULTLIB:LIBCMT[^D])", std::regex_constants::icase); - static const std::regex REGEX_RELEASE_DYNAMIC(R"(/DEFAULTLIB:MSVCRT[^D])", std::regex_constants::icase); - - switch (backing_enum) - { - case BuildTypeC::DEBUG_STATIC: return REGEX_DEBUG_STATIC; - case BuildTypeC::DEBUG_DYNAMIC: return REGEX_DEBUG_DYNAMIC; - case BuildTypeC::RELEASE_STATIC: return REGEX_RELEASE_STATIC; - case BuildTypeC::RELEASE_DYNAMIC: return REGEX_RELEASE_DYNAMIC; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - const std::string& BuildType::to_string() const - { - static const std::string NAME_DEBUG_STATIC("Debug,Static"); - static const std::string NAME_DEBUG_DYNAMIC("Debug,Dynamic"); - static const std::string NAME_RELEASE_STATIC("Release,Static"); - static const std::string NAME_RELEASE_DYNAMIC("Release,Dynamic"); - - switch (backing_enum) - { - case BuildTypeC::DEBUG_STATIC: return NAME_DEBUG_STATIC; - case BuildTypeC::DEBUG_DYNAMIC: return NAME_DEBUG_DYNAMIC; - case BuildTypeC::RELEASE_STATIC: return NAME_RELEASE_STATIC; - case BuildTypeC::RELEASE_DYNAMIC: return NAME_RELEASE_DYNAMIC; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } -} diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp deleted file mode 100644 index 0f1a38d19..000000000 --- a/toolsrc/src/SourceParagraph.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "pch.h" - -#include "PackageSpec.h" -#include "SourceParagraph.h" -#include "Triplet.h" -#include "vcpkg_Checks.h" -#include "vcpkg_Maps.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include "vcpkg_expected.h" - -namespace vcpkg -{ - using namespace vcpkg::Parse; - - namespace Fields - { - static const std::string BUILD_DEPENDS = "Build-Depends"; - static const std::string DEFAULTFEATURES = "Default-Features"; - static const std::string DESCRIPTION = "Description"; - static const std::string FEATURE = "Feature"; - static const std::string MAINTAINER = "Maintainer"; - static const std::string SOURCE = "Source"; - static const std::string SUPPORTS = "Supports"; - static const std::string VERSION = "Version"; - } - - static Span get_list_of_valid_fields() - { - static const std::string valid_fields[] = { - Fields::SOURCE, - Fields::VERSION, - Fields::DESCRIPTION, - Fields::MAINTAINER, - Fields::BUILD_DEPENDS, - }; - - return valid_fields; - } - - void print_error_message(Span> error_info_list) - { - Checks::check_exit(VCPKG_LINE_INFO, error_info_list.size() > 0); - - for (auto&& error_info : error_info_list) - { - Checks::check_exit(VCPKG_LINE_INFO, error_info != nullptr); - if (error_info->error) - { - System::println( - System::Color::error, "Error: while loading %s: %s", error_info->name, error_info->error.message()); - } - } - - bool have_remaining_fields = false; - for (auto&& error_info : error_info_list) - { - if (!error_info->extra_fields.empty()) - { - System::println(System::Color::error, - "Error: There are invalid fields in the control file of %s", - error_info->name); - System::println("The following fields were not expected:\n\n %s\n", - Strings::join("\n ", error_info->extra_fields)); - have_remaining_fields = true; - } - } - - if (have_remaining_fields) - { - System::println("This is the list of valid fields (case-sensitive): \n\n %s\n", - Strings::join("\n ", get_list_of_valid_fields())); - System::println("Different source may be available for vcpkg. Use .\\bootstrap-vcpkg.bat to update.\n"); - } - - for (auto&& error_info : error_info_list) - { - if (!error_info->missing_fields.empty()) - { - System::println(System::Color::error, - "Error: There are missing fields in the control file of %s", - error_info->name); - System::println("The following fields were missing:\n\n %s\n", - Strings::join("\n ", error_info->missing_fields)); - } - } - } - - static ParseExpected parse_source_paragraph(RawParagraph&& fields) - { - ParagraphParser parser(std::move(fields)); - - auto spgh = std::make_unique(); - - parser.required_field(Fields::SOURCE, spgh->name); - parser.required_field(Fields::VERSION, spgh->version); - - spgh->description = parser.optional_field(Fields::DESCRIPTION); - spgh->maintainer = parser.optional_field(Fields::MAINTAINER); - spgh->depends = expand_qualified_dependencies(parse_comma_list(parser.optional_field(Fields::BUILD_DEPENDS))); - spgh->supports = parse_comma_list(parser.optional_field(Fields::SUPPORTS)); - spgh->default_features = parse_comma_list(parser.optional_field(Fields::DEFAULTFEATURES)); - - auto err = parser.error_info(spgh->name); - if (err) - return std::move(err); - else - return std::move(spgh); - } - - static ParseExpected parse_feature_paragraph(RawParagraph&& fields) - { - ParagraphParser parser(std::move(fields)); - - auto fpgh = std::make_unique(); - - parser.required_field(Fields::FEATURE, fpgh->name); - parser.required_field(Fields::DESCRIPTION, fpgh->description); - - fpgh->depends = expand_qualified_dependencies(parse_comma_list(parser.optional_field(Fields::BUILD_DEPENDS))); - - auto err = parser.error_info(fpgh->name); - if (err) - return std::move(err); - else - return std::move(fpgh); - } - - ParseExpected SourceControlFile::parse_control_file( - std::vector>&& control_paragraphs) - { - if (control_paragraphs.size() == 0) - { - return std::make_unique(); - } - - auto control_file = std::make_unique(); - - auto maybe_source = parse_source_paragraph(std::move(control_paragraphs.front())); - if (const auto source = maybe_source.get()) - control_file->core_paragraph = std::move(*source); - else - return std::move(maybe_source).error(); - - control_paragraphs.erase(control_paragraphs.begin()); - - for (auto&& feature_pgh : control_paragraphs) - { - auto maybe_feature = parse_feature_paragraph(std::move(feature_pgh)); - if (const auto feature = maybe_feature.get()) - control_file->feature_paragraphs.emplace_back(std::move(*feature)); - else - return std::move(maybe_feature).error(); - } - - return std::move(control_file); - } - - Dependency Dependency::parse_dependency(std::string name, std::string qualifier) - { - Dependency dep; - dep.qualifier = qualifier; - if (auto maybe_features = Features::from_string(name)) - dep.depend = *maybe_features.get(); - else - Checks::exit_with_message( - VCPKG_LINE_INFO, "error while parsing dependency: %s: %s", to_string(maybe_features.error()), name); - return dep; - } - - std::string Dependency::name() const - { - if (this->depend.features.empty()) return this->depend.name; - - const std::string features = Strings::join(",", this->depend.features); - return Strings::format("%s[%s]", this->depend.name, features); - } - - std::vector vcpkg::expand_qualified_dependencies(const std::vector& depends) - { - return Util::fmap(depends, [&](const std::string& depend_string) -> Dependency { - auto pos = depend_string.find(' '); - if (pos == std::string::npos) return Dependency::parse_dependency(depend_string, Strings::EMPTY); - // expect of the form "\w+ \[\w+\]" - Dependency dep; - - dep.depend.name = depend_string.substr(0, pos); - if (depend_string.c_str()[pos + 1] != '(' || depend_string[depend_string.size() - 1] != ')') - { - // Error, but for now just slurp the entire string. - return Dependency::parse_dependency(depend_string, Strings::EMPTY); - } - dep.qualifier = depend_string.substr(pos + 2, depend_string.size() - pos - 3); - return dep; - }); - } - - std::vector filter_dependencies(const std::vector& deps, const Triplet& t) - { - std::vector ret; - for (auto&& dep : deps) - { - if (dep.qualifier.empty() || t.canonical_name().find(dep.qualifier) != std::string::npos) - { - ret.emplace_back(dep.name()); - } - } - return ret; - } - - std::vector filter_dependencies_to_specs(const std::vector& deps, const Triplet& t) - { - return FeatureSpec::from_strings_and_triplet(filter_dependencies(deps, t), t); - } - - std::string to_string(const Dependency& dep) { return dep.name(); } - - ExpectedT> Supports::parse(const std::vector& strs) - { - Supports ret; - std::vector unrecognized; - - for (auto&& str : strs) - { - if (str == "x64") - ret.architectures.push_back(Architecture::X64); - else if (str == "x86") - ret.architectures.push_back(Architecture::X86); - else if (str == "arm") - ret.architectures.push_back(Architecture::ARM); - else if (str == "windows") - ret.platforms.push_back(Platform::WINDOWS); - else if (str == "uwp") - ret.platforms.push_back(Platform::UWP); - else if (str == "v140") - ret.toolsets.push_back(ToolsetVersion::V140); - else if (str == "v141") - ret.toolsets.push_back(ToolsetVersion::V141); - else if (str == "crt-static") - ret.crt_linkages.push_back(Linkage::STATIC); - else if (str == "crt-dynamic") - ret.crt_linkages.push_back(Linkage::DYNAMIC); - else - unrecognized.push_back(str); - } - - if (unrecognized.empty()) - return std::move(ret); - else - return std::move(unrecognized); - } - - bool Supports::is_supported(Architecture arch, Platform plat, Linkage crt, ToolsetVersion tools) - { - const auto is_in_or_empty = [](auto v, auto&& c) -> bool { return c.empty() || c.end() != Util::find(c, v); }; - if (!is_in_or_empty(arch, architectures)) return false; - if (!is_in_or_empty(plat, platforms)) return false; - if (!is_in_or_empty(crt, crt_linkages)) return false; - if (!is_in_or_empty(tools, toolsets)) return false; - return true; - } -} diff --git a/toolsrc/src/StatusParagraph.cpp b/toolsrc/src/StatusParagraph.cpp deleted file mode 100644 index f8ae293c2..000000000 --- a/toolsrc/src/StatusParagraph.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "pch.h" - -#include "StatusParagraph.h" - -using namespace vcpkg::Parse; - -namespace vcpkg -{ - namespace BinaryParagraphRequiredField - { - static const std::string STATUS = "Status"; - } - - StatusParagraph::StatusParagraph() : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) {} - - void serialize(const StatusParagraph& pgh, std::string& out_str) - { - serialize(pgh.package, out_str); - out_str.append("Status: ") - .append(to_string(pgh.want)) - .append(" ok ") - .append(to_string(pgh.state)) - .push_back('\n'); - } - - StatusParagraph::StatusParagraph(std::unordered_map&& fields) - { - auto status_it = fields.find(BinaryParagraphRequiredField::STATUS); - Checks::check_exit(VCPKG_LINE_INFO, status_it != fields.end(), "Expected 'Status' field in status paragraph"); - std::string status_field = std::move(status_it->second); - fields.erase(status_it); - - this->package = BinaryParagraph(std::move(fields)); - - auto b = status_field.begin(); - const auto mark = b; - const auto e = status_field.end(); - - // Todo: improve error handling - while (b != e && *b != ' ') - ++b; - - want = [](const std::string& text) { - if (text == "unknown") return Want::UNKNOWN; - if (text == "install") return Want::INSTALL; - if (text == "hold") return Want::HOLD; - if (text == "deinstall") return Want::DEINSTALL; - if (text == "purge") return Want::PURGE; - return Want::ERROR_STATE; - }(std::string(mark, b)); - - if (std::distance(b, e) < 4) return; - b += 4; - - state = [](const std::string& text) { - if (text == "not-installed") return InstallState::NOT_INSTALLED; - if (text == "installed") return InstallState::INSTALLED; - if (text == "half-installed") return InstallState::HALF_INSTALLED; - return InstallState::ERROR_STATE; - }(std::string(b, e)); - } - - std::string to_string(InstallState f) - { - switch (f) - { - case InstallState::HALF_INSTALLED: return "half-installed"; - case InstallState::INSTALLED: return "installed"; - case InstallState::NOT_INSTALLED: return "not-installed"; - default: return "error"; - } - } - - std::string to_string(Want f) - { - switch (f) - { - case Want::DEINSTALL: return "deinstall"; - case Want::HOLD: return "hold"; - case Want::INSTALL: return "install"; - case Want::PURGE: return "purge"; - case Want::UNKNOWN: return "unknown"; - default: return "error"; - } - } -} diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp deleted file mode 100644 index aaba95eb9..000000000 --- a/toolsrc/src/StatusParagraphs.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "pch.h" - -#include "StatusParagraphs.h" -#include "vcpkg_Checks.h" -#include - -namespace vcpkg -{ - StatusParagraphs::StatusParagraphs() = default; - - StatusParagraphs::StatusParagraphs(std::vector>&& ps) - : paragraphs(std::move(ps)){}; - - StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) const - { - return std::find_if(begin(), end(), [&](const std::unique_ptr& pgh) { - const PackageSpec& spec = pgh->package.spec; - return spec.name() == name && spec.triplet() == triplet; - }); - } - - StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) - { - return std::find_if(begin(), end(), [&](const std::unique_ptr& pgh) { - const PackageSpec& spec = pgh->package.spec; - return spec.name() == name && spec.triplet() == triplet; - }); - } - - std::vector*> StatusParagraphs::find_all(const std::string& name, - const Triplet& triplet) - { - std::vector*> spghs; - for (auto&& p : *this) - { - if (p->package.spec.name() == name && p->package.spec.triplet() == triplet) - { - spghs.emplace_back(&p); - } - } - return spghs; - } - - StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, - const Triplet& triplet, - const std::string& feature) - { - return std::find_if(begin(), end(), [&](const std::unique_ptr& pgh) { - const PackageSpec& spec = pgh->package.spec; - return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature; - }); - } - - StatusParagraphs::const_iterator StatusParagraphs::find_installed(const std::string& name, - const Triplet& triplet) const - { - const const_iterator it = find(name, triplet); - if (it != end() && (*it)->want == Want::INSTALL && (*it)->state == InstallState::INSTALLED) - { - return it; - } - - return end(); - } - - StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr pgh) - { - Checks::check_exit(VCPKG_LINE_INFO, pgh != nullptr, "Inserted null paragraph"); - const PackageSpec& spec = pgh->package.spec; - const auto ptr = find(spec.name(), spec.triplet(), pgh->package.feature); - if (ptr == end()) - { - paragraphs.push_back(std::move(pgh)); - return paragraphs.rbegin(); - } - - // consume data from provided pgh. - **ptr = std::move(*pgh); - return ptr; - } - - void serialize(const StatusParagraphs& pghs, std::string& out_str) - { - for (auto& pgh : pghs.paragraphs) - { - serialize(*pgh, out_str); - out_str.push_back('\n'); - } - } -} diff --git a/toolsrc/src/VcpkgCmdArguments.cpp b/toolsrc/src/VcpkgCmdArguments.cpp deleted file mode 100644 index cb261930e..000000000 --- a/toolsrc/src/VcpkgCmdArguments.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "pch.h" - -#include "VcpkgCmdArguments.h" -#include "metrics.h" -#include "vcpkg_Commands.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_System.h" - -namespace vcpkg -{ - static void parse_value(const std::string* arg_begin, - const std::string* arg_end, - const std::string& option_name, - std::unique_ptr& option_field) - { - if (arg_begin == arg_end) - { - System::println(System::Color::error, "Error: expected value after %s", option_name); - Metrics::g_metrics.lock()->track_property("error", "error option name"); - Commands::Help::print_usage(); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - if (option_field != nullptr) - { - System::println(System::Color::error, "Error: %s specified multiple times", option_name); - Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times"); - Commands::Help::print_usage(); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - option_field = std::make_unique(*arg_begin); - } - - static void parse_switch(bool new_setting, const std::string& option_name, Optional& option_field) - { - if (option_field && option_field != new_setting) - { - System::println(System::Color::error, "Error: conflicting values specified for --%s", option_name); - Metrics::g_metrics.lock()->track_property("error", "error conflicting switches"); - Commands::Help::print_usage(); - Checks::exit_fail(VCPKG_LINE_INFO); - } - option_field = new_setting; - } - - VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc, const wchar_t* const* const argv) - { - std::vector v; - for (int i = 1; i < argc; ++i) - { - v.push_back(Strings::to_utf8(argv[i])); - } - - return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size()); - } - - VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin, - const std::string* arg_end) - { - VcpkgCmdArguments args; - - for (; arg_begin != arg_end; ++arg_begin) - { - std::string arg = *arg_begin; - - if (arg.empty()) - { - continue; - } - - if (arg[0] == '-' && arg[1] != '-') - { - Metrics::g_metrics.lock()->track_property("error", "error short options are not supported"); - Checks::exit_with_message(VCPKG_LINE_INFO, "Error: short options are not supported: %s", arg); - } - - if (arg[0] == '-' && arg[1] == '-') - { - // make argument case insensitive - auto& f = std::use_facet>(std::locale()); - f.tolower(&arg[0], &arg[0] + arg.size()); - // command switch - if (arg == "--vcpkg-root") - { - ++arg_begin; - parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir); - continue; - } - if (arg == "--triplet") - { - ++arg_begin; - parse_value(arg_begin, arg_end, "--triplet", args.triplet); - continue; - } - if (arg == "--debug") - { - parse_switch(true, "debug", args.debug); - continue; - } - if (arg == "--sendmetrics") - { - parse_switch(true, "sendmetrics", args.sendmetrics); - continue; - } - if (arg == "--printmetrics") - { - parse_switch(true, "printmetrics", args.printmetrics); - continue; - } - if (arg == "--no-sendmetrics") - { - parse_switch(false, "sendmetrics", args.sendmetrics); - continue; - } - if (arg == "--no-printmetrics") - { - parse_switch(false, "printmetrics", args.printmetrics); - continue; - } - if (arg == "--featurepackages") - { - GlobalState::feature_packages = true; - continue; - } - - const auto eq_pos = arg.find('='); - if (eq_pos != std::string::npos) - { - args.optional_command_arguments.emplace(arg.substr(0, eq_pos), arg.substr(eq_pos + 1)); - } - else - { - args.optional_command_arguments.emplace(arg, nullopt); - } - continue; - } - - if (args.command.empty()) - { - args.command = arg; - } - else - { - args.command_arguments.push_back(arg); - } - } - - return args; - } - - ParsedArguments VcpkgCmdArguments::check_and_get_optional_command_arguments( - const std::vector& valid_switches, const std::vector& valid_settings) const - { - bool failed = false; - ParsedArguments output; - - auto options_copy = this->optional_command_arguments; - for (const std::string& option : valid_switches) - { - const auto it = options_copy.find(option); - if (it != options_copy.end()) - { - if (it->second.has_value()) - { - // Having a string value indicates it was passed like '--a=xyz' - System::println(System::Color::error, "The option '%s' does not accept an argument.", option); - failed = true; - } - else - { - output.switches.insert(option); - options_copy.erase(it); - } - } - } - - for (const std::string& option : valid_settings) - { - const auto it = options_copy.find(option); - if (it != options_copy.end()) - { - if (!it->second.has_value()) - { - // Not having a string value indicates it was passed like '--a' - System::println(System::Color::error, "The option '%s' must be passed an argument.", option); - failed = true; - } - else - { - output.settings.emplace(option, it->second.value_or_exit(VCPKG_LINE_INFO)); - options_copy.erase(it); - } - } - } - - if (!options_copy.empty()) - { - System::println(System::Color::error, "Unknown option(s) for command '%s':", this->command); - for (auto&& option : options_copy) - { - System::println(" %s", option.first); - } - System::println("\nValid options are:", this->command); - for (auto&& option : valid_switches) - { - System::println(" %s", option); - } - for (auto&& option : valid_settings) - { - System::println(" %s=...", option); - } - System::println(" --triplet "); - System::println(" --vcpkg-root "); - - Checks::exit_fail(VCPKG_LINE_INFO); - } - if (failed) Checks::exit_fail(VCPKG_LINE_INFO); - - return output; - } - - void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count) const - { - return check_max_arg_count(expected_arg_count, Strings::EMPTY); - } - - void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count) const - { - return check_min_arg_count(expected_arg_count, Strings::EMPTY); - } - - void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count) const - { - return check_exact_arg_count(expected_arg_count, Strings::EMPTY); - } - - void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count, const std::string& example_text) const - { - const size_t actual_arg_count = command_arguments.size(); - if (actual_arg_count > expected_arg_count) - { - System::println(System::Color::error, - "Error: `%s` requires at most %u arguments, but %u were provided", - this->command, - expected_arg_count, - actual_arg_count); - System::print(example_text); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count, const std::string& example_text) const - { - const size_t actual_arg_count = command_arguments.size(); - if (actual_arg_count < expected_arg_count) - { - System::println(System::Color::error, - "Error: `%s` requires at least %u arguments, but %u were provided", - this->command, - expected_arg_count, - actual_arg_count); - System::print(example_text); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count, - const std::string& example_text) const - { - const size_t actual_arg_count = command_arguments.size(); - if (actual_arg_count != expected_arg_count) - { - System::println(System::Color::error, - "Error: `%s` requires %u arguments, but %u were provided", - this->command, - expected_arg_count, - actual_arg_count); - System::print(example_text); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } -} diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp deleted file mode 100644 index d14002e47..000000000 --- a/toolsrc/src/VcpkgPaths.cpp +++ /dev/null @@ -1,519 +0,0 @@ -#include "pch.h" - -#include "PackageSpec.h" -#include "VcpkgPaths.h" -#include "metrics.h" -#include "vcpkg_Files.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include "vcpkg_expected.h" - -namespace vcpkg -{ - static constexpr CWStringView V_120 = L"v120"; - static constexpr CWStringView V_140 = L"v140"; - static constexpr CWStringView V_141 = L"v141"; - - static bool exists_and_has_equal_or_greater_version(const std::wstring& version_cmd, - const std::array& expected_version) - { - static const std::regex RE(R"###((\d+)\.(\d+)\.(\d+))###"); - - const auto rc = System::cmd_execute_and_capture_output(Strings::wformat(LR"(%s)", version_cmd)); - if (rc.exit_code != 0) - { - return false; - } - - std::match_results match; - const auto found = std::regex_search(rc.output, match, RE); - if (!found) - { - return false; - } - - const int d1 = atoi(match[1].str().c_str()); - const int d2 = atoi(match[2].str().c_str()); - const int d3 = atoi(match[3].str().c_str()); - if (d1 > expected_version[0] || (d1 == expected_version[0] && d2 > expected_version[1]) || - (d1 == expected_version[0] && d2 == expected_version[1] && d3 >= expected_version[2])) - { - // satisfactory version found - return true; - } - - return false; - } - - static Optional find_if_has_equal_or_greater_version(const std::vector& candidate_paths, - const std::wstring& version_check_arguments, - const std::array& expected_version) - { - auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { - const std::wstring cmd = Strings::wformat(LR"("%s" %s)", p.native(), version_check_arguments); - return exists_and_has_equal_or_greater_version(cmd, expected_version); - }); - - if (it != candidate_paths.cend()) - { - return std::move(*it); - } - - return nullopt; - } - - static fs::path fetch_dependency(const fs::path& scripts_folder, - const std::wstring& tool_name, - const fs::path& expected_downloaded_path, - const std::array& version) - { - const std::string tool_name_utf8 = Strings::to_utf8(tool_name); - const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]); - System::println("A suitable version of %s was not found (required v%s). Downloading portable %s v%s...", - tool_name_utf8, - version_as_string, - tool_name_utf8, - version_as_string); - const fs::path script = scripts_folder / "fetchDependency.ps1"; - const auto install_cmd = - System::create_powershell_script_cmd(script, Strings::wformat(L"-Dependency %s", tool_name)); - const System::ExitCodeAndOutput rc = System::cmd_execute_and_capture_output(install_cmd); - if (rc.exit_code) - { - System::println(System::Color::error, - "Launching powershell failed or was denied when trying to fetch %s version %s.\n" - "(No sufficient installed version was found)", - tool_name_utf8, - version_as_string); - { - auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->track_property("error", "powershell install failed"); - locked_metrics->track_property("dependency", tool_name); - } - Checks::exit_with_code(VCPKG_LINE_INFO, rc.exit_code); - } - - const fs::path actual_downloaded_path = Strings::trimmed(rc.output); - std::error_code ec; - const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - eq && !ec, - "Expected dependency downloaded path to be %s, but was %s", - expected_downloaded_path.u8string(), - actual_downloaded_path.u8string()); - return actual_downloaded_path; - } - - static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path& scripts_folder) - { - static constexpr std::array EXPECTED_VERSION = {3, 9, 3}; - static const std::wstring VERSION_CHECK_ARGUMENTS = L"--version"; - - const fs::path downloaded_copy = downloads_folder / "cmake-3.9.3-win32-x86" / "bin" / "cmake.exe"; - const std::vector from_path = Files::find_from_PATH(L"cmake"); - - std::vector candidate_paths; - candidate_paths.push_back(downloaded_copy); - candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); - candidate_paths.push_back(System::get_program_files_platform_bitness() / "CMake" / "bin" / "cmake.exe"); - candidate_paths.push_back(System::get_program_files_32_bit() / "CMake" / "bin"); - - const Optional path = - find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); - if (const auto p = path.get()) - { - return *p; - } - - return fetch_dependency(scripts_folder, L"cmake", downloaded_copy, EXPECTED_VERSION); - } - - fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path& scripts_folder) - { - static constexpr std::array EXPECTED_VERSION = {4, 3, 0}; - static const std::wstring VERSION_CHECK_ARGUMENTS = Strings::WEMPTY; - - const fs::path downloaded_copy = downloads_folder / "nuget-4.3.0" / "nuget.exe"; - const std::vector from_path = Files::find_from_PATH(L"nuget"); - - std::vector candidate_paths; - candidate_paths.push_back(downloaded_copy); - candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); - - auto path = find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); - if (const auto p = path.get()) - { - return *p; - } - - return fetch_dependency(scripts_folder, L"nuget", downloaded_copy, EXPECTED_VERSION); - } - - fs::path get_git_path(const fs::path& downloads_folder, const fs::path& scripts_folder) - { - static constexpr std::array EXPECTED_VERSION = {2, 14, 1}; - static const std::wstring VERSION_CHECK_ARGUMENTS = L"--version"; - - const fs::path downloaded_copy = downloads_folder / "MinGit-2.14.1-32-bit" / "cmd" / "git.exe"; - const std::vector from_path = Files::find_from_PATH(L"git"); - - std::vector candidate_paths; - candidate_paths.push_back(downloaded_copy); - candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); - candidate_paths.push_back(System::get_program_files_platform_bitness() / "git" / "cmd" / "git.exe"); - candidate_paths.push_back(System::get_program_files_32_bit() / "git" / "cmd" / "git.exe"); - - const Optional path = - find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); - if (const auto p = path.get()) - { - return *p; - } - - return fetch_dependency(scripts_folder, L"git", downloaded_copy, EXPECTED_VERSION); - } - - static fs::path get_ifw_installerbase_path(const fs::path& downloads_folder, const fs::path& scripts_folder) - { - static constexpr std::array EXPECTED_VERSION = {3, 1, 81}; - static const std::wstring VERSION_CHECK_ARGUMENTS = L"--framework-version"; - - const fs::path downloaded_copy = - downloads_folder / "QtInstallerFramework-win-x86" / "bin" / "installerbase.exe"; - - std::vector candidate_paths; - candidate_paths.push_back(downloaded_copy); - // TODO: Uncomment later - // const std::vector from_path = Files::find_from_PATH(L"installerbase"); - // candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); - // candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / - // "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe"); - // candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / - // "QtIFW-3.1.0" / "bin" / "installerbase.exe"); - - const Optional path = - find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); - if (const auto p = path.get()) - { - return *p; - } - - return fetch_dependency(scripts_folder, L"installerbase", downloaded_copy, EXPECTED_VERSION); - } - - Expected VcpkgPaths::create(const fs::path& vcpkg_root_dir) - { - std::error_code ec; - const fs::path canonical_vcpkg_root_dir = fs::stdfs::canonical(vcpkg_root_dir, ec); - if (ec) - { - return ec; - } - - VcpkgPaths paths; - paths.root = canonical_vcpkg_root_dir; - - if (paths.root.empty()) - { - Metrics::g_metrics.lock()->track_property("error", "Invalid vcpkg root directory"); - Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid vcpkg root directory: %s", paths.root.string()); - } - - paths.packages = paths.root / "packages"; - paths.buildtrees = paths.root / "buildtrees"; - paths.downloads = paths.root / "downloads"; - paths.ports = paths.root / "ports"; - paths.installed = paths.root / "installed"; - paths.triplets = paths.root / "triplets"; - paths.scripts = paths.root / "scripts"; - - paths.buildsystems = paths.scripts / "buildsystems"; - paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets"; - - paths.vcpkg_dir = paths.installed / "vcpkg"; - paths.vcpkg_dir_status_file = paths.vcpkg_dir / "status"; - paths.vcpkg_dir_info = paths.vcpkg_dir / "info"; - paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates"; - - paths.ports_cmake = paths.scripts / "ports.cmake"; - - return paths; - } - - fs::path VcpkgPaths::package_dir(const PackageSpec& spec) const { return this->packages / spec.dir(); } - - fs::path VcpkgPaths::port_dir(const PackageSpec& spec) const { return this->ports / spec.name(); } - fs::path VcpkgPaths::port_dir(const std::string& name) const { return this->ports / name; } - - fs::path VcpkgPaths::build_info_file_path(const PackageSpec& spec) const - { - return this->package_dir(spec) / "BUILD_INFO"; - } - - fs::path VcpkgPaths::listfile_path(const BinaryParagraph& pgh) const - { - return this->vcpkg_dir_info / (pgh.fullstem() + ".list"); - } - - bool VcpkgPaths::is_valid_triplet(const Triplet& t) const - { - for (auto&& path : get_filesystem().get_files_non_recursive(this->triplets)) - { - const std::string triplet_file_name = path.stem().generic_u8string(); - if (t.canonical_name() == triplet_file_name) // TODO: fuzzy compare - { - // t.value = triplet_file_name; // NOTE: uncomment when implementing fuzzy compare - return true; - } - } - return false; - } - - const fs::path& VcpkgPaths::get_cmake_exe() const - { - return this->cmake_exe.get_lazy([this]() { return get_cmake_path(this->downloads, this->scripts); }); - } - - const fs::path& VcpkgPaths::get_git_exe() const - { - return this->git_exe.get_lazy([this]() { return get_git_path(this->downloads, this->scripts); }); - } - - const fs::path& VcpkgPaths::get_nuget_exe() const - { - return this->nuget_exe.get_lazy([this]() { return get_nuget_path(this->downloads, this->scripts); }); - } - - const fs::path& VcpkgPaths::get_ifw_installerbase_exe() const - { - return this->ifw_installerbase_exe.get_lazy( - [this]() { return get_ifw_installerbase_path(this->downloads, this->scripts); }); - } - - const fs::path& VcpkgPaths::get_ifw_binarycreator_exe() const - { - return this->ifw_binarycreator_exe.get_lazy( - [this]() { return get_ifw_installerbase_exe().parent_path() / "binarycreator.exe"; }); - } - - const fs::path& VcpkgPaths::get_ifw_repogen_exe() const - { - return this->ifw_repogen_exe.get_lazy( - [this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; }); - } - - struct VisualStudioInstance - { - fs::path root_path; - std::string version; - std::string release_type; - std::string preference_weight; // Mostly unused, just for verification that order is as intended - - std::string major_version() const { return version.substr(0, 2); } - }; - - static std::vector get_visual_studio_instances(const VcpkgPaths& paths) - { - const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; - const std::wstring cmd = System::create_powershell_script_cmd(script); - const System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd); - Checks::check_exit( - VCPKG_LINE_INFO, ec_data.exit_code == 0, "Could not run script to detect Visual Studio instances"); - - const std::vector instances_as_strings = Strings::split(ec_data.output, "\n"); - std::vector output; - for (const std::string& instance_as_string : instances_as_strings) - { - const std::vector split = Strings::split(instance_as_string, "::"); - output.push_back({split.at(3), split.at(2), split.at(1), split.at(0)}); - } - - return output; - } - - static std::vector find_toolset_instances(const VcpkgPaths& paths) - { - using CPU = System::CPUArchitecture; - - const auto& fs = paths.get_filesystem(); - - // Note: this will contain a mix of vcvarsall.bat locations and dumpbin.exe locations. - std::vector paths_examined; - - std::vector found_toolsets; - - const std::vector vs_instances = get_visual_studio_instances(paths); - const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) { - return vs_instance.major_version() == "14"; - }) != vs_instances.cend(); - - for (const VisualStudioInstance& vs_instance : vs_instances) - { - const std::string major_version = vs_instance.major_version(); - if (major_version == "15") - { - const fs::path vc_dir = vs_instance.root_path / "VC"; - - // Skip any instances that do not have vcvarsall. - const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build"; - const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat"; - paths_examined.push_back(vcvarsall_bat); - if (!fs.exists(vcvarsall_bat)) continue; - - // Get all supported architectures - std::vector supported_architectures; - if (fs.exists(vcvarsall_dir / "vcvars32.bat")) - supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); - if (fs.exists(vcvarsall_dir / "vcvars64.bat")) - supported_architectures.push_back({L"amd64", CPU::X64, CPU::X64}); - if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat")) - supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); - if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat")) - supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); - if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat")) - supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); - if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat")) - supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); - - // Locate the "best" MSVC toolchain version - const fs::path msvc_path = vc_dir / "Tools" / "MSVC"; - std::vector msvc_subdirectories = fs.get_files_non_recursive(msvc_path); - Util::unstable_keep_if(msvc_subdirectories, - [&fs](const fs::path& path) { return fs.is_directory(path); }); - - // Sort them so that latest comes first - std::sort( - msvc_subdirectories.begin(), - msvc_subdirectories.end(), - [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); }); - - for (const fs::path& subdir : msvc_subdirectories) - { - const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe"; - paths_examined.push_back(dumpbin_path); - if (fs.exists(dumpbin_path)) - { - found_toolsets.push_back(Toolset{ - vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}); - - if (v140_is_available) - { - found_toolsets.push_back(Toolset{vs_instance.root_path, - dumpbin_path, - vcvarsall_bat, - {L"-vcvars_ver=14.0"}, - V_140, - supported_architectures}); - } - - break; - } - } - - continue; - } - - if (major_version == "14") - { - const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat"; - - paths_examined.push_back(vcvarsall_bat); - if (fs.exists(vcvarsall_bat)) - { - const fs::path vs2015_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe"; - paths_examined.push_back(vs2015_dumpbin_exe); - - const fs::path vs2015_bin_dir = vcvarsall_bat.parent_path() / "bin"; - std::vector supported_architectures; - if (fs.exists(vs2015_bin_dir / "vcvars32.bat")) - supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); - if (fs.exists(vs2015_bin_dir / "amd64\\vcvars64.bat")) - supported_architectures.push_back({L"x64", CPU::X64, CPU::X64}); - if (fs.exists(vs2015_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat")) - supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); - if (fs.exists(vs2015_bin_dir / "x86_arm\\vcvarsx86_arm.bat")) - supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); - if (fs.exists(vs2015_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat")) - supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); - if (fs.exists(vs2015_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat")) - supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); - - if (fs.exists(vs2015_dumpbin_exe)) - { - found_toolsets.push_back({vs_instance.root_path, - vs2015_dumpbin_exe, - vcvarsall_bat, - {}, - V_140, - supported_architectures}); - } - } - } - } - - if (found_toolsets.empty()) - { - System::println(System::Color::error, "Could not locate a complete toolset."); - System::println("The following paths were examined:"); - for (const fs::path& path : paths_examined) - { - System::println(" %s", path.u8string()); - } - Checks::exit_fail(VCPKG_LINE_INFO); - } - - return found_toolsets; - } - - const Toolset& VcpkgPaths::get_toolset(const Optional& toolset_version, - const Optional& visual_studio_path) const - { - // Invariant: toolsets are non-empty and sorted with newest at back() - const std::vector& vs_toolsets = - this->toolsets.get_lazy([this]() { return find_toolset_instances(*this); }); - - std::vector candidates = Util::element_pointers(vs_toolsets); - const auto tsv = toolset_version.get(); - const auto vsp = visual_studio_path.get(); - - if (tsv && vsp) - { - const std::wstring w_toolset_version = Strings::to_utf16(*tsv); - const fs::path vs_root_path = *vsp; - Util::stable_keep_if(candidates, [&](const Toolset* t) { - return w_toolset_version == t->version && vs_root_path == t->visual_studio_root_path; - }); - Checks::check_exit(VCPKG_LINE_INFO, - !candidates.empty(), - "Could not find Visual Studio instace at %s with %s toolset.", - vs_root_path.generic_string(), - *tsv); - - Checks::check_exit(VCPKG_LINE_INFO, candidates.size() == 1); - return *candidates.back(); - } - - if (tsv) - { - const std::wstring w_toolset_version = Strings::to_utf16(*tsv); - Util::stable_keep_if(candidates, [&](const Toolset* t) { return w_toolset_version == t->version; }); - Checks::check_exit( - VCPKG_LINE_INFO, !candidates.empty(), "Could not find Visual Studio instace with %s toolset.", *tsv); - } - - if (vsp) - { - const fs::path vs_root_path = *vsp; - Util::stable_keep_if(candidates, - [&](const Toolset* t) { return vs_root_path == t->visual_studio_root_path; }); - Checks::check_exit(VCPKG_LINE_INFO, - !candidates.empty(), - "Could not find Visual Studio instace at %s.", - vs_root_path.generic_string()); - } - - return *candidates.front(); - } - - Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); } -} diff --git a/toolsrc/src/VersionT.cpp b/toolsrc/src/VersionT.cpp deleted file mode 100644 index 738d2ce88..000000000 --- a/toolsrc/src/VersionT.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "pch.h" - -#include "VersionT.h" -#include "vcpkg_Strings.h" - -namespace vcpkg -{ - VersionT::VersionT() : value("0.0.0") {} - VersionT::VersionT(const std::string& value) : value(value) {} - std::string VersionT::to_string() const { return value; } - bool operator==(const VersionT& left, const VersionT& right) { return left.value == right.value; } - bool operator!=(const VersionT& left, const VersionT& right) { return left.value != right.value; } - std::string to_printf_arg(const VersionT& version) { return version.value; } - - VersionDiff::VersionDiff() : left(), right() {} - VersionDiff::VersionDiff(const VersionT& left, const VersionT& right) : left(left), right(right) {} - - std::string VersionDiff::to_string() const { return Strings::format("%s -> %s", left.value, right.value); } -} diff --git a/toolsrc/src/coff_file_reader.cpp b/toolsrc/src/coff_file_reader.cpp deleted file mode 100644 index bb3a6cefd..000000000 --- a/toolsrc/src/coff_file_reader.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "pch.h" - -#include "coff_file_reader.h" -#include "vcpkg_Checks.h" - -using namespace std; - -namespace vcpkg::CoffFileReader -{ - template - static T reinterpret_bytes(const char* data) - { - return (*reinterpret_cast(&data[0])); - } - - template - static T read_value_from_stream(fstream& fs) - { - T data; - fs.read(reinterpret_cast(&data), sizeof data); - return data; - } - - template - static T peek_value_from_stream(fstream& fs) - { - const fpos_t original_pos = fs.tellg().seekpos(); - T data; - fs.read(reinterpret_cast(&data), sizeof data); - fs.seekg(original_pos); - return data; - } - - static void verify_equal_strings( - const LineInfo& line_info, const char* expected, const char* actual, int size, const char* label) - { - Checks::check_exit(line_info, - memcmp(expected, actual, size) == 0, - "Incorrect string (%s) found. Expected: (%s) but found (%s)", - label, - expected, - actual); - } - - static void read_and_verify_PE_signature(fstream& fs) - { - static const size_t OFFSET_TO_PE_SIGNATURE_OFFSET = 0x3c; - - static const char* PE_SIGNATURE = "PE\0\0"; - static const size_t PE_SIGNATURE_SIZE = 4; - - fs.seekg(OFFSET_TO_PE_SIGNATURE_OFFSET, ios_base::beg); - const int32_t offset_to_PE_signature = read_value_from_stream(fs); - - fs.seekg(offset_to_PE_signature); - char signature[PE_SIGNATURE_SIZE]; - fs.read(signature, PE_SIGNATURE_SIZE); - verify_equal_strings(VCPKG_LINE_INFO, PE_SIGNATURE, signature, PE_SIGNATURE_SIZE, "PE_SIGNATURE"); - fs.seekg(offset_to_PE_signature + PE_SIGNATURE_SIZE, ios_base::beg); - } - - static fpos_t align_to_size(const uint64_t unaligned, const uint64_t alignment_size) - { - fpos_t aligned = unaligned - 1; - aligned /= alignment_size; - aligned += 1; - aligned *= alignment_size; - return aligned; - } - - struct CoffFileHeader - { - static const size_t HEADER_SIZE = 20; - - static CoffFileHeader read(fstream& fs) - { - CoffFileHeader ret; - ret.data.resize(HEADER_SIZE); - fs.read(&ret.data[0], HEADER_SIZE); - return ret; - } - - MachineType machine_type() const - { - static const size_t MACHINE_TYPE_OFFSET = 0; - static const size_t MACHINE_TYPE_SIZE = 2; - - std::string machine_field_as_string = data.substr(MACHINE_TYPE_OFFSET, MACHINE_TYPE_SIZE); - const uint16_t machine = reinterpret_bytes(machine_field_as_string.c_str()); - return to_machine_type(machine); - } - - private: - std::string data; - }; - - struct ArchiveMemberHeader - { - static const size_t HEADER_SIZE = 60; - - static ArchiveMemberHeader read(fstream& fs) - { - static const size_t HEADER_END_OFFSET = 58; - static const char* HEADER_END = "`\n"; - static const size_t HEADER_END_SIZE = 2; - - ArchiveMemberHeader ret; - ret.data.resize(HEADER_SIZE); - fs.read(&ret.data[0], HEADER_SIZE); - - if (ret.data[0] != '\0') // Due to freeglut. github issue #223 - { - const std::string header_end = ret.data.substr(HEADER_END_OFFSET, HEADER_END_SIZE); - verify_equal_strings( - VCPKG_LINE_INFO, HEADER_END, header_end.c_str(), HEADER_END_SIZE, "LIB HEADER_END"); - } - - return ret; - } - - std::string name() const - { - static const size_t HEADER_NAME_OFFSET = 0; - static const size_t HEADER_NAME_SIZE = 16; - return data.substr(HEADER_NAME_OFFSET, HEADER_NAME_SIZE); - } - - uint64_t member_size() const - { - static const size_t ALIGNMENT_SIZE = 2; - - static const size_t HEADER_SIZE_OFFSET = 48; - static const size_t HEADER_SIZE_FIELD_SIZE = 10; - const std::string as_string = data.substr(HEADER_SIZE_OFFSET, HEADER_SIZE_FIELD_SIZE); - // This is in ASCII decimal representation - const uint64_t value = std::strtoull(as_string.c_str(), nullptr, 10); - - const uint64_t aligned = align_to_size(value, ALIGNMENT_SIZE); - return aligned; - } - - std::string data; - }; - - struct OffsetsArray - { - static OffsetsArray read(fstream& fs, const uint32_t offset_count) - { - static const size_t OFFSET_WIDTH = 4; - - std::string raw_offsets; - const size_t raw_offset_size = offset_count * OFFSET_WIDTH; - raw_offsets.resize(raw_offset_size); - fs.read(&raw_offsets[0], raw_offset_size); - - OffsetsArray ret; - for (uint32_t i = 0; i < offset_count; ++i) - { - const std::string value_as_string = raw_offsets.substr(OFFSET_WIDTH * i, OFFSET_WIDTH * (i + 1)); - const uint32_t value = reinterpret_bytes(value_as_string.c_str()); - - // Ignore offsets that point to offset 0. See vcpkg github #223 #288 #292 - if (value != 0) - { - ret.data.push_back(value); - } - } - - // Sort the offsets, because it is possible for them to be unsorted. See vcpkg github #292 - std::sort(ret.data.begin(), ret.data.end()); - return ret; - } - - std::vector data; - }; - - struct ImportHeader - { - static const size_t HEADER_SIZE = 20; - - static ImportHeader read(fstream& fs) - { - static const size_t SIG1_OFFSET = 0; - static const uint16_t SIG1 = static_cast(MachineType::UNKNOWN); - static const size_t SIG1_SIZE = 2; - - static const size_t SIG2_OFFSET = 2; - static const uint16_t SIG2 = 0xFFFF; - static const size_t SIG2_SIZE = 2; - - ImportHeader ret; - ret.data.resize(HEADER_SIZE); - fs.read(&ret.data[0], HEADER_SIZE); - - const std::string sig1_as_string = ret.data.substr(SIG1_OFFSET, SIG1_SIZE); - const uint16_t sig1 = reinterpret_bytes(sig1_as_string.c_str()); - Checks::check_exit(VCPKG_LINE_INFO, sig1 == SIG1, "Sig1 was incorrect. Expected %s but got %s", SIG1, sig1); - - const std::string sig2_as_string = ret.data.substr(SIG2_OFFSET, SIG2_SIZE); - const uint16_t sig2 = reinterpret_bytes(sig2_as_string.c_str()); - Checks::check_exit(VCPKG_LINE_INFO, sig2 == SIG2, "Sig2 was incorrect. Expected %s but got %s", SIG2, sig2); - - return ret; - } - - MachineType machine_type() const - { - static const size_t MACHINE_TYPE_OFFSET = 6; - static const size_t MACHINE_TYPE_SIZE = 2; - - std::string machine_field_as_string = data.substr(MACHINE_TYPE_OFFSET, MACHINE_TYPE_SIZE); - const uint16_t machine = reinterpret_bytes(machine_field_as_string.c_str()); - return to_machine_type(machine); - } - - private: - std::string data; - }; - - static void read_and_verify_archive_file_signature(fstream& fs) - { - static const char* FILE_START = "!\n"; - static const size_t FILE_START_SIZE = 8; - - fs.seekg(fs.beg); - - char file_start[FILE_START_SIZE]; - fs.read(file_start, FILE_START_SIZE); - verify_equal_strings(VCPKG_LINE_INFO, FILE_START, file_start, FILE_START_SIZE, "LIB FILE_START"); - } - - DllInfo read_dll(const fs::path& path) - { - std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate); - Checks::check_exit(VCPKG_LINE_INFO, fs.is_open(), "Could not open file %s for reading", path.generic_string()); - - read_and_verify_PE_signature(fs); - CoffFileHeader header = CoffFileHeader::read(fs); - const MachineType machine = header.machine_type(); - return {machine}; - } - - struct Marker - { - void set_to_offset(const fpos_t position) { this->m_absolute_position = position; } - - void set_to_current_pos(fstream& fs) { this->m_absolute_position = fs.tellg().seekpos(); } - - void seek_to_marker(fstream& fs) const { fs.seekg(this->m_absolute_position, ios_base::beg); } - - void advance_by(const uint64_t offset) { this->m_absolute_position += offset; } - - private: - fpos_t m_absolute_position = 0; - }; - - LibInfo read_lib(const fs::path& path) - { - std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate); - Checks::check_exit(VCPKG_LINE_INFO, fs.is_open(), "Could not open file %s for reading", path.generic_string()); - - read_and_verify_archive_file_signature(fs); - - Marker marker; - marker.set_to_current_pos(fs); - - // First Linker Member - const ArchiveMemberHeader first_linker_member_header = ArchiveMemberHeader::read(fs); - Checks::check_exit(VCPKG_LINE_INFO, - first_linker_member_header.name().substr(0, 2) == "/ ", - "Could not find proper first linker member"); - marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + first_linker_member_header.member_size()); - marker.seek_to_marker(fs); - - const ArchiveMemberHeader second_linker_member_header = ArchiveMemberHeader::read(fs); - Checks::check_exit(VCPKG_LINE_INFO, - second_linker_member_header.name().substr(0, 2) == "/ ", - "Could not find proper second linker member"); - // The first 4 bytes contains the number of archive members - const uint32_t archive_member_count = read_value_from_stream(fs); - const OffsetsArray offsets = OffsetsArray::read(fs, archive_member_count); - marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + second_linker_member_header.member_size()); - marker.seek_to_marker(fs); - - const bool hasLongnameMemberHeader = peek_value_from_stream(fs) == 0x2F2F; - if (hasLongnameMemberHeader) - { - const ArchiveMemberHeader longnames_member_header = ArchiveMemberHeader::read(fs); - marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + longnames_member_header.member_size()); - marker.seek_to_marker(fs); - } - - std::set machine_types; - // Next we have the obj and pseudo-object files - for (const uint32_t offset : offsets.data) - { - marker.set_to_offset(offset + ArchiveMemberHeader::HEADER_SIZE); // Skip the header, no need to read it. - marker.seek_to_marker(fs); - const uint16_t first_two_bytes = peek_value_from_stream(fs); - const bool isImportHeader = to_machine_type(first_two_bytes) == MachineType::UNKNOWN; - const MachineType machine = - isImportHeader ? ImportHeader::read(fs).machine_type() : CoffFileHeader::read(fs).machine_type(); - machine_types.insert(machine); - } - - return {std::vector(machine_types.cbegin(), machine_types.cend())}; - } -} diff --git a/toolsrc/src/commands_autocomplete.cpp b/toolsrc/src/commands_autocomplete.cpp deleted file mode 100644 index 3963f904b..000000000 --- a/toolsrc/src/commands_autocomplete.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "SortedVector.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Maps.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Autocomplete -{ - std::vector autocomplete_install( - const std::vector>& source_paragraphs, const std::string& start_with) - { - std::vector results; - const auto& istartswith = Strings::case_insensitive_ascii_starts_with; - - for (const auto& source_control_file : source_paragraphs) - { - auto&& sp = *source_control_file->core_paragraph; - - if (istartswith(sp.name, start_with)) - { - results.push_back(sp.name); - } - } - return results; - } - - std::vector autocomplete_remove(std::vector installed_packages, - const std::string& start_with) - { - std::vector results; - const auto& istartswith = Strings::case_insensitive_ascii_starts_with; - - for (const auto& installed_package : installed_packages) - { - auto sp = installed_package->package.displayname(); - - if (istartswith(sp, start_with)) - { - results.push_back(sp); - } - } - return results; - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = - Strings::format("The argument should be a command line to autocomplete.\n%s", - Commands::Help::create_example_string("autocomplete install z")); - - args.check_min_arg_count(1, EXAMPLE); - args.check_max_arg_count(2, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - const std::string requested_command = args.command_arguments.at(0); - const std::string start_with = - args.command_arguments.size() > 1 ? args.command_arguments.at(1) : Strings::EMPTY; - std::vector results; - if (requested_command == "install") - { - auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); - auto& source_paragraphs = sources_and_errors.paragraphs; - - results = autocomplete_install(source_paragraphs, start_with); - } - else if (requested_command == "remove") - { - const StatusParagraphs status_db = database_load_check(paths); - std::vector installed_packages = get_installed_ports(status_db); - results = autocomplete_remove(installed_packages, start_with); - } - - System::println(Strings::join(" ", results)); - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_available_commands.cpp b/toolsrc/src/commands_available_commands.cpp deleted file mode 100644 index d3280e6d7..000000000 --- a/toolsrc/src/commands_available_commands.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" - -namespace vcpkg::Commands -{ - const std::vector>& get_available_commands_type_a() - { - static std::vector> t = { - {"install", &Install::perform_and_exit}, - {"ci", &CI::perform_and_exit}, - {"remove", &Remove::perform_and_exit}, - {"build", &BuildCommand::perform_and_exit}, - {"env", &Env::perform_and_exit}, - {"build-external", &BuildExternal::perform_and_exit}, - {"export", &Export::perform_and_exit}, - }; - return t; - } - - const std::vector>& get_available_commands_type_b() - { - static std::vector> t = { - {"/?", &Help::perform_and_exit}, - {"help", &Help::perform_and_exit}, - {"search", &Search::perform_and_exit}, - {"list", &List::perform_and_exit}, - {"integrate", &Integrate::perform_and_exit}, - {"owns", &Owns::perform_and_exit}, - {"update", &Update::perform_and_exit}, - {"depend-info", &DependInfo::perform_and_exit}, - {"edit", &Edit::perform_and_exit}, - {"create", &Create::perform_and_exit}, - {"import", &Import::perform_and_exit}, - {"cache", &Cache::perform_and_exit}, - {"portsdiff", &PortsDiff::perform_and_exit}, - {"autocomplete", &Autocomplete::perform_and_exit}}; - return t; - } - - const std::vector>& get_available_commands_type_c() - { - static std::vector> t = { - {"version", &Version::perform_and_exit}, - {"contact", &Contact::perform_and_exit}, - {"hash", &Hash::perform_and_exit}, - }; - return t; - } -} diff --git a/toolsrc/src/commands_build.cpp b/toolsrc/src/commands_build.cpp deleted file mode 100644 index a69958058..000000000 --- a/toolsrc/src/commands_build.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "PostBuildLint.h" -#include "StatusParagraphs.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Enums.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -using vcpkg::Build::BuildResult; -using vcpkg::Parse::ParseControlErrorInfo; -using vcpkg::Parse::ParseExpected; - -namespace vcpkg::Commands::BuildCommand -{ - using Dependencies::InstallPlanAction; - using Dependencies::InstallPlanType; - - static const std::string OPTION_CHECKS_ONLY = "--checks-only"; - - void perform_and_exit(const FullPackageSpec& full_spec, - const fs::path& port_dir, - const std::unordered_set& options, - const VcpkgPaths& paths) - { - const PackageSpec& spec = full_spec.package_spec; - if (options.find(OPTION_CHECKS_ONLY) != options.end()) - { - const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, spec.triplet()); - const auto build_info = Build::read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec)); - const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info); - Checks::check_exit(VCPKG_LINE_INFO, error_count == 0); - Checks::exit_success(VCPKG_LINE_INFO); - } - - const ParseExpected source_control_file = - Paragraphs::try_load_port(paths.get_filesystem(), port_dir); - - if (!source_control_file.has_value()) - { - print_error_message(source_control_file.error()); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - for (const std::string& str : full_spec.features) - { - System::println("%s \n", str); - } - const auto& scf = source_control_file.value_or_exit(VCPKG_LINE_INFO); - Checks::check_exit(VCPKG_LINE_INFO, - spec.name() == scf->core_paragraph->name, - "The Name: field inside the CONTROL does not match the port directory: '%s' != '%s'", - scf->core_paragraph->name, - spec.name()); - - const StatusParagraphs status_db = database_load_check(paths); - const Build::BuildPackageOptions build_package_options{Build::UseHeadVersion::NO, Build::AllowDownloads::YES}; - - const Build::BuildPackageConfig build_config{ - *scf->core_paragraph, spec.triplet(), paths.port_dir(spec), build_package_options}; - - const auto result = Build::build_package(paths, build_config, status_db); - if (result.code == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES) - { - System::println(System::Color::error, - "The build command requires all dependencies to be already installed."); - System::println("The following dependencies are missing:"); - System::println(); - for (const auto& p : result.unmet_dependencies) - { - System::println(" %s", p); - } - System::println(); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - if (result.code != BuildResult::SUCCEEDED) - { - System::println(System::Color::error, Build::create_error_message(result.code, spec)); - System::println(Build::create_user_troubleshooting_message(spec)); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string EXAMPLE = Commands::Help::create_example_string("build zlib:x64-windows"); - // Build only takes a single package and all dependencies must already be installed - args.check_exact_arg_count(1, EXAMPLE); - const std::string command_argument = args.command_arguments.at(0); - const FullPackageSpec spec = Input::check_and_get_full_package_spec(command_argument, default_triplet, EXAMPLE); - Input::check_triplet(spec.package_spec.triplet(), paths); - const std::unordered_set options = - args.check_and_get_optional_command_arguments({OPTION_CHECKS_ONLY}); - perform_and_exit(spec, paths.port_dir(spec.package_spec), options, paths); - } -} diff --git a/toolsrc/src/commands_build_external.cpp b/toolsrc/src/commands_build_external.cpp deleted file mode 100644 index 7e85f2250..000000000 --- a/toolsrc/src/commands_build_external.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_Input.h" - -namespace vcpkg::Commands::BuildExternal -{ - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string EXAMPLE = - Commands::Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"); - args.check_exact_arg_count(2, EXAMPLE); - const FullPackageSpec spec = - Input::check_and_get_full_package_spec(args.command_arguments.at(0), default_triplet, EXAMPLE); - Input::check_triplet(spec.package_spec.triplet(), paths); - const std::unordered_set options = args.check_and_get_optional_command_arguments({}); - - const fs::path port_dir = args.command_arguments.at(1); - BuildCommand::perform_and_exit(spec, port_dir, options, paths); - } -} diff --git a/toolsrc/src/commands_cache.cpp b/toolsrc/src/commands_cache.cpp deleted file mode 100644 index 5b65b197f..000000000 --- a/toolsrc/src/commands_cache.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "pch.h" - -#include "BinaryParagraph.h" -#include "Paragraphs.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Files.h" -#include "vcpkg_System.h" - -namespace vcpkg::Commands::Cache -{ - static std::vector read_all_binary_paragraphs(const VcpkgPaths& paths) - { - std::vector output; - for (auto&& path : paths.get_filesystem().get_files_non_recursive(paths.packages)) - { - const Expected> pghs = - Paragraphs::get_single_paragraph(paths.get_filesystem(), path / "CONTROL"); - if (const auto p = pghs.get()) - { - const BinaryParagraph binary_paragraph = BinaryParagraph(*p); - output.push_back(binary_paragraph); - } - } - - return output; - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Strings::format( - "The argument should be a substring to search for, or no argument to display all cached libraries.\n%s", - Commands::Help::create_example_string("cache png")); - args.check_max_arg_count(1, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - const std::vector binary_paragraphs = read_all_binary_paragraphs(paths); - if (binary_paragraphs.empty()) - { - System::println("No packages are cached."); - Checks::exit_success(VCPKG_LINE_INFO); - } - - if (args.command_arguments.size() == 0) - { - for (const BinaryParagraph& binary_paragraph : binary_paragraphs) - { - const std::string displayname = binary_paragraph.displayname(); - System::println(displayname); - } - } - else - { - // At this point there is 1 argument - for (const BinaryParagraph& binary_paragraph : binary_paragraphs) - { - const std::string displayname = binary_paragraph.displayname(); - if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end()) - { - continue; - } - - System::println(displayname); - } - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_ci.cpp b/toolsrc/src/commands_ci.cpp deleted file mode 100644 index 75ff65556..000000000 --- a/toolsrc/src/commands_ci.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "vcpkg_Build.h" -#include "vcpkg_Chrono.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Files.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::CI -{ - using Build::BuildResult; - using Dependencies::InstallPlanAction; - using Dependencies::InstallPlanType; - - static std::vector load_all_package_specs(Files::Filesystem& fs, - const fs::path& ports_directory, - const Triplet& triplet) - { - auto ports = Paragraphs::load_all_ports(fs, ports_directory); - return Util::fmap(ports, [&](auto&& control_file) -> PackageSpec { - return PackageSpec::from_name_and_triplet(control_file->core_paragraph->name, triplet) - .value_or_exit(VCPKG_LINE_INFO); - }); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string EXAMPLE = Commands::Help::create_example_string("ci x64-windows"); - args.check_max_arg_count(1, EXAMPLE); - const Triplet triplet = args.command_arguments.size() == 1 - ? Triplet::from_canonical_name(args.command_arguments.at(0)) - : default_triplet; - Input::check_triplet(triplet, paths); - args.check_and_get_optional_command_arguments({}); - const std::vector specs = load_all_package_specs(paths.get_filesystem(), paths.ports, triplet); - - StatusParagraphs status_db = database_load_check(paths); - const auto& paths_port_file = Dependencies::PathsPortFile(paths); - std::vector install_plan = - Dependencies::create_install_plan(paths_port_file, specs, status_db); - Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty"); - - const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO, Build::AllowDownloads::YES}; - - const std::vector action_plan = - Util::fmap(install_plan, [](InstallPlanAction& install_action) { - return Dependencies::AnyAction(std::move(install_action)); - }); - - Install::perform_and_exit( - action_plan, install_plan_options, Install::KeepGoing::YES, Install::PrintSummary::YES, paths, status_db); - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_contact.cpp b/toolsrc/src/commands_contact.cpp deleted file mode 100644 index 8374350ee..000000000 --- a/toolsrc/src/commands_contact.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" - -namespace vcpkg::Commands::Contact -{ - const std::string& email() - { - static const std::string S_EMAIL = R"(vcpkg@microsoft.com)"; - return S_EMAIL; - } - - void perform_and_exit(const VcpkgCmdArguments& args) - { - args.check_exact_arg_count(0); - args.check_and_get_optional_command_arguments({}); - - System::println("Send an email to %s with any feedback.", email()); - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_create.cpp b/toolsrc/src/commands_create.cpp deleted file mode 100644 index 6898f7399..000000000 --- a/toolsrc/src/commands_create.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_Files.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Create -{ - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Commands::Help::create_example_string( - R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###"); - args.check_max_arg_count(3, EXAMPLE); - args.check_min_arg_count(2, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - const std::string port_name = args.command_arguments.at(0); - const std::string url = args.command_arguments.at(1); - - const fs::path& cmake_exe = paths.get_cmake_exe(); - - std::vector cmake_args{{L"CMD", L"CREATE"}, {L"PORT", port_name}, {L"URL", url}}; - - if (args.command_arguments.size() >= 3) - { - const std::string& zip_file_name = args.command_arguments.at(2); - Checks::check_exit(VCPKG_LINE_INFO, - !Files::has_invalid_chars_for_filesystem(zip_file_name), - R"(Filename cannot contain invalid chars %s, but was %s)", - Files::FILESYSTEM_INVALID_CHARACTERS, - zip_file_name); - cmake_args.push_back({L"FILENAME", zip_file_name}); - } - - const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe, paths.ports_cmake, cmake_args); - Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute_clean(cmd_launch_cmake)); - } -} diff --git a/toolsrc/src/commands_depends.cpp b/toolsrc/src/commands_depends.cpp deleted file mode 100644 index 17cd9c881..000000000 --- a/toolsrc/src/commands_depends.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Strings.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" - -namespace vcpkg::Commands::DependInfo -{ - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Commands::Help::create_example_string(R"###(depend-info [pat])###"); - args.check_max_arg_count(1, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - std::vector> source_control_files = - Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); - - if (args.command_arguments.size() == 1) - { - const std::string filter = args.command_arguments.at(0); - - Util::erase_remove_if(source_control_files, - [&](const std::unique_ptr& source_control_file) { - - const SourceParagraph& source_paragraph = *source_control_file->core_paragraph; - - if (Strings::case_insensitive_ascii_contains(source_paragraph.name, filter)) - { - return false; - } - - for (const Dependency& dependency : source_paragraph.depends) - { - if (Strings::case_insensitive_ascii_contains(dependency.name(), filter)) - { - return false; - } - } - - return true; - }); - } - - for (auto&& source_control_file : source_control_files) - { - const SourceParagraph& source_paragraph = *source_control_file->core_paragraph; - const auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name(); }); - System::println("%s: %s", source_paragraph.name, s); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_edit.cpp b/toolsrc/src/commands_edit.cpp deleted file mode 100644 index 823c87534..000000000 --- a/toolsrc/src/commands_edit.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" - -namespace vcpkg::Commands::Edit -{ - static std::vector find_from_registry() - { - static const std::array REGKEYS = { - LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)", - LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1)", - LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)", - }; - - std::vector output; - for (auto&& keypath : REGKEYS) - { - const Optional code_installpath = - System::get_registry_string(HKEY_LOCAL_MACHINE, keypath, L"InstallLocation"); - if (const auto c = code_installpath.get()) - { - const fs::path install_path = fs::path(*c); - output.push_back(install_path / "Code - Insiders.exe"); - output.push_back(install_path / "Code.exe"); - } - } - return output; - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string OPTION_BUILDTREES = "--buildtrees"; - - static const fs::path VS_CODE_INSIDERS = fs::path{"Microsoft VS Code Insiders"} / "Code - Insiders.exe"; - static const fs::path VS_CODE = fs::path{"Microsoft VS Code"} / "Code.exe"; - - auto& fs = paths.get_filesystem(); - - static const std::string EXAMPLE = Commands::Help::create_example_string("edit zlib"); - args.check_exact_arg_count(1, EXAMPLE); - const std::unordered_set options = - args.check_and_get_optional_command_arguments({OPTION_BUILDTREES}); - const std::string port_name = args.command_arguments.at(0); - - const fs::path portpath = paths.ports / port_name; - Checks::check_exit(VCPKG_LINE_INFO, fs.is_directory(portpath), R"(Could not find port named "%s")", port_name); - - std::vector candidate_paths; - const std::vector from_path = Files::find_from_PATH(L"EDITOR"); - candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); - candidate_paths.push_back(System::get_program_files_platform_bitness() / VS_CODE_INSIDERS); - candidate_paths.push_back(System::get_program_files_32_bit() / VS_CODE_INSIDERS); - candidate_paths.push_back(System::get_program_files_platform_bitness() / VS_CODE); - candidate_paths.push_back(System::get_program_files_32_bit() / VS_CODE); - - const std::vector from_registry = find_from_registry(); - candidate_paths.insert(candidate_paths.end(), from_registry.cbegin(), from_registry.cend()); - - auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { return fs.exists(p); }); - if (it == candidate_paths.cend()) - { - System::println(System::Color::error, - "Error: Visual Studio Code was not found and the environment variable EDITOR is not set."); - System::println("The following paths were examined:"); - Files::print_paths(candidate_paths); - System::println("You can also set the environmental variable EDITOR to your editor of choice."); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - const fs::path env_editor = *it; - if (options.find(OPTION_BUILDTREES) != options.cend()) - { - const auto buildtrees_current_dir = paths.buildtrees / port_name; - - const std::wstring cmd_line = - Strings::wformat(LR"("%s" "%s" -n)", env_editor, buildtrees_current_dir.native()); - Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute(cmd_line)); - } - - const std::wstring cmd_line = Strings::wformat( - LR"("%s" "%s" "%s" -n)", env_editor, portpath.native(), (portpath / "portfile.cmake").native()); - Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute(cmd_line)); - } -} diff --git a/toolsrc/src/commands_env.cpp b/toolsrc/src/commands_env.cpp deleted file mode 100644 index 6dad3e882..000000000 --- a/toolsrc/src/commands_env.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Build.h" -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" - -namespace vcpkg::Commands::Env -{ - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string EXAMPLE = Commands::Help::create_example_string(R"(env --Triplet x64-windows)"); - args.check_exact_arg_count(0, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet); - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); - System::cmd_execute_clean(Build::make_build_env_cmd(pre_build_info, toolset) + L" && cmd"); - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp deleted file mode 100644 index 20838f5a5..000000000 --- a/toolsrc/src/commands_export.cpp +++ /dev/null @@ -1,526 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Commands_Export_IFW.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include "vcpkglib.h" -#include - -namespace vcpkg::Commands::Export -{ - using Dependencies::ExportPlanAction; - using Dependencies::ExportPlanType; - using Dependencies::RequestType; - using Install::InstallDir; - - static std::string create_nuspec_file_contents(const std::string& raw_exported_dir, - const std::string& targets_redirect_path, - const std::string& nuget_id, - const std::string& nupkg_version) - { - static constexpr auto CONTENT_TEMPLATE = R"( - - - @NUGET_ID@ - @VERSION@ - vcpkg - - Vcpkg NuGet export - - - - - - - - - -)"; - - std::string nuspec_file_content = std::regex_replace(CONTENT_TEMPLATE, std::regex("@NUGET_ID@"), nuget_id); - nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VERSION@"), nupkg_version); - nuspec_file_content = - std::regex_replace(nuspec_file_content, std::regex("@RAW_EXPORTED_DIR@"), raw_exported_dir); - nuspec_file_content = - std::regex_replace(nuspec_file_content, std::regex("@TARGETS_REDIRECT_PATH@"), targets_redirect_path); - return nuspec_file_content; - } - - static std::string create_targets_redirect(const std::string& target_path) noexcept - { - return Strings::format(R"###( - - - -)###", - target_path, - target_path); - } - - static void print_plan(const std::map>& group_by_plan_type) - { - static constexpr std::array ORDER = {ExportPlanType::ALREADY_BUILT, - ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT}; - - for (const ExportPlanType plan_type : ORDER) - { - const auto it = group_by_plan_type.find(plan_type); - if (it == group_by_plan_type.cend()) - { - continue; - } - - std::vector cont = it->second; - std::sort(cont.begin(), cont.end(), &ExportPlanAction::compare_by_name); - const std::string as_string = Strings::join("\n", cont, [](const ExportPlanAction* p) { - return Dependencies::to_output_string(p->request_type, p->spec.to_string()); - }); - - switch (plan_type) - { - case ExportPlanType::ALREADY_BUILT: - System::println("The following packages are already built and will be exported:\n%s", as_string); - continue; - case ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT: - System::println("The following packages need to be built:\n%s", as_string); - continue; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - } - - static std::string create_export_id() - { - const tm date_time = System::get_current_date_time(); - - // Format is: YYYYmmdd-HHMMSS - // 15 characters + 1 null terminating character will be written for a total of 16 chars - char mbstr[16]; - const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y%m%d-%H%M%S", &date_time); - Checks::check_exit(VCPKG_LINE_INFO, - bytes_written == 15, - "Expected 15 bytes to be written, but %u were written", - bytes_written); - const std::string date_time_as_string(mbstr); - return ("vcpkg-export-" + date_time_as_string); - } - - static fs::path do_nuget_export(const VcpkgPaths& paths, - const std::string& nuget_id, - const std::string& nuget_version, - const fs::path& raw_exported_dir, - const fs::path& output_dir) - { - Files::Filesystem& fs = paths.get_filesystem(); - const fs::path& nuget_exe = paths.get_nuget_exe(); - - // This file will be placed in "build\native" in the nuget package. Therefore, go up two dirs. - const std::string targets_redirect_content = - create_targets_redirect("../../scripts/buildsystems/msbuild/vcpkg.targets"); - const fs::path targets_redirect = paths.buildsystems / "tmp" / "vcpkg.export.nuget.targets"; - - std::error_code ec; - fs.create_directories(paths.buildsystems / "tmp", ec); - - fs.write_contents(targets_redirect, targets_redirect_content); - - const std::string nuspec_file_content = - create_nuspec_file_contents(raw_exported_dir.string(), targets_redirect.string(), nuget_id, nuget_version); - const fs::path nuspec_file_path = paths.buildsystems / "tmp" / "vcpkg.export.nuspec"; - fs.write_contents(nuspec_file_path, nuspec_file_content); - - // -NoDefaultExcludes is needed for ".vcpkg-root" - const std::wstring cmd_line = - Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes > nul)", - nuget_exe.native(), - output_dir.native(), - nuspec_file_path.native()); - - const int exit_code = System::cmd_execute_clean(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed"); - - const fs::path output_path = output_dir / (nuget_id + ".nupkg"); - return output_path; - } - - struct ArchiveFormat final - { - enum class BackingEnum - { - ZIP = 1, - SEVEN_ZIP, - }; - - constexpr ArchiveFormat() = delete; - - constexpr ArchiveFormat(BackingEnum backing_enum, const wchar_t* extension, const wchar_t* cmake_option) - : backing_enum(backing_enum), m_extension(extension), m_cmake_option(cmake_option) - { - } - - constexpr operator BackingEnum() const { return backing_enum; } - constexpr CWStringView extension() const { return this->m_extension; } - constexpr CWStringView cmake_option() const { return this->m_cmake_option; } - - private: - BackingEnum backing_enum; - const wchar_t* m_extension; - const wchar_t* m_cmake_option; - }; - - namespace ArchiveFormatC - { - constexpr const ArchiveFormat ZIP(ArchiveFormat::BackingEnum::ZIP, L"zip", L"zip"); - constexpr const ArchiveFormat SEVEN_ZIP(ArchiveFormat::BackingEnum::SEVEN_ZIP, L"7z", L"7zip"); - } - - static fs::path do_archive_export(const VcpkgPaths& paths, - const fs::path& raw_exported_dir, - const fs::path& output_dir, - const ArchiveFormat& format) - { - const fs::path& cmake_exe = paths.get_cmake_exe(); - - const std::wstring exported_dir_filename = raw_exported_dir.filename().native(); - const std::wstring exported_archive_filename = - Strings::wformat(L"%s.%s", exported_dir_filename, format.extension()); - const fs::path exported_archive_path = (output_dir / exported_archive_filename); - - // -NoDefaultExcludes is needed for ".vcpkg-root" - const std::wstring cmd_line = Strings::wformat(LR"("%s" -E tar "cf" "%s" --format=%s -- "%s")", - cmake_exe.native(), - exported_archive_path.native(), - format.cmake_option(), - raw_exported_dir.native()); - - const int exit_code = System::cmd_execute_clean(cmd_line); - Checks::check_exit( - VCPKG_LINE_INFO, exit_code == 0, "Error: %s creation failed", exported_archive_path.generic_string()); - return exported_archive_path; - } - - static Optional maybe_lookup(std::unordered_map const& m, - std::string const& key) - { - const auto it = m.find(key); - if (it != m.end()) return it->second; - return nullopt; - } - - void export_integration_files(const fs::path& raw_exported_dir_path, const VcpkgPaths& paths) - { - const std::vector integration_files_relative_to_root = { - {".vcpkg-root"}, - {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"}, - {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, - {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"}, - {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, - {fs::path{"scripts"} / "getWindowsSDK.ps1"}, - {fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"}, - {fs::path{"scripts"} / "getProgramFiles32bit.ps1"}, - }; - - for (const fs::path& file : integration_files_relative_to_root) - { - const fs::path source = paths.root / file; - fs::path destination = raw_exported_dir_path / file; - Files::Filesystem& fs = paths.get_filesystem(); - std::error_code ec; - fs.create_directories(destination.parent_path(), ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec); - fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec); - } - } - - struct ExportArguments - { - bool dry_run; - bool raw; - bool nuget; - bool ifw; - bool zip; - bool seven_zip; - - Optional maybe_nuget_id; - Optional maybe_nuget_version; - - IFW::Options ifw_options; - std::vector specs; - }; - - static ExportArguments handle_export_command_arguments(const VcpkgCmdArguments& args, - const Triplet& default_triplet) - { - ExportArguments ret; - - static const std::string OPTION_DRY_RUN = "--dry-run"; - static const std::string OPTION_RAW = "--raw"; - static const std::string OPTION_NUGET = "--nuget"; - static const std::string OPTION_IFW = "--ifw"; - static const std::string OPTION_ZIP = "--zip"; - static const std::string OPTION_SEVEN_ZIP = "--7zip"; - static const std::string OPTION_NUGET_ID = "--nuget-id"; - static const std::string OPTION_NUGET_VERSION = "--nuget-version"; - static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url"; - static const std::string OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path"; - static const std::string OPTION_IFW_REPOSITORY_DIR_PATH = "--ifw-repository-directory-path"; - static const std::string OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path"; - static const std::string OPTION_IFW_INSTALLER_FILE_PATH = "--ifw-installer-file-path"; - - // input sanitization - static const std::string EXAMPLE = - Commands::Help::create_example_string("export zlib zlib:x64-windows boost --nuget"); - args.check_min_arg_count(1, EXAMPLE); - - ret.specs = Util::fmap(args.command_arguments, [&](auto&& arg) { - return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE); - }); - - const auto options = args.check_and_get_optional_command_arguments( - { - OPTION_DRY_RUN, - OPTION_RAW, - OPTION_NUGET, - OPTION_IFW, - OPTION_ZIP, - OPTION_SEVEN_ZIP, - }, - { - OPTION_NUGET_ID, - OPTION_NUGET_VERSION, - OPTION_IFW_REPOSITORY_URL, - OPTION_IFW_PACKAGES_DIR_PATH, - OPTION_IFW_REPOSITORY_DIR_PATH, - OPTION_IFW_CONFIG_FILE_PATH, - OPTION_IFW_INSTALLER_FILE_PATH, - }); - ret.dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); - ret.raw = options.switches.find(OPTION_RAW) != options.switches.cend(); - ret.nuget = options.switches.find(OPTION_NUGET) != options.switches.cend(); - ret.ifw = options.switches.find(OPTION_IFW) != options.switches.cend(); - ret.zip = options.switches.find(OPTION_ZIP) != options.switches.cend(); - ret.seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend(); - - if (!ret.raw && !ret.nuget && !ret.ifw && !ret.zip && !ret.seven_zip && !ret.dry_run) - { - System::println(System::Color::error, - "Must provide at least one export type: --raw --nuget --ifw --zip --7zip"); - System::print(EXAMPLE); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - struct OptionPair - { - const std::string& name; - Optional& out_opt; - }; - const auto options_implies = - [&](const std::string& main_opt_name, bool main_opt, Span implying_opts) { - if (main_opt) - { - for (auto&& opt : implying_opts) - opt.out_opt = maybe_lookup(options.settings, opt.name); - } - else - { - for (auto&& opt : implying_opts) - Checks::check_exit(VCPKG_LINE_INFO, - !maybe_lookup(options.settings, opt.name), - "%s is only valid with %s", - opt.name, - main_opt_name); - } - }; - - options_implies(OPTION_NUGET, - ret.nuget, - { - {OPTION_NUGET_ID, ret.maybe_nuget_id}, - {OPTION_NUGET_VERSION, ret.maybe_nuget_version}, - }); - - options_implies(OPTION_IFW, - ret.ifw, - { - {OPTION_IFW_REPOSITORY_URL, ret.ifw_options.maybe_repository_url}, - {OPTION_IFW_PACKAGES_DIR_PATH, ret.ifw_options.maybe_packages_dir_path}, - {OPTION_IFW_REPOSITORY_DIR_PATH, ret.ifw_options.maybe_repository_dir_path}, - {OPTION_IFW_CONFIG_FILE_PATH, ret.ifw_options.maybe_config_file_path}, - {OPTION_IFW_INSTALLER_FILE_PATH, ret.ifw_options.maybe_installer_file_path}, - }); - return ret; - } - - static void print_next_step_info(const fs::path& prefix) - { - const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; - const CMakeVariable cmake_variable = CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); - System::println("\n" - "To use the exported libraries in CMake projects use:" - "\n" - " %s" - "\n", - Strings::to_utf8(cmake_variable.s)); - }; - - static void handle_raw_based_export(Span export_plan, - const ExportArguments& opts, - const std::string& export_id, - const VcpkgPaths& paths) - { - Files::Filesystem& fs = paths.get_filesystem(); - const fs::path export_to_path = paths.root; - const fs::path raw_exported_dir_path = export_to_path / export_id; - std::error_code ec; - fs.remove_all(raw_exported_dir_path, ec); - fs.create_directory(raw_exported_dir_path, ec); - - // execute the plan - for (const ExportPlanAction& action : export_plan) - { - if (action.plan_type != ExportPlanType::ALREADY_BUILT) - { - Checks::unreachable(VCPKG_LINE_INFO); - } - - const std::string display_name = action.spec.to_string(); - System::println("Exporting package %s... ", display_name); - - const BinaryParagraph& binary_paragraph = - action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - - const InstallDir dirs = InstallDir::from_destination_root( - raw_exported_dir_path / "installed", - action.spec.triplet().to_string(), - raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); - - Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); - System::println(System::Color::success, "Exporting package %s... done", display_name); - } - - // Copy files needed for integration - export_integration_files(raw_exported_dir_path, paths); - - if (opts.raw) - { - System::println( - System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string()); - print_next_step_info(export_to_path); - } - - if (opts.nuget) - { - System::println("Creating nuget package... "); - - const std::string nuget_id = opts.maybe_nuget_id.value_or(raw_exported_dir_path.filename().string()); - const std::string nuget_version = opts.maybe_nuget_version.value_or("1.0.0"); - const fs::path output_path = - do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path); - System::println(System::Color::success, "Creating nuget package... done"); - System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string()); - - System::println(R"( -With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: - Install-Package %s -Source "%s" -)" - "\n", - nuget_id, - output_path.parent_path().u8string()); - } - - if (opts.zip) - { - System::println("Creating zip archive... "); - const fs::path output_path = - do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP); - System::println(System::Color::success, "Creating zip archive... done"); - System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string()); - print_next_step_info("[...]"); - } - - if (opts.seven_zip) - { - System::println("Creating 7zip archive... "); - const fs::path output_path = - do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP); - System::println(System::Color::success, "Creating 7zip archive... done"); - System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string()); - print_next_step_info("[...]"); - } - - if (!opts.raw) - { - fs.remove_all(raw_exported_dir_path, ec); - } - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - const auto opts = handle_export_command_arguments(args, default_triplet); - for (auto&& spec : opts.specs) - Input::check_triplet(spec.triplet(), paths); - - // create the plan - const StatusParagraphs status_db = database_load_check(paths); - std::vector export_plan = Dependencies::create_export_plan(paths, opts.specs, status_db); - Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty"); - - std::map> group_by_plan_type; - Util::group_by(export_plan, &group_by_plan_type, [](const ExportPlanAction& p) { return p.plan_type; }); - print_plan(group_by_plan_type); - - const bool has_non_user_requested_packages = - Util::find_if(export_plan, [](const ExportPlanAction& package) -> bool { - return package.request_type != RequestType::USER_REQUESTED; - }) != export_plan.cend(); - - if (has_non_user_requested_packages) - { - System::println(System::Color::warning, - "Additional packages (*) need to be exported to complete this operation."); - } - - const auto it = group_by_plan_type.find(ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT); - if (it != group_by_plan_type.cend() && !it->second.empty()) - { - System::println(System::Color::error, "There are packages that have not been built."); - - // No need to show all of them, just the user-requested ones. Dependency resolution will handle the rest. - std::vector unbuilt = it->second; - Util::erase_remove_if( - unbuilt, [](const ExportPlanAction* a) { return a->request_type != RequestType::USER_REQUESTED; }); - - const auto s = Strings::join(" ", unbuilt, [](const ExportPlanAction* a) { return a->spec.to_string(); }); - System::println("To build them, run:\n" - " vcpkg install %s", - s); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - if (opts.dry_run) - { - Checks::exit_success(VCPKG_LINE_INFO); - } - - std::string export_id = create_export_id(); - - if (opts.raw || opts.nuget || opts.zip || opts.seven_zip) - { - handle_raw_based_export(export_plan, opts, export_id, paths); - } - - if (opts.ifw) - { - IFW::do_export(export_plan, export_id, opts.ifw_options, paths); - - print_next_step_info("@RootDir@/src/vcpkg"); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_export_ifw.cpp b/toolsrc/src/commands_export_ifw.cpp deleted file mode 100644 index 191dbb763..000000000 --- a/toolsrc/src/commands_export_ifw.cpp +++ /dev/null @@ -1,481 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_Commands_Export.h" -#include "vcpkg_Commands_Export_IFW.h" - -namespace vcpkg::Commands::Export::IFW -{ - using Dependencies::ExportPlanAction; - using Dependencies::ExportPlanType; - using Install::InstallDir; - - static std::string create_release_date() - { - const tm date_time = System::get_current_date_time(); - - // Format is: YYYY-mm-dd - // 10 characters + 1 null terminating character will be written for a total of 11 chars - char mbstr[11]; - const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d", &date_time); - Checks::check_exit(VCPKG_LINE_INFO, - bytes_written == 10, - "Expected 10 bytes to be written, but %u were written", - bytes_written); - const std::string date_time_as_string(mbstr); - return date_time_as_string; - } - - fs::path get_packages_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - return ifw_options.maybe_packages_dir_path.has_value() - ? fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-packages"); - } - - fs::path get_repository_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - return ifw_options.maybe_repository_dir_path.has_value() - ? fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-repository"); - } - - fs::path get_config_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - return ifw_options.maybe_config_file_path.has_value() - ? fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-configuration.xml"); - } - - fs::path get_installer_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - return ifw_options.maybe_installer_file_path.has_value() - ? fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO)) - : paths.root / (export_id + "-ifw-installer.exe"); - } - - fs::path export_real_package(const fs::path& ifw_packages_dir_path, - const ExportPlanAction& action, - Files::Filesystem& fs) - { - std::error_code ec; - - const BinaryParagraph& binary_paragraph = - action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - - // Prepare meta dir - const fs::path package_xml_file_path = - ifw_packages_dir_path / - Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" / - "package.xml"; - const fs::path package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - - auto deps = Strings::join( - ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); - - if (!deps.empty()) deps = "\n " + deps + ""; - - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - %s - %s - %s - packages.%s:,triplets.%s:%s - true - -)###", - action.spec.to_string(), - binary_paragraph.version, - create_release_date(), - action.spec.name(), - action.spec.triplet().canonical_name(), - deps)); - - // Return dir path for export package data - return ifw_packages_dir_path / - Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" / - "installed"; - } - - void export_unique_packages(const fs::path& raw_exported_dir_path, - std::map unique_packages, - Files::Filesystem& fs) - { - std::error_code ec; - - // packages - - fs::path package_xml_file_path = raw_exported_dir_path / "packages" / "meta" / "package.xml"; - fs::path package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - Packages - 1.0.0 - %s - -)###", - create_release_date())); - - for (auto package = unique_packages.begin(); package != unique_packages.end(); ++package) - { - const ExportPlanAction& action = *(package->second); - const BinaryParagraph& binary_paragraph = - action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - - package_xml_file_path = - raw_exported_dir_path / Strings::format("packages.%s", package->first) / "meta" / "package.xml"; - package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - %s - %s - %s - %s - -)###", - action.spec.name(), - binary_paragraph.description, - binary_paragraph.version, - create_release_date())); - } - } - - void export_unique_triplets(const fs::path& raw_exported_dir_path, - std::set unique_triplets, - Files::Filesystem& fs) - { - std::error_code ec; - - // triplets - - fs::path package_xml_file_path = raw_exported_dir_path / "triplets" / "meta" / "package.xml"; - fs::path package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - Triplets - 1.0.0 - %s - -)###", - create_release_date())); - - for (const std::string& triplet : unique_triplets) - { - package_xml_file_path = - raw_exported_dir_path / Strings::format("triplets.%s", triplet) / "meta" / "package.xml"; - package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - %s - 1.0.0 - %s - -)###", - triplet, - create_release_date())); - } - } - - void export_integration(const fs::path& raw_exported_dir_path, Files::Filesystem& fs) - { - std::error_code ec; - - // integration - fs::path package_xml_file_path = raw_exported_dir_path / "integration" / "meta" / "package.xml"; - fs::path package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - Integration - 1.0.0 - %s - -)###", - create_release_date())); - } - - void export_config(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - std::error_code ec; - Files::Filesystem& fs = paths.get_filesystem(); - - const fs::path config_xml_file_path = get_config_file_path(export_id, ifw_options, paths); - - fs::path config_xml_dir_path = config_xml_file_path.parent_path(); - fs.create_directories(config_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for configuration file %s", - config_xml_file_path.generic_string()); - - std::string formatted_repo_url; - std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); - if (!ifw_repo_url.empty()) - { - formatted_repo_url = Strings::format(R"###( - - - %s - - )###", - ifw_repo_url); - } - - fs.write_contents(config_xml_file_path, - Strings::format( - R"###( - - vcpkg - 1.0.0 - vcpkg - @RootDir@/src/vcpkg%s - -)###", - formatted_repo_url)); - } - - void export_maintenance_tool(const fs::path& ifw_packages_dir_path, const VcpkgPaths& paths) - { - System::println("Exporting maintenance tool... "); - - std::error_code ec; - Files::Filesystem& fs = paths.get_filesystem(); - - const fs::path& installerbase_exe = paths.get_ifw_installerbase_exe(); - fs::path tempmaintenancetool = ifw_packages_dir_path / "maintenance" / "data" / "tempmaintenancetool.exe"; - fs.create_directories(tempmaintenancetool.parent_path(), ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - tempmaintenancetool.generic_string()); - fs.copy_file(installerbase_exe, tempmaintenancetool, fs::copy_options::overwrite_existing, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not write package file %s", tempmaintenancetool.generic_string()); - - fs::path package_xml_file_path = ifw_packages_dir_path / "maintenance" / "meta" / "package.xml"; - fs::path package_xml_dir_path = package_xml_file_path.parent_path(); - fs.create_directories(package_xml_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not create directory for package file %s", - package_xml_file_path.generic_string()); - fs.write_contents(package_xml_file_path, - Strings::format( - R"###( - - Maintenance Tool - Maintenance Tool - 1.0.0 - %s - - true - true - true - -)###", - create_release_date())); - const fs::path script_source = paths.root / "scripts" / "ifw" / "maintenance.qs"; - const fs::path script_destination = ifw_packages_dir_path / "maintenance" / "meta" / "maintenance.qs"; - fs.copy_file(script_source, script_destination, fs::copy_options::overwrite_existing, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not write package file %s", script_destination.generic_string()); - - System::println("Exporting maintenance tool... done"); - } - - void do_repository(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - const fs::path& repogen_exe = paths.get_ifw_repogen_exe(); - const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); - const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths); - - System::println("Generating repository %s...", repository_dir.generic_string()); - - std::error_code ec; - Files::Filesystem& fs = paths.get_filesystem(); - - fs.remove_all(repository_dir, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not remove outdated repository directory %s", repository_dir.generic_string()); - - const std::wstring cmd_line = Strings::wformat(LR"("%s" --packages "%s" "%s" > nul)", - repogen_exe.native(), - packages_dir.native(), - repository_dir.native()); - - const int exit_code = System::cmd_execute_clean(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW repository generating failed"); - - System::println(System::Color::success, "Generating repository %s... done.", repository_dir.generic_string()); - } - - void do_installer(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) - { - const fs::path& binarycreator_exe = paths.get_ifw_binarycreator_exe(); - const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); - const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); - const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths); - const fs::path installer_file = get_installer_file_path(export_id, ifw_options, paths); - - System::println("Generating installer %s...", installer_file.generic_string()); - - std::wstring cmd_line; - - std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); - if (!ifw_repo_url.empty()) - { - cmd_line = Strings::wformat(LR"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)", - binarycreator_exe.native(), - config_file.native(), - repository_dir.native(), - installer_file.native()); - } - else - { - cmd_line = Strings::wformat(LR"("%s" --config "%s" --packages "%s" "%s" > nul)", - binarycreator_exe.native(), - config_file.native(), - packages_dir.native(), - installer_file.native()); - } - - const int exit_code = System::cmd_execute_clean(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW installer generating failed"); - - System::println(System::Color::success, "Generating installer %s... done.", installer_file.generic_string()); - } - - void do_export(const std::vector& export_plan, - const std::string& export_id, - const Options& ifw_options, - const VcpkgPaths& paths) - { - std::error_code ec; - Files::Filesystem& fs = paths.get_filesystem(); - - // Prepare packages directory - const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); - - fs.remove_all(ifw_packages_dir_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Could not remove outdated packages directory %s", - ifw_packages_dir_path.generic_string()); - - fs.create_directory(ifw_packages_dir_path, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create packages directory %s", ifw_packages_dir_path.generic_string()); - - // Export maintenance tool - export_maintenance_tool(ifw_packages_dir_path, paths); - - System::println("Exporting packages %s... ", ifw_packages_dir_path.generic_string()); - - // execute the plan - std::map unique_packages; - std::set unique_triplets; - for (const ExportPlanAction& action : export_plan) - { - if (action.plan_type != ExportPlanType::ALREADY_BUILT) - { - Checks::unreachable(VCPKG_LINE_INFO); - } - - const std::string display_name = action.spec.to_string(); - System::println("Exporting package %s... ", display_name); - - const BinaryParagraph& binary_paragraph = - action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; - - unique_packages[action.spec.name()] = &action; - unique_triplets.insert(action.spec.triplet().canonical_name()); - - // Export real package and return data dir for installation - fs::path ifw_package_dir_path = export_real_package(ifw_packages_dir_path, action, fs); - - // Copy package data - const InstallDir dirs = InstallDir::from_destination_root(ifw_package_dir_path, - action.spec.triplet().to_string(), - ifw_package_dir_path / "vcpkg" / "info" / - (binary_paragraph.fullstem() + ".list")); - - Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); - System::println("Exporting package %s... done", display_name); - } - - System::println("Exporting packages %s... done", ifw_packages_dir_path.generic_string()); - - const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); - - System::println("Generating configuration %s...", config_file.generic_string()); - - // Unique packages - export_unique_packages(ifw_packages_dir_path, unique_packages, fs); - - // Unique triplets - export_unique_triplets(ifw_packages_dir_path, unique_triplets, fs); - - // Copy files needed for integration - export_integration_files(ifw_packages_dir_path / "integration" / "data", paths); - // Integration - export_integration(ifw_packages_dir_path, fs); - - // Configuration - export_config(export_id, ifw_options, paths); - - System::println("Generating configuration %s... done.", config_file.generic_string()); - - // Do repository (optional) - std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); - if (!ifw_repo_url.empty()) - { - do_repository(export_id, ifw_options, paths); - } - - // Do installer - do_installer(export_id, ifw_options, paths); - } -} diff --git a/toolsrc/src/commands_hash.cpp b/toolsrc/src/commands_hash.cpp deleted file mode 100644 index b3211b9f8..000000000 --- a/toolsrc/src/commands_hash.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" - -namespace vcpkg::Commands::Hash -{ - static void do_file_hash(fs::path const& path, std::wstring const& hash_type) - { - const auto cmd_line = Strings::wformat(LR"(CertUtil.exe -hashfile "%s" %s)", path.c_str(), hash_type); - const auto ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit( - VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::to_utf8(cmd_line)); - - std::string const& output = ec_data.output; - - const auto start = output.find_first_of("\r\n"); - Checks::check_exit(VCPKG_LINE_INFO, - start != std::string::npos, - "Unexpected output format from command: %s", - Strings::to_utf8(cmd_line)); - - const auto end = output.find_first_of("\r\n", start + 1); - Checks::check_exit(VCPKG_LINE_INFO, - end != std::string::npos, - "Unexpected output format from command: %s", - Strings::to_utf8(cmd_line)); - - auto hash = output.substr(start, end - start); - Util::erase_remove_if(hash, isspace); - System::println(hash); - } - - void perform_and_exit(const VcpkgCmdArguments& args) - { - static const std::string EXAMPLE = - Strings::format("The argument should be a file path\n%s", - Commands::Help::create_example_string("hash boost_1_62_0.tar.bz2")); - args.check_min_arg_count(1, EXAMPLE); - args.check_max_arg_count(2, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - if (args.command_arguments.size() == 1) - { - do_file_hash(args.command_arguments[0], L"SHA512"); - } - if (args.command_arguments.size() == 2) - { - do_file_hash(args.command_arguments[0], Strings::to_utf16(args.command_arguments[1])); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_help.cpp b/toolsrc/src/commands_help.cpp deleted file mode 100644 index a12f9003d..000000000 --- a/toolsrc/src/commands_help.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" - -namespace vcpkg::Commands::Help -{ - void help_topics() - { - System::println("Available help topics:\n" - " triplet\n" - " integrate\n" - " export"); - } - - void help_topic_valid_triplet(const VcpkgPaths& paths) - { - System::println("Available architecture triplets:"); - for (auto&& path : paths.get_filesystem().get_files_non_recursive(paths.triplets)) - { - System::println(" %s", path.stem().filename().string()); - } - } - - void help_topic_export() - { - System::println("Summary:\n" - " vcpkg export [options] ...\n" - "\n" - "Options:\n" - " --7zip Export to a 7zip (.7z) file\n" - " --dry-run Do not actually export\n" - " --nuget Export a NuGet package\n" - " --nuget-id= Specify the id for the exported NuGet package\n" - " --nuget-version= Specify the version for the exported NuGet package\n" - " --raw Export to an uncompressed directory\n" - " --zip Export to a zip file"); - } - - void print_usage() - { - System::println( - "Commands:\n" - " vcpkg search [pat] Search for packages available to be built\n" - " vcpkg install ... Install a package\n" - " vcpkg remove ... Uninstall a package\n" - " vcpkg remove --outdated Uninstall all out-of-date packages\n" - " vcpkg list List installed packages\n" - " vcpkg update Display list of packages for updating\n" - " vcpkg hash [alg] Hash a file by specific algorithm, default SHA512\n" - " vcpkg help topics Display the list of help topics\n" - " vcpkg help Display help for a specific topic\n" - "\n" - "%s" // Integration help - "\n" - " vcpkg export ... [opt]... Exports a package\n" - " vcpkg edit Open up a port for editing (uses %%EDITOR%%, default 'code')\n" - " vcpkg import Import a pre-built library\n" - " vcpkg create \n" - " [archivename] Create a new package\n" - " vcpkg owns Search for files in installed packages\n" - " vcpkg cache List cached compiled packages\n" - " vcpkg version Display version information\n" - " vcpkg contact Display contact information to send feedback\n" - "\n" - //"internal commands:\n" - //" --check-build-deps \n" - //" --create-binary-control \n" - //"\n" - "Options:\n" - " --triplet Specify the target architecture triplet.\n" - " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n" - "\n" - " --vcpkg-root Specify the vcpkg root directory\n" - " (default: %%VCPKG_ROOT%%)\n" - "\n" - "For more help (including examples) see the accompanying README.md.", - Integrate::INTEGRATE_COMMAND_HELPSTRING); - } - - std::string create_example_string(const std::string& command_and_arguments) - { - std::string cs = Strings::format("Example:\n" - " vcpkg %s\n", - command_and_arguments); - return cs; - } - - void print_example(const std::string& command_and_arguments) - { - System::println(create_example_string(command_and_arguments)); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - args.check_max_arg_count(1); - args.check_and_get_optional_command_arguments({}); - - if (args.command_arguments.empty()) - { - print_usage(); - Checks::exit_success(VCPKG_LINE_INFO); - } - const auto& topic = args.command_arguments[0]; - if (topic == "triplet" || topic == "triplets" || topic == "triple") - { - help_topic_valid_triplet(paths); - } - else if (topic == "export") - { - help_topic_export(); - } - else if (topic == "integrate") - { - System::print("Commands:\n" - "%s", - Integrate::INTEGRATE_COMMAND_HELPSTRING); - } - else if (topic == "topics") - { - help_topics(); - } - else - { - System::println(System::Color::error, "Error: unknown topic %s", topic); - help_topics(); - Checks::exit_fail(VCPKG_LINE_INFO); - } - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_import.cpp b/toolsrc/src/commands_import.cpp deleted file mode 100644 index 412a03d7f..000000000 --- a/toolsrc/src/commands_import.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "StatusParagraph.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Files.h" - -namespace vcpkg::Commands::Import -{ - struct Binaries - { - std::vector dlls; - std::vector libs; - }; - - static void check_is_directory(const LineInfo& line_info, const Files::Filesystem& fs, const fs::path& dirpath) - { - Checks::check_exit(line_info, fs.is_directory(dirpath), "The path %s is not a directory", dirpath.string()); - } - - static Binaries find_binaries_in_dir(const Files::Filesystem& fs, const fs::path& path) - { - auto files = fs.get_files_recursive(path); - - check_is_directory(VCPKG_LINE_INFO, fs, path); - - Binaries binaries; - for (auto&& file : files) - { - if (fs.is_directory(file)) continue; - const auto ext = file.extension(); - if (ext == ".dll") - binaries.dlls.push_back(std::move(file)); - else if (ext == ".lib") - binaries.libs.push_back(std::move(file)); - } - return binaries; - } - - static void copy_files_into_directory(Files::Filesystem& fs, - const std::vector& files, - const fs::path& destination_folder) - { - std::error_code ec; - fs.create_directory(destination_folder, ec); - - for (auto const& src_path : files) - { - const fs::path dest_path = destination_folder / src_path.filename(); - fs.copy(src_path, dest_path, fs::copy_options::overwrite_existing); - } - } - - static void place_library_files_in(Files::Filesystem& fs, - const fs::path& include_directory, - const fs::path& project_directory, - const fs::path& destination_path) - { - check_is_directory(VCPKG_LINE_INFO, fs, include_directory); - check_is_directory(VCPKG_LINE_INFO, fs, project_directory); - check_is_directory(VCPKG_LINE_INFO, fs, destination_path); - const Binaries debug_binaries = find_binaries_in_dir(fs, project_directory / "Debug"); - const Binaries release_binaries = find_binaries_in_dir(fs, project_directory / "Release"); - - const fs::path destination_include_directory = destination_path / "include"; - fs.copy(include_directory, - destination_include_directory, - fs::copy_options::recursive | fs::copy_options::overwrite_existing); - - copy_files_into_directory(fs, release_binaries.dlls, destination_path / "bin"); - copy_files_into_directory(fs, release_binaries.libs, destination_path / "lib"); - - std::error_code ec; - fs.create_directory(destination_path / "debug", ec); - copy_files_into_directory(fs, debug_binaries.dlls, destination_path / "debug" / "bin"); - copy_files_into_directory(fs, debug_binaries.libs, destination_path / "debug" / "lib"); - } - - static void do_import(const VcpkgPaths& paths, - const fs::path& include_directory, - const fs::path& project_directory, - const BinaryParagraph& control_file_data) - { - auto& fs = paths.get_filesystem(); - const fs::path library_destination_path = paths.package_dir(control_file_data.spec); - std::error_code ec; - fs.create_directory(library_destination_path, ec); - place_library_files_in(paths.get_filesystem(), include_directory, project_directory, library_destination_path); - - const fs::path control_file_path = library_destination_path / "CONTROL"; - fs.write_contents(control_file_path, Strings::serialize(control_file_data)); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Commands::Help::create_example_string( - R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"); - args.check_exact_arg_count(3, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - const fs::path control_file_path(args.command_arguments[0]); - const fs::path include_directory(args.command_arguments[1]); - const fs::path project_directory(args.command_arguments[2]); - - const Expected> pghs = - Paragraphs::get_single_paragraph(paths.get_filesystem(), control_file_path); - Checks::check_exit(VCPKG_LINE_INFO, - pghs.get() != nullptr, - "Invalid control file %s for package", - control_file_path.generic_string()); - - StatusParagraph spgh; - spgh.package = BinaryParagraph(*pghs.get()); - auto& control_file_data = spgh.package; - - do_import(paths, include_directory, project_directory, control_file_data); - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp deleted file mode 100644 index d815332fe..000000000 --- a/toolsrc/src/commands_install.cpp +++ /dev/null @@ -1,613 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "metrics.h" -#include "vcpkg_Build.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Files.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Install -{ - using namespace Dependencies; - - InstallDir InstallDir::from_destination_root(const fs::path& destination_root, - const std::string& destination_subdirectory, - const fs::path& listfile) - { - InstallDir dirs; - dirs.m_destination = destination_root / destination_subdirectory; - dirs.m_destination_subdirectory = destination_subdirectory; - dirs.m_listfile = listfile; - return dirs; - } - - const fs::path& InstallDir::destination() const { return this->m_destination; } - - const std::string& InstallDir::destination_subdirectory() const { return this->m_destination_subdirectory; } - - const fs::path& InstallDir::listfile() const { return this->m_listfile; } - - void install_files_and_write_listfile(Files::Filesystem& fs, - const fs::path& source_dir, - const InstallDir& destination_dir) - { - std::vector output; - std::error_code ec; - - const size_t prefix_length = source_dir.native().size(); - const fs::path& destination = destination_dir.destination(); - const std::string& destination_subdirectory = destination_dir.destination_subdirectory(); - const fs::path& listfile = destination_dir.listfile(); - - Checks::check_exit( - VCPKG_LINE_INFO, fs.exists(source_dir), "Source directory %s does not exist", source_dir.generic_string()); - fs.create_directories(destination, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create destination directory %s", destination.generic_string()); - const fs::path listfile_parent = listfile.parent_path(); - fs.create_directories(listfile_parent, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !ec, "Could not create directory for listfile %s", listfile.generic_string()); - - output.push_back(Strings::format(R"(%s/)", destination_subdirectory)); - auto files = fs.get_files_recursive(source_dir); - for (auto&& file : files) - { - const auto status = fs.status(file, ec); - if (ec) - { - System::println(System::Color::error, "failed: %s: %s", file.u8string(), ec.message()); - continue; - } - - const std::string filename = file.filename().generic_string(); - if (fs::is_regular_file(status) && - (Strings::case_insensitive_ascii_compare(filename.c_str(), "CONTROL") == 0 || - Strings::case_insensitive_ascii_compare(filename.c_str(), "BUILD_INFO") == 0)) - { - // Do not copy the control file - continue; - } - - const std::string suffix = file.generic_u8string().substr(prefix_length + 1); - const fs::path target = destination / suffix; - - if (fs::is_directory(status)) - { - fs.create_directory(target, ec); - if (ec) - { - System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message()); - } - - // Trailing backslash for directories - output.push_back(Strings::format(R"(%s/%s/)", destination_subdirectory, suffix)); - continue; - } - - if (fs::is_regular_file(status)) - { - if (fs.exists(target)) - { - System::println(System::Color::warning, - "File %s was already present and will be overwritten", - target.u8string(), - ec.message()); - } - fs.copy_file(file, target, fs::copy_options::overwrite_existing, ec); - if (ec) - { - System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message()); - } - output.push_back(Strings::format(R"(%s/%s)", destination_subdirectory, suffix)); - continue; - } - - if (!fs::status_known(status)) - { - System::println(System::Color::error, "failed: %s: unknown status", file.u8string()); - continue; - } - - System::println(System::Color::error, "failed: %s: cannot handle file type", file.u8string()); - } - - std::sort(output.begin(), output.end()); - - fs.write_lines(listfile, output); - } - - static void remove_first_n_chars(std::vector* strings, const size_t n) - { - for (std::string& s : *strings) - { - s.erase(0, n); - } - }; - - static std::vector extract_files_in_triplet( - const std::vector& pgh_and_files, const Triplet& triplet) - { - std::vector output; - for (const StatusParagraphAndAssociatedFiles& t : pgh_and_files) - { - if (t.pgh.package.spec.triplet() != triplet) - { - continue; - } - - output.insert(output.end(), t.files.begin(), t.files.end()); - } - - std::sort(output.begin(), output.end()); - return output; - } - - static SortedVector build_list_of_package_files(const Files::Filesystem& fs, - const fs::path& package_dir) - { - const std::vector package_file_paths = fs.get_files_recursive(package_dir); - const size_t package_remove_char_count = package_dir.generic_string().size() + 1; // +1 for the slash - auto package_files = Util::fmap(package_file_paths, [package_remove_char_count](const fs::path& path) { - std::string as_string = path.generic_string(); - as_string.erase(0, package_remove_char_count); - return std::move(as_string); - }); - - return SortedVector(std::move(package_files)); - } - - static SortedVector build_list_of_installed_files( - const std::vector& pgh_and_files, const Triplet& triplet) - { - std::vector installed_files = extract_files_in_triplet(pgh_and_files, triplet); - const size_t installed_remove_char_count = triplet.canonical_name().size() + 1; // +1 for the slash - remove_first_n_chars(&installed_files, installed_remove_char_count); - - return SortedVector(std::move(installed_files)); - } - - InstallResult install_package(const VcpkgPaths& paths, const BinaryControlFile& bcf, StatusParagraphs* status_db) - { - const fs::path package_dir = paths.package_dir(bcf.core_paragraph.spec); - const Triplet& triplet = bcf.core_paragraph.spec.triplet(); - const std::vector pgh_and_files = get_installed_files(paths, *status_db); - - const SortedVector package_files = - build_list_of_package_files(paths.get_filesystem(), package_dir); - const SortedVector installed_files = build_list_of_installed_files(pgh_and_files, triplet); - - std::vector intersection; - std::set_intersection(package_files.begin(), - package_files.end(), - installed_files.begin(), - installed_files.end(), - std::back_inserter(intersection)); - - if (!intersection.empty()) - { - const fs::path triplet_install_path = paths.installed / triplet.canonical_name(); - System::println(System::Color::error, - "The following files are already installed in %s and are in conflict with %s", - triplet_install_path.generic_string(), - bcf.core_paragraph.spec); - System::print("\n "); - System::println(Strings::join("\n ", intersection)); - System::println(); - return InstallResult::FILE_CONFLICTS; - } - - StatusParagraph source_paragraph; - source_paragraph.package = bcf.core_paragraph; - source_paragraph.want = Want::INSTALL; - source_paragraph.state = InstallState::HALF_INSTALLED; - - write_update(paths, source_paragraph); - status_db->insert(std::make_unique(source_paragraph)); - - std::vector features_spghs; - for (auto&& feature : bcf.features) - { - features_spghs.emplace_back(); - - StatusParagraph& feature_paragraph = features_spghs.back(); - feature_paragraph.package = feature; - feature_paragraph.want = Want::INSTALL; - feature_paragraph.state = InstallState::HALF_INSTALLED; - - write_update(paths, feature_paragraph); - status_db->insert(std::make_unique(feature_paragraph)); - } - - const InstallDir install_dir = InstallDir::from_destination_root( - paths.installed, triplet.to_string(), paths.listfile_path(bcf.core_paragraph)); - - install_files_and_write_listfile(paths.get_filesystem(), package_dir, install_dir); - - source_paragraph.state = InstallState::INSTALLED; - write_update(paths, source_paragraph); - status_db->insert(std::make_unique(source_paragraph)); - - for (auto&& feature_paragraph : features_spghs) - { - feature_paragraph.state = InstallState::INSTALLED; - write_update(paths, feature_paragraph); - status_db->insert(std::make_unique(feature_paragraph)); - } - - return InstallResult::SUCCESS; - } - - using Build::BuildResult; - - BuildResult perform_install_plan_action(const VcpkgPaths& paths, - const InstallPlanAction& action, - const Build::BuildPackageOptions& build_package_options, - StatusParagraphs& status_db) - { - const InstallPlanType& plan_type = action.plan_type; - const std::string display_name = action.spec.to_string(); - const std::string display_name_with_features = - GlobalState::feature_packages ? action.displayname() : display_name; - - const bool is_user_requested = action.request_type == RequestType::USER_REQUESTED; - const bool use_head_version = to_bool(build_package_options.use_head_version); - - if (plan_type == InstallPlanType::ALREADY_INSTALLED) - { - if (use_head_version && is_user_requested) - System::println( - System::Color::warning, "Package %s is already installed -- not building from HEAD", display_name); - else - System::println(System::Color::success, "Package %s is already installed", display_name); - return BuildResult::SUCCEEDED; - } - - if (plan_type == InstallPlanType::BUILD_AND_INSTALL) - { - if (use_head_version) - System::println("Building package %s from HEAD... ", display_name_with_features); - else - System::println("Building package %s... ", display_name_with_features); - - const auto result = [&]() -> Build::ExtendedBuildResult { - if (GlobalState::feature_packages) - { - const Build::BuildPackageConfig build_config{ - *action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO), - action.spec.triplet(), - paths.port_dir(action.spec), - build_package_options, - action.feature_list}; - return Build::build_package(paths, build_config, status_db); - } - else - { - const Build::BuildPackageConfig build_config{ - action.any_paragraph.source_paragraph.value_or_exit(VCPKG_LINE_INFO), - action.spec.triplet(), - paths.port_dir(action.spec), - build_package_options}; - return Build::build_package(paths, build_config, status_db); - } - }(); - - if (result.code != Build::BuildResult::SUCCEEDED) - { - System::println(System::Color::error, Build::create_error_message(result.code, action.spec)); - return result.code; - } - - System::println("Building package %s... done", display_name_with_features); - - const BinaryControlFile bcf = - Paragraphs::try_load_cached_control_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO); - System::println("Installing package %s... ", display_name_with_features); - const auto install_result = install_package(paths, bcf, &status_db); - switch (install_result) - { - case InstallResult::SUCCESS: - System::println(System::Color::success, "Installing package %s... done", display_name); - return BuildResult::SUCCEEDED; - case InstallResult::FILE_CONFLICTS: return BuildResult::FILE_CONFLICTS; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - if (plan_type == InstallPlanType::INSTALL) - { - if (use_head_version && is_user_requested) - { - System::println( - System::Color::warning, "Package %s is already built -- not building from HEAD", display_name); - } - System::println("Installing package %s... ", display_name); - const auto install_result = install_package( - paths, action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO), &status_db); - switch (install_result) - { - case InstallResult::SUCCESS: - System::println(System::Color::success, "Installing package %s... done", display_name); - return BuildResult::SUCCEEDED; - case InstallResult::FILE_CONFLICTS: return BuildResult::FILE_CONFLICTS; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - Checks::unreachable(VCPKG_LINE_INFO); - } - - static void print_plan(const std::vector& action_plan, bool is_recursive) - { - std::vector remove_plans; - std::vector rebuilt_plans; - std::vector only_install_plans; - std::vector new_plans; - std::vector already_installed_plans; - - const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { - if (auto iplan = package.install_plan.get()) - return iplan->request_type != RequestType::USER_REQUESTED; - else - return false; - }) != action_plan.cend(); - - for (auto&& action : action_plan) - { - if (auto install_action = action.install_plan.get()) - { - // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at - // all. - auto it = Util::find_if( - remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; }); - if (it != remove_plans.end()) - { - rebuilt_plans.emplace_back(install_action); - } - else - { - switch (install_action->plan_type) - { - case InstallPlanType::INSTALL: only_install_plans.emplace_back(install_action); break; - case InstallPlanType::ALREADY_INSTALLED: - if (install_action->request_type == RequestType::USER_REQUESTED) - already_installed_plans.emplace_back(install_action); - break; - case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - } - else if (auto remove_action = action.remove_plan.get()) - { - remove_plans.emplace_back(remove_action); - } - } - - std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name); - std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); - - if (already_installed_plans.size() > 0) - { - const std::string already_string = - Strings::join("\n", already_installed_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages are already installed:\n%s", already_string); - } - - if (rebuilt_plans.size() > 0) - { - const std::string rebuilt_string = Strings::join("\n", rebuilt_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages will be rebuilt:\n%s", rebuilt_string); - } - - if (new_plans.size() > 0) - { - const std::string new_string = Strings::join("\n", new_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages will be built and installed:\n%s", new_string); - } - - if (only_install_plans.size() > 0) - { - const std::string only_install_string = - Strings::join("\n", only_install_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages will be directly installed:\n%s", only_install_string); - } - - if (has_non_user_requested_packages) - System::println("Additional packages (*) will be installed to complete this operation."); - - if (remove_plans.size() > 0 && !is_recursive) - { - System::println(System::Color::warning, - "If you are sure you want to rebuild the above packages, run the command with the " - "--recurse option"); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - void perform_and_exit(const std::vector& action_plan, - const Build::BuildPackageOptions& install_plan_options, - const KeepGoing keep_going, - const PrintSummary print_summary, - const VcpkgPaths& paths, - StatusParagraphs& status_db) - { - std::vector results; - std::vector timing; - const ElapsedTime timer = ElapsedTime::create_started(); - size_t counter = 0; - const size_t package_count = action_plan.size(); - - for (const auto& action : action_plan) - { - const ElapsedTime build_timer = ElapsedTime::create_started(); - counter++; - - const std::string display_name = action.spec().to_string(); - System::println("Starting package %d/%d: %s", counter, package_count, display_name); - - timing.push_back("0"); - results.push_back(BuildResult::NULLVALUE); - - if (const auto install_action = action.install_plan.get()) - { - const BuildResult result = - perform_install_plan_action(paths, *install_action, install_plan_options, status_db); - if (result != BuildResult::SUCCEEDED && keep_going == KeepGoing::NO) - { - System::println(Build::create_user_troubleshooting_message(install_action->spec)); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - results.back() = result; - } - else if (const auto remove_action = action.remove_plan.get()) - { - Checks::check_exit(VCPKG_LINE_INFO, GlobalState::feature_packages); - Remove::perform_remove_plan_action(paths, *remove_action, Remove::Purge::YES, status_db); - } - else - { - Checks::unreachable(VCPKG_LINE_INFO); - } - - timing.back() = build_timer.to_string(); - System::println("Elapsed time for package %s: %s", display_name, build_timer.to_string()); - } - - System::println("Total time taken: %s", timer.to_string()); - - if (print_summary == PrintSummary::YES) - { - for (size_t i = 0; i < results.size(); i++) - { - System::println("%s: %s: %s", action_plan[i].spec(), Build::to_string(results[i]), timing[i]); - } - - std::map summary; - for (const BuildResult& v : Build::BUILD_RESULT_VALUES) - { - summary[v] = 0; - } - - for (const BuildResult& r : results) - { - summary[r]++; - } - - System::println("\n\nSUMMARY"); - for (const std::pair& entry : summary) - { - System::println(" %s: %d", Build::to_string(entry.first), entry.second); - } - } - - Checks::exit_success(VCPKG_LINE_INFO); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string OPTION_DRY_RUN = "--dry-run"; - static const std::string OPTION_USE_HEAD_VERSION = "--head"; - static const std::string OPTION_NO_DOWNLOADS = "--no-downloads"; - static const std::string OPTION_RECURSE = "--recurse"; - static const std::string OPTION_KEEP_GOING = "--keep-going"; - - // input sanitization - static const std::string EXAMPLE = - Commands::Help::create_example_string("install zlib zlib:x64-windows curl boost"); - args.check_min_arg_count(1, EXAMPLE); - - const std::vector specs = Util::fmap(args.command_arguments, [&](auto&& arg) { - return Input::check_and_get_full_package_spec(arg, default_triplet, EXAMPLE); - }); - - for (auto&& spec : specs) - { - Input::check_triplet(spec.package_spec.triplet(), paths); - if (!spec.features.empty() && !GlobalState::feature_packages) - { - Checks::exit_with_message( - VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag."); - } - } - - const std::unordered_set options = args.check_and_get_optional_command_arguments( - {OPTION_DRY_RUN, OPTION_USE_HEAD_VERSION, OPTION_NO_DOWNLOADS, OPTION_RECURSE, OPTION_KEEP_GOING}); - const bool dry_run = options.find(OPTION_DRY_RUN) != options.cend(); - const bool use_head_version = options.find(OPTION_USE_HEAD_VERSION) != options.cend(); - const bool no_downloads = options.find(OPTION_NO_DOWNLOADS) != options.cend(); - const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); - const KeepGoing keep_going = to_keep_going(options.find(OPTION_KEEP_GOING) != options.cend()); - - // create the plan - StatusParagraphs status_db = database_load_check(paths); - - const Build::BuildPackageOptions install_plan_options = {Build::to_use_head_version(use_head_version), - Build::to_allow_downloads(!no_downloads)}; - - std::vector action_plan; - - if (GlobalState::feature_packages) - { - std::unordered_map scf_map; - auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); - for (auto&& port : all_ports) - { - scf_map[port->core_paragraph->name] = std::move(*port); - } - action_plan = create_feature_install_plan(scf_map, FullPackageSpec::to_feature_specs(specs), status_db); - } - else - { - Dependencies::PathsPortFile paths_port_file(paths); - auto install_plan = Dependencies::create_install_plan( - paths_port_file, Util::fmap(specs, [](auto&& spec) { return spec.package_spec; }), status_db); - - action_plan = Util::fmap( - install_plan, [](InstallPlanAction& install_action) { return AnyAction(std::move(install_action)); }); - } - - // install plan will be empty if it is already installed - need to change this at status paragraph part - Checks::check_exit(VCPKG_LINE_INFO, !action_plan.empty(), "Install plan cannot be empty"); - - // log the plan - const std::string specs_string = Strings::join(",", action_plan, [](const AnyAction& action) { - if (auto iaction = action.install_plan.get()) - return iaction->spec.to_string(); - else if (auto raction = action.remove_plan.get()) - return "R$" + raction->spec.to_string(); - Checks::unreachable(VCPKG_LINE_INFO); - }); - - Metrics::g_metrics.lock()->track_property("installplan", specs_string); - - print_plan(action_plan, is_recursive); - - if (dry_run) - { - Checks::exit_success(VCPKG_LINE_INFO); - } - - perform_and_exit(action_plan, install_plan_options, keep_going, PrintSummary::NO, paths, status_db); - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_integrate.cpp b/toolsrc/src/commands_integrate.cpp deleted file mode 100644 index fd2f11294..000000000 --- a/toolsrc/src/commands_integrate.cpp +++ /dev/null @@ -1,342 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Checks.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Files.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" - -namespace vcpkg::Commands::Integrate -{ - static const std::array OLD_SYSTEM_TARGET_FILES = { - System::get_program_files_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets", - System::get_program_files_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets"}; - static const fs::path SYSTEM_WIDE_TARGETS_FILE = - System::get_program_files_32_bit() / "MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props"; - - static std::string create_appdata_targets_shortcut(const std::string& target_path) noexcept - { - return Strings::format(R"###( - - - -)###", - target_path, - target_path); - } - - static std::string create_system_targets_shortcut() noexcept - { - return R"###( - - - - $(LOCALAPPDATA)\vcpkg\vcpkg.user - - - -)###"; - } - - static std::string create_nuget_targets_file_contents(const fs::path& msbuild_vcpkg_targets_file) noexcept - { - const std::string as_string = msbuild_vcpkg_targets_file.string(); - - return Strings::format(R"###( - - - - - - -)###", - as_string, - as_string); - } - - static std::string create_nuget_props_file_contents() noexcept - { - return R"###( - - - true - - -)###"; - } - - static std::string get_nuget_id(const fs::path& vcpkg_root_dir) - { - std::string dir_id = vcpkg_root_dir.generic_string(); - std::replace(dir_id.begin(), dir_id.end(), '/', '.'); - dir_id.erase(1, 1); // Erasing the ":" - - // NuGet id cannot have invalid characters. We will only use alphanumeric and dot. - Util::erase_remove_if(dir_id, [](char c) { return !isalnum(c) && (c != '.'); }); - - const std::string nuget_id = "vcpkg." + dir_id; - return nuget_id; - } - - static std::string create_nuspec_file_contents(const fs::path& vcpkg_root_dir, - const std::string& nuget_id, - const std::string& nupkg_version) - { - static constexpr auto CONTENT_TEMPLATE = R"( - - - @NUGET_ID@ - @VERSION@ - vcpkg - - This package imports all libraries currently installed in @VCPKG_DIR@. This package does not contain any libraries and instead refers to the folder directly (like a symlink). - - - - - - - -)"; - - std::string content = std::regex_replace(CONTENT_TEMPLATE, std::regex("@NUGET_ID@"), nuget_id); - content = std::regex_replace(content, std::regex("@VCPKG_DIR@"), vcpkg_root_dir.string()); - content = std::regex_replace(content, std::regex("@VERSION@"), nupkg_version); - return content; - } - - enum class ElevationPromptChoice - { - YES, - NO - }; - - static ElevationPromptChoice elevated_cmd_execute(const std::string& param) - { - SHELLEXECUTEINFOW sh_ex_info = {0}; - sh_ex_info.cbSize = sizeof(sh_ex_info); - sh_ex_info.fMask = SEE_MASK_NOCLOSEPROCESS; - sh_ex_info.hwnd = nullptr; - sh_ex_info.lpVerb = L"runas"; - sh_ex_info.lpFile = L"cmd"; // Application to start - - auto wparam = Strings::to_utf16(param); - sh_ex_info.lpParameters = wparam.c_str(); // Additional parameters - sh_ex_info.lpDirectory = nullptr; - sh_ex_info.nShow = SW_HIDE; - sh_ex_info.hInstApp = nullptr; - - if (!ShellExecuteExW(&sh_ex_info)) - { - return ElevationPromptChoice::NO; - } - if (sh_ex_info.hProcess == nullptr) - { - return ElevationPromptChoice::NO; - } - WaitForSingleObject(sh_ex_info.hProcess, INFINITE); - CloseHandle(sh_ex_info.hProcess); - return ElevationPromptChoice::YES; - } - - static fs::path get_appdata_targets_path() - { - static const fs::path LOCAL_APP_DATA = - fs::path(System::get_environment_variable(L"LOCALAPPDATA").value_or_exit(VCPKG_LINE_INFO)); - return LOCAL_APP_DATA / "vcpkg" / "vcpkg.user.targets"; - } - - static void integrate_install(const VcpkgPaths& paths) - { - auto& fs = paths.get_filesystem(); - - // TODO: This block of code should eventually be removed - for (auto&& old_system_wide_targets_file : OLD_SYSTEM_TARGET_FILES) - { - if (fs.exists(old_system_wide_targets_file)) - { - const std::string param = - Strings::format(R"(/c DEL "%s" /Q > nul)", old_system_wide_targets_file.string()); - const ElevationPromptChoice user_choice = elevated_cmd_execute(param); - switch (user_choice) - { - case ElevationPromptChoice::YES: break; - case ElevationPromptChoice::NO: - System::println(System::Color::warning, "Warning: Previous integration file was not removed"); - Checks::exit_fail(VCPKG_LINE_INFO); - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - } - - std::error_code ec; - const fs::path tmp_dir = paths.buildsystems / "tmp"; - fs.create_directory(paths.buildsystems, ec); - fs.create_directory(tmp_dir, ec); - - bool should_install_system = true; - const Expected system_wide_file_contents = fs.read_contents(SYSTEM_WIDE_TARGETS_FILE); - static const std::regex RE(R"###()###"); - if (const auto contents_data = system_wide_file_contents.get()) - { - std::match_results match; - const auto found = std::regex_search(*contents_data, match, RE); - if (found) - { - const int ver = atoi(match[1].str().c_str()); - if (ver >= 1) should_install_system = false; - } - } - - if (should_install_system) - { - const fs::path sys_src_path = tmp_dir / "vcpkg.system.targets"; - fs.write_contents(sys_src_path, create_system_targets_shortcut()); - - const std::string param = Strings::format(R"(/c mkdir "%s" & copy "%s" "%s" /Y > nul)", - SYSTEM_WIDE_TARGETS_FILE.parent_path().string(), - sys_src_path.string(), - SYSTEM_WIDE_TARGETS_FILE.string()); - const ElevationPromptChoice user_choice = elevated_cmd_execute(param); - switch (user_choice) - { - case ElevationPromptChoice::YES: break; - case ElevationPromptChoice::NO: - System::println(System::Color::warning, "Warning: integration was not applied"); - Checks::exit_fail(VCPKG_LINE_INFO); - default: Checks::unreachable(VCPKG_LINE_INFO); - } - - Checks::check_exit(VCPKG_LINE_INFO, - fs.exists(SYSTEM_WIDE_TARGETS_FILE), - "Error: failed to copy targets file to %s", - SYSTEM_WIDE_TARGETS_FILE.string()); - } - - const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets"; - fs.write_contents(appdata_src_path, - create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string())); - auto appdata_dst_path = get_appdata_targets_path(); - - const auto rc = fs.copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing, ec); - - if (!rc || ec) - { - System::println(System::Color::error, - "Error: Failed to copy file: %s -> %s", - appdata_src_path.string(), - appdata_dst_path.string()); - Checks::exit_fail(VCPKG_LINE_INFO); - } - System::println(System::Color::success, "Applied user-wide integration for this vcpkg root."); - const fs::path cmake_toolchain = paths.buildsystems / "vcpkg.cmake"; - System::println("\n" - "All MSBuild C++ projects can now #include any installed libraries.\n" - "Linking will be handled automatically.\n" - "Installing new libraries will make them instantly available.\n" - "\n" - "CMake projects should use -DCMAKE_TOOLCHAIN_FILE=%s", - cmake_toolchain.generic_string()); - - Checks::exit_success(VCPKG_LINE_INFO); - } - - static void integrate_remove(Files::Filesystem& fs) - { - const fs::path path = get_appdata_targets_path(); - - std::error_code ec; - const bool was_deleted = fs.remove(path, ec); - - Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %d", ec.message()); - - if (was_deleted) - { - System::println(System::Color::success, "User-wide integration was removed"); - } - else - { - System::println(System::Color::success, "User-wide integration is not installed"); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } - - static void integrate_project(const VcpkgPaths& paths) - { - auto& fs = paths.get_filesystem(); - - const fs::path& nuget_exe = paths.get_nuget_exe(); - - const fs::path& buildsystems_dir = paths.buildsystems; - const fs::path tmp_dir = buildsystems_dir / "tmp"; - std::error_code ec; - fs.create_directory(buildsystems_dir, ec); - fs.create_directory(tmp_dir, ec); - - const fs::path targets_file_path = tmp_dir / "vcpkg.nuget.targets"; - const fs::path props_file_path = tmp_dir / "vcpkg.nuget.props"; - const fs::path nuspec_file_path = tmp_dir / "vcpkg.nuget.nuspec"; - const std::string nuget_id = get_nuget_id(paths.root); - const std::string nupkg_version = "1.0.0"; - - fs.write_contents(targets_file_path, create_nuget_targets_file_contents(paths.buildsystems_msbuild_targets)); - fs.write_contents(props_file_path, create_nuget_props_file_contents()); - fs.write_contents(nuspec_file_path, create_nuspec_file_contents(paths.root, nuget_id, nupkg_version)); - - // Using all forward slashes for the command line - const std::wstring cmd_line = Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" > nul)", - nuget_exe.native(), - buildsystems_dir.native(), - nuspec_file_path.native()); - - const int exit_code = System::cmd_execute_clean(cmd_line); - - const fs::path nuget_package = buildsystems_dir / Strings::format("%s.%s.nupkg", nuget_id, nupkg_version); - Checks::check_exit( - VCPKG_LINE_INFO, exit_code == 0 && fs.exists(nuget_package), "Error: NuGet package creation failed"); - System::println(System::Color::success, "Created nupkg: %s", nuget_package.string()); - - auto source_path = buildsystems_dir.u8string(); - source_path = std::regex_replace(source_path, std::regex("`"), "``"); - - System::println(R"( -With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: - Install-Package %s -Source "%s" -)", - nuget_id, - source_path); - - Checks::exit_success(VCPKG_LINE_INFO); - } - - const char* const INTEGRATE_COMMAND_HELPSTRING = - " vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on " - "first use\n" - " vcpkg integrate remove Remove user-wide integration\n" - " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n"; - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Strings::format("Commands:\n" - "%s", - INTEGRATE_COMMAND_HELPSTRING); - args.check_exact_arg_count(1, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - if (args.command_arguments[0] == "install") - { - return integrate_install(paths); - } - if (args.command_arguments[0] == "remove") - { - return integrate_remove(paths.get_filesystem()); - } - if (args.command_arguments[0] == "project") - { - return integrate_project(paths); - } - - Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown parameter %s for integrate", args.command_arguments[0]); - } -} diff --git a/toolsrc/src/commands_list.cpp b/toolsrc/src/commands_list.cpp deleted file mode 100644 index 640885860..000000000 --- a/toolsrc/src/commands_list.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::List -{ - static const std::string OPTION_FULLDESC = "--x-full-desc"; // TODO: This should find a better home, eventually - - static void do_print(const StatusParagraph& pgh, bool full_desc) - { - if (full_desc) - { - System::println("%-30s %-16s %s", pgh.package.displayname(), pgh.package.version, pgh.package.description); - } - else - { - System::println("%-30s %-16s %s", - vcpkg::shorten_text(pgh.package.displayname(), 30), - vcpkg::shorten_text(pgh.package.version, 16), - vcpkg::shorten_text(pgh.package.description, 71)); - } - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Strings::format( - "The argument should be a substring to search for, or no argument to display all installed libraries.\n%s", - Commands::Help::create_example_string("list png")); - args.check_max_arg_count(1, EXAMPLE); - const std::unordered_set options = - args.check_and_get_optional_command_arguments({OPTION_FULLDESC}); - - const StatusParagraphs status_paragraphs = database_load_check(paths); - std::vector installed_packages = get_installed_ports(status_paragraphs); - - if (installed_packages.empty()) - { - System::println("No packages are installed. Did you mean `search`?"); - Checks::exit_success(VCPKG_LINE_INFO); - } - - std::sort(installed_packages.begin(), - installed_packages.end(), - [](const StatusParagraph* lhs, const StatusParagraph* rhs) -> bool { - return lhs->package.displayname() < rhs->package.displayname(); - }); - - if (args.command_arguments.size() == 0) - { - for (const StatusParagraph* status_paragraph : installed_packages) - { - do_print(*status_paragraph, options.find(OPTION_FULLDESC) != options.cend()); - } - } - else - { - // At this point there is 1 argument - for (const StatusParagraph* status_paragraph : installed_packages) - { - const std::string displayname = status_paragraph->package.displayname(); - if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end()) - { - continue; - } - - do_print(*status_paragraph, options.find(OPTION_FULLDESC) != options.cend()); - } - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_owns.cpp b/toolsrc/src/commands_owns.cpp deleted file mode 100644 index 718a0277f..000000000 --- a/toolsrc/src/commands_owns.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Owns -{ - static void search_file(const VcpkgPaths& paths, const std::string& file_substr, const StatusParagraphs& status_db) - { - const std::vector installed_files = get_installed_files(paths, status_db); - for (const StatusParagraphAndAssociatedFiles& pgh_and_file : installed_files) - { - const StatusParagraph& pgh = pgh_and_file.pgh; - - for (const std::string& file : pgh_and_file.files) - { - if (file.find(file_substr) != std::string::npos) - { - System::println("%s: %s", pgh.package.displayname(), file); - } - } - } - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Strings::format("The argument should be a pattern to search for. %s", - Commands::Help::create_example_string("owns zlib.dll")); - args.check_exact_arg_count(1, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - StatusParagraphs status_db = database_load_check(paths); - search_file(paths, args.command_arguments[0], status_db); - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_portsdiff.cpp b/toolsrc/src/commands_portsdiff.cpp deleted file mode 100644 index 2334b2270..000000000 --- a/toolsrc/src/commands_portsdiff.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "SortedVector.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Maps.h" -#include "vcpkg_System.h" - -namespace vcpkg::Commands::PortsDiff -{ - struct UpdatedPort - { - static bool compare_by_name(const UpdatedPort& left, const UpdatedPort& right) - { - return left.port < right.port; - } - - std::string port; - VersionDiff version_diff; - }; - - template - struct SetElementPresence - { - static SetElementPresence create(std::vector left, std::vector right) - { - // TODO: This can be done with one pass instead of three passes - SetElementPresence output; - std::set_difference( - left.cbegin(), left.cend(), right.cbegin(), right.cend(), std::back_inserter(output.only_left)); - std::set_intersection( - left.cbegin(), left.cend(), right.cbegin(), right.cend(), std::back_inserter(output.both)); - std::set_difference( - right.cbegin(), right.cend(), left.cbegin(), left.cend(), std::back_inserter(output.only_right)); - - return output; - } - - std::vector only_left; - std::vector both; - std::vector only_right; - }; - - static std::vector find_updated_ports( - const std::vector& ports, - const std::map& previous_names_and_versions, - const std::map& current_names_and_versions) - { - std::vector output; - for (const std::string& name : ports) - { - const VersionT& previous_version = previous_names_and_versions.at(name); - const VersionT& current_version = current_names_and_versions.at(name); - if (previous_version == current_version) - { - continue; - } - - output.push_back({name, VersionDiff(previous_version, current_version)}); - } - - return output; - } - - static void do_print_name_and_version(const std::vector& ports_to_print, - const std::map& names_and_versions) - { - for (const std::string& name : ports_to_print) - { - const VersionT& version = names_and_versions.at(name); - System::println(" - %-14s %-16s", name, version); - } - } - - static std::map read_ports_from_commit(const VcpkgPaths& paths, - const std::wstring& git_commit_id) - { - std::error_code ec; - auto& fs = paths.get_filesystem(); - const fs::path& git_exe = paths.get_git_exe(); - const fs::path dot_git_dir = paths.root / ".git"; - const std::wstring ports_dir_name_as_string = paths.ports.filename().native(); - const fs::path temp_checkout_path = - paths.root / Strings::wformat(L"%s-%s", ports_dir_name_as_string, git_commit_id); - fs.create_directory(temp_checkout_path, ec); - const std::wstring checkout_this_dir = - Strings::wformat(LR"(.\%s)", ports_dir_name_as_string); // Must be relative to the root of the repository - - const std::wstring cmd = - Strings::wformat(LR"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & "%s" reset >NUL)", - git_exe.native(), - dot_git_dir.native(), - temp_checkout_path.native(), - git_commit_id, - checkout_this_dir, - L".vcpkg-root", - git_exe.native()); - System::cmd_execute_clean(cmd); - const std::map names_and_versions = Paragraphs::load_all_port_names_and_versions( - paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string); - fs.remove_all(temp_checkout_path, ec); - return names_and_versions; - } - - static void check_commit_exists(const fs::path& git_exe, const std::wstring& git_commit_id) - { - static const std::string VALID_COMMIT_OUTPUT = "commit\n"; - - const std::wstring cmd = Strings::wformat(LR"("%s" cat-file -t %s)", git_exe.native(), git_commit_id); - const System::ExitCodeAndOutput output = System::cmd_execute_and_capture_output(cmd); - Checks::check_exit(VCPKG_LINE_INFO, - output.output == VALID_COMMIT_OUTPUT, - "Invalid commit id %s", - Strings::to_utf8(git_commit_id)); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = - Strings::format("The argument should be a branch/tag/hash to checkout.\n%s", - Commands::Help::create_example_string("portsdiff mybranchname")); - args.check_min_arg_count(1, EXAMPLE); - args.check_max_arg_count(2, EXAMPLE); - args.check_and_get_optional_command_arguments({}); - - const fs::path& git_exe = paths.get_git_exe(); - - const std::wstring git_commit_id_for_previous_snapshot = Strings::to_utf16(args.command_arguments.at(0)); - const std::wstring git_commit_id_for_current_snapshot = - args.command_arguments.size() < 2 ? L"HEAD" : Strings::to_utf16(args.command_arguments.at(1)); - - check_commit_exists(git_exe, git_commit_id_for_current_snapshot); - check_commit_exists(git_exe, git_commit_id_for_previous_snapshot); - - const std::map current_names_and_versions = - read_ports_from_commit(paths, git_commit_id_for_current_snapshot); - const std::map previous_names_and_versions = - read_ports_from_commit(paths, git_commit_id_for_previous_snapshot); - - // Already sorted, so set_difference can work on std::vector too - const std::vector current_ports = Maps::extract_keys(current_names_and_versions); - const std::vector previous_ports = Maps::extract_keys(previous_names_and_versions); - - const SetElementPresence setp = - SetElementPresence::create(current_ports, previous_ports); - - const std::vector& added_ports = setp.only_left; - if (!added_ports.empty()) - { - System::println("\nThe following %d ports were added:", added_ports.size()); - do_print_name_and_version(added_ports, current_names_and_versions); - } - - const std::vector& removed_ports = setp.only_right; - if (!removed_ports.empty()) - { - System::println("\nThe following %d ports were removed:", removed_ports.size()); - do_print_name_and_version(removed_ports, previous_names_and_versions); - } - - const std::vector& common_ports = setp.both; - const std::vector updated_ports = - find_updated_ports(common_ports, previous_names_and_versions, current_names_and_versions); - - if (!updated_ports.empty()) - { - System::println("\nThe following %d ports were updated:", updated_ports.size()); - for (const UpdatedPort& p : updated_ports) - { - System::println(" - %-14s %-16s", p.port, p.version_diff.to_string()); - } - } - - if (added_ports.empty() && removed_ports.empty() && updated_ports.empty()) - { - System::println("There were no changes in the ports between the two commits."); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp deleted file mode 100644 index a9f1b2564..000000000 --- a/toolsrc/src/commands_remove.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Commands.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Remove -{ - using Dependencies::RemovePlanAction; - using Dependencies::RemovePlanType; - using Dependencies::RequestType; - using Update::OutdatedPackage; - - void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db) - { - auto& fs = paths.get_filesystem(); - auto spghs = status_db->find_all(spec.name(), spec.triplet()); - const auto core_pkg = **status_db->find(spec.name(), spec.triplet(), Strings::EMPTY); - - for (auto&& spgh : spghs) - { - StatusParagraph& pkg = **spgh; - if (pkg.state != InstallState::INSTALLED) continue; - pkg.want = Want::PURGE; - pkg.state = InstallState::HALF_INSTALLED; - write_update(paths, pkg); - } - - auto maybe_lines = fs.read_lines(paths.listfile_path(core_pkg.package)); - - if (const auto lines = maybe_lines.get()) - { - std::vector dirs_touched; - for (auto&& suffix : *lines) - { - if (!suffix.empty() && suffix.back() == '\r') suffix.pop_back(); - - std::error_code ec; - - auto target = paths.installed / suffix; - - const auto status = fs.status(target, ec); - if (ec) - { - System::println(System::Color::error, "failed: %s", ec.message()); - continue; - } - - if (fs::is_directory(status)) - { - dirs_touched.push_back(target); - } - else if (fs::is_regular_file(status)) - { - fs.remove(target, ec); - if (ec) - { - System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message()); - } - } - else if (!fs::status_known(status)) - { - System::println(System::Color::warning, "Warning: unknown status: %s", target.u8string()); - } - else - { - System::println(System::Color::warning, "Warning: %s: cannot handle file type", target.u8string()); - } - } - - auto b = dirs_touched.rbegin(); - const auto e = dirs_touched.rend(); - for (; b != e; ++b) - { - if (fs.is_empty(*b)) - { - std::error_code ec; - fs.remove(*b, ec); - if (ec) - { - System::println(System::Color::error, "failed: %s", ec.message()); - } - } - } - - fs.remove(paths.listfile_path(core_pkg.package)); - } - - for (auto&& spgh : spghs) - { - StatusParagraph& pkg = **spgh; - if (pkg.state != InstallState::HALF_INSTALLED) continue; - pkg.state = InstallState::NOT_INSTALLED; - write_update(paths, pkg); - } - } - - static void print_plan(const std::map>& group_by_plan_type) - { - static constexpr std::array ORDER = {RemovePlanType::NOT_INSTALLED, RemovePlanType::REMOVE}; - - for (const RemovePlanType plan_type : ORDER) - { - const auto it = group_by_plan_type.find(plan_type); - if (it == group_by_plan_type.cend()) - { - continue; - } - - std::vector cont = it->second; - std::sort(cont.begin(), cont.end(), &RemovePlanAction::compare_by_name); - const std::string as_string = Strings::join("\n", cont, [](const RemovePlanAction* p) { - return Dependencies::to_output_string(p->request_type, p->spec.to_string()); - }); - - switch (plan_type) - { - case RemovePlanType::NOT_INSTALLED: - System::println("The following packages are not installed, so not removed:\n%s", as_string); - continue; - case RemovePlanType::REMOVE: - System::println("The following packages will be removed:\n%s", as_string); - continue; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - } - - void perform_remove_plan_action(const VcpkgPaths& paths, - const RemovePlanAction& action, - const Purge purge, - StatusParagraphs& status_db) - { - const std::string display_name = action.spec.to_string(); - - switch (action.plan_type) - { - case RemovePlanType::NOT_INSTALLED: - System::println(System::Color::success, "Package %s is not installed", display_name); - break; - case RemovePlanType::REMOVE: - System::println("Removing package %s... ", display_name); - remove_package(paths, action.spec, &status_db); - System::println(System::Color::success, "Removing package %s... done", display_name); - break; - case RemovePlanType::UNKNOWN: - default: Checks::unreachable(VCPKG_LINE_INFO); - } - - if (purge == Purge::YES) - { - System::println("Purging package %s... ", display_name); - Files::Filesystem& fs = paths.get_filesystem(); - std::error_code ec; - fs.remove_all(paths.packages / action.spec.dir(), ec); - System::println(System::Color::success, "Purging package %s... done", display_name); - } - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string OPTION_PURGE = "--purge"; - static const std::string OPTION_NO_PURGE = "--no-purge"; - static const std::string OPTION_RECURSE = "--recurse"; - static const std::string OPTION_DRY_RUN = "--dry-run"; - static const std::string OPTION_OUTDATED = "--outdated"; - static const std::string EXAMPLE = - Commands::Help::create_example_string("remove zlib zlib:x64-windows curl boost"); - const std::unordered_set options = args.check_and_get_optional_command_arguments( - {OPTION_PURGE, OPTION_NO_PURGE, OPTION_RECURSE, OPTION_DRY_RUN, OPTION_OUTDATED}); - - StatusParagraphs status_db = database_load_check(paths); - std::vector specs; - if (options.find(OPTION_OUTDATED) != options.cend()) - { - args.check_exact_arg_count(0, EXAMPLE); - specs = Util::fmap(Update::find_outdated_packages(paths, status_db), - [](auto&& outdated) { return outdated.spec; }); - - if (specs.empty()) - { - System::println(System::Color::success, "There are no outdated packages."); - Checks::exit_success(VCPKG_LINE_INFO); - } - } - else - { - args.check_min_arg_count(1, EXAMPLE); - specs = Util::fmap(args.command_arguments, [&](auto&& arg) { - return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE); - }); - - for (auto&& spec : specs) - Input::check_triplet(spec.triplet(), paths); - } - - const bool no_purge_was_passed = options.find(OPTION_NO_PURGE) != options.end(); - const bool purge_was_passed = options.find(OPTION_PURGE) != options.end(); - if (purge_was_passed && no_purge_was_passed) - { - System::println(System::Color::error, "Error: cannot specify both --no-purge and --purge."); - System::print(EXAMPLE); - Checks::exit_fail(VCPKG_LINE_INFO); - } - const Purge purge = to_purge(purge_was_passed || !no_purge_was_passed); - const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); - const bool dry_run = options.find(OPTION_DRY_RUN) != options.cend(); - - const std::vector remove_plan = Dependencies::create_remove_plan(specs, status_db); - Checks::check_exit(VCPKG_LINE_INFO, !remove_plan.empty(), "Remove plan cannot be empty"); - - std::map> group_by_plan_type; - Util::group_by(remove_plan, &group_by_plan_type, [](const RemovePlanAction& p) { return p.plan_type; }); - print_plan(group_by_plan_type); - - const bool has_non_user_requested_packages = - Util::find_if(remove_plan, [](const RemovePlanAction& package) -> bool { - return package.request_type != RequestType::USER_REQUESTED; - }) != remove_plan.cend(); - - if (has_non_user_requested_packages) - { - System::println(System::Color::warning, - "Additional packages (*) need to be removed to complete this operation."); - - if (!is_recursive) - { - System::println(System::Color::warning, - "If you are sure you want to remove them, run the command with the --recurse option"); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - if (dry_run) - { - Checks::exit_success(VCPKG_LINE_INFO); - } - - for (const RemovePlanAction& action : remove_plan) - { - perform_remove_plan_action(paths, action, purge, status_db); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_search.cpp b/toolsrc/src/commands_search.cpp deleted file mode 100644 index d35a546c4..000000000 --- a/toolsrc/src/commands_search.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "SourceParagraph.h" -#include "vcpkg_Commands.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Search -{ - static const std::string OPTION_GRAPH = "--graph"; // TODO: This should find a better home, eventually - static const std::string OPTION_FULLDESC = "--x-full-desc"; // TODO: This should find a better home, eventually - - static std::string replace_dashes_with_underscore(const std::string& input) - { - std::string output = input; - std::replace(output.begin(), output.end(), '-', '_'); - return output; - } - - static std::string create_graph_as_string( - const std::vector>& source_control_files) - { - int empty_node_count = 0; - - std::string s; - s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;"); - - for (const auto& source_control_file : source_control_files) - { - const SourceParagraph& source_paragraph = *source_control_file->core_paragraph; - if (source_paragraph.depends.empty()) - { - empty_node_count++; - continue; - } - - const std::string name = replace_dashes_with_underscore(source_paragraph.name); - s.append(Strings::format("%s;", name)); - for (const Dependency& d : source_paragraph.depends) - { - const std::string dependency_name = replace_dashes_with_underscore(d.name()); - s.append(Strings::format("%s -> %s;", name, dependency_name)); - } - } - - s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count)); - return s; - } - static void do_print(const SourceParagraph& source_paragraph, bool full_desc) - { - if (full_desc) - { - System::println( - "%-20s %-16s %s", source_paragraph.name, source_paragraph.version, source_paragraph.description); - } - else - { - System::println("%-20s %-16s %s", - vcpkg::shorten_text(source_paragraph.name, 20), - vcpkg::shorten_text(source_paragraph.version, 16), - vcpkg::shorten_text(source_paragraph.description, 81)); - } - } - - static void do_print(const std::string& name, const FeatureParagraph& feature_paragraph, bool full_desc) - { - if (full_desc) - { - System::println("%-37s %s", name + "[" + feature_paragraph.name + "]", feature_paragraph.description); - } - else - { - System::println("%-37s %s", - vcpkg::shorten_text(name + "[" + feature_paragraph.name + "]", 37), - vcpkg::shorten_text(feature_paragraph.description, 81)); - } - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - static const std::string EXAMPLE = Strings::format( - "The argument should be a substring to search for, or no argument to display all libraries.\n%s", - Commands::Help::create_example_string("search png")); - args.check_max_arg_count(1, EXAMPLE); - const std::unordered_set options = - args.check_and_get_optional_command_arguments({OPTION_GRAPH, OPTION_FULLDESC}); - - auto source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); - - if (options.find(OPTION_GRAPH) != options.cend()) - { - const std::string graph_as_string = create_graph_as_string(source_paragraphs); - System::println(graph_as_string); - Checks::exit_success(VCPKG_LINE_INFO); - } - - if (args.command_arguments.empty()) - { - for (const auto& source_control_file : source_paragraphs) - { - do_print(*source_control_file->core_paragraph, options.find(OPTION_FULLDESC) != options.cend()); - for (auto&& feature_paragraph : source_control_file->feature_paragraphs) - { - do_print(source_control_file->core_paragraph->name, - *feature_paragraph, - options.find(OPTION_FULLDESC) != options.cend()); - } - } - } - else - { - const auto& icontains = Strings::case_insensitive_ascii_contains; - - // At this point there is 1 argument - auto&& args_zero = args.command_arguments[0]; - for (const auto& source_control_file : source_paragraphs) - { - auto&& sp = *source_control_file->core_paragraph; - - bool contains_name = icontains(sp.name, args_zero); - if (contains_name || icontains(sp.description, args_zero)) - { - do_print(sp, options.find(OPTION_FULLDESC) != options.cend()); - } - - for (auto&& feature_paragraph : source_control_file->feature_paragraphs) - { - if (contains_name || icontains(feature_paragraph->name, args_zero) || - icontains(feature_paragraph->description, args_zero)) - { - do_print(sp.name, *feature_paragraph, options.find(OPTION_FULLDESC) != options.cend()); - } - } - } - } - - System::println( - "\nIf your library is not listed, please open an issue at and/or consider making a pull request:\n" - " https://github.com/Microsoft/vcpkg/issues"); - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_update.cpp b/toolsrc/src/commands_update.cpp deleted file mode 100644 index 71ea4b063..000000000 --- a/toolsrc/src/commands_update.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Commands::Update -{ - bool OutdatedPackage::compare_by_name(const OutdatedPackage& left, const OutdatedPackage& right) - { - return left.spec.name() < right.spec.name(); - } - - std::vector find_outdated_packages(const VcpkgPaths& paths, const StatusParagraphs& status_db) - { - const std::map src_names_to_versions = - Paragraphs::load_all_port_names_and_versions(paths.get_filesystem(), paths.ports); - const std::vector installed_packages = get_installed_ports(status_db); - - std::vector output; - for (const StatusParagraph* pgh : installed_packages) - { - const auto it = src_names_to_versions.find(pgh->package.spec.name()); - if (it == src_names_to_versions.end()) - { - // Package was not installed from portfile - continue; - } - if (it->second != pgh->package.version) - { - output.push_back({pgh->package.spec, VersionDiff(pgh->package.version, it->second)}); - } - } - - return output; - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { - args.check_exact_arg_count(0); - args.check_and_get_optional_command_arguments({}); - System::println("Using local portfile versions. To update the local portfiles, use `git pull`."); - - const StatusParagraphs status_db = database_load_check(paths); - - const auto outdated_packages = - SortedVector(find_outdated_packages(paths, status_db), &OutdatedPackage::compare_by_name); - - if (outdated_packages.empty()) - { - System::println("No packages need updating."); - } - else - { - std::string install_line; - System::println("The following packages differ from their port versions:"); - for (auto&& package : outdated_packages) - { - install_line += package.spec.to_string(); - install_line += " "; - System::println(" %-32s %s", package.spec, package.version_diff.to_string()); - } - System::println("\n" - "To update these packages, run\n" - " .\\vcpkg remove --outdated\n" - " .\\vcpkg install " + - install_line); - } - - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/commands_version.cpp b/toolsrc/src/commands_version.cpp deleted file mode 100644 index 5744ea9ef..000000000 --- a/toolsrc/src/commands_version.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "pch.h" - -#include "metrics.h" -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" - -#define STRINGIFY(...) #__VA_ARGS__ -#define MACRO_TO_STRING(X) STRINGIFY(X) - -#define VCPKG_VERSION_AS_STRING MACRO_TO_STRING(VCPKG_VERSION) - -namespace vcpkg::Commands::Version -{ - const std::string& version() - { - static const std::string S_VERSION = -#include "../VERSION.txt" - - +std::string(VCPKG_VERSION_AS_STRING) -#ifndef NDEBUG - + std::string("-debug") -#endif - + std::string(Metrics::get_compiled_metrics_enabled() ? Strings::EMPTY : "-external"); - return S_VERSION; - } - - void warn_if_vcpkg_version_mismatch(const VcpkgPaths& paths) - { - auto version_file = paths.get_filesystem().read_contents(paths.root / "toolsrc" / "VERSION.txt"); - if (const auto version_contents = version_file.get()) - { - int maj1, min1, rev1; - const auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1); - - int maj2, min2, rev2; - const auto num2 = sscanf_s(Version::version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2); - - if (num1 == 3 && num2 == 3) - { - if (maj1 != maj2 || min1 != min2 || rev1 != rev2) - { - System::println(System::Color::warning, - "Warning: Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use " - ".\\bootstrap-vcpkg.bat to update.", - maj2, - min2, - rev2, - maj1, - min1, - rev1); - } - } - } - } - - void perform_and_exit(const VcpkgCmdArguments& args) - { - args.check_exact_arg_count(0); - args.check_and_get_optional_command_arguments({}); - - System::println("Vcpkg package management program version %s\n" - "\n" - "See LICENSE.txt for license information.", - version()); - Checks::exit_success(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/metrics.cpp b/toolsrc/src/metrics.cpp deleted file mode 100644 index 8a0050bfc..000000000 --- a/toolsrc/src/metrics.cpp +++ /dev/null @@ -1,395 +0,0 @@ -#include "pch.h" - -#include "filesystem_fs.h" -#include "metrics.h" -#include "vcpkg_Files.h" -#include "vcpkg_Strings.h" -#include "vcpkg_System.h" - -namespace vcpkg::Metrics -{ - Util::LockGuarded g_metrics; - - static std::string get_current_date_time() - { - struct tm newtime; - std::array date; - date.fill(0); - - struct _timeb timebuffer; - - _ftime_s(&timebuffer); - time_t now = timebuffer.time; - const int milli = timebuffer.millitm; - - const errno_t err = gmtime_s(&newtime, &now); - if (err) - { - return Strings::EMPTY; - } - - strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S", &newtime); - return std::string(&date[0]) + "." + std::to_string(milli) + "Z"; - } - - static std::string generate_random_UUID() - { - int part_sizes[] = {8, 4, 4, 4, 12}; - char uuid[37]; - memset(uuid, 0, sizeof(uuid)); - int num; - srand(static_cast(time(nullptr))); - int index = 0; - for (int part = 0; part < 5; part++) - { - if (part > 0) - { - uuid[index] = '-'; - index++; - } - - // Generating UUID format version 4 - // http://en.wikipedia.org/wiki/Universally_unique_identifier - for (int i = 0; i < part_sizes[part]; i++, index++) - { - if (part == 2 && i == 0) - { - num = 4; - } - else if (part == 4 && i == 0) - { - num = (rand() % 4) + 8; - } - else - { - num = rand() % 16; - } - - if (num < 10) - { - uuid[index] = static_cast('0' + num); - } - else - { - uuid[index] = static_cast('a' + (num - 10)); - } - } - } - - return uuid; - } - - static const std::string& get_session_id() - { - static const std::string ID = generate_random_UUID(); - return ID; - } - - static std::string to_json_string(const std::string& str) - { - std::string encoded = "\""; - for (auto&& ch : str) - { - if (ch == '\\') - { - encoded.append("\\\\"); - } - else if (ch == '"') - { - encoded.append("\\\""); - } - else if (ch < 0x20 || ch >= 0x80) - { - // Note: this treats incoming Strings as Latin-1 - static constexpr const char HEX[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - encoded.append("\\u00"); - encoded.push_back(HEX[ch / 16]); - encoded.push_back(HEX[ch % 16]); - } - else - { - encoded.push_back(ch); - } - } - encoded.push_back('"'); - return encoded; - } - - static std::string get_os_version_string() - { - std::wstring path; - path.resize(MAX_PATH); - const auto n = GetSystemDirectoryW(&path[0], static_cast(path.size())); - path.resize(n); - path += L"\\kernel32.dll"; - - const auto versz = GetFileVersionInfoSizeW(path.c_str(), nullptr); - if (versz == 0) return Strings::EMPTY; - - std::vector verbuf; - verbuf.resize(versz); - - if (!GetFileVersionInfoW(path.c_str(), 0, static_cast(verbuf.size()), &verbuf[0])) return Strings::EMPTY; - - void* rootblock; - UINT rootblocksize; - if (!VerQueryValueW(&verbuf[0], L"\\", &rootblock, &rootblocksize)) return Strings::EMPTY; - - auto rootblock_ffi = static_cast(rootblock); - - return Strings::format("%d.%d.%d", - static_cast(HIWORD(rootblock_ffi->dwProductVersionMS)), - static_cast(LOWORD(rootblock_ffi->dwProductVersionMS)), - static_cast(HIWORD(rootblock_ffi->dwProductVersionLS))); - } - - struct MetricMessage - { - std::string user_id = generate_random_UUID(); - std::string user_timestamp; - std::string timestamp = get_current_date_time(); - std::string properties; - std::string measurements; - - void track_property(const std::string& name, const std::string& value) - { - if (properties.size() != 0) properties.push_back(','); - properties.append(to_json_string(name)); - properties.push_back(':'); - properties.append(to_json_string(value)); - } - - void track_metric(const std::string& name, double value) - { - if (measurements.size() != 0) measurements.push_back(','); - measurements.append(to_json_string(name)); - measurements.push_back(':'); - measurements.append(std::to_string(value)); - } - - std::string format_event_data_template() const - { - const std::string& session_id = get_session_id(); - return Strings::format(R"([{ - "ver": 1, - "name": "Microsoft.ApplicationInsights.Event", - "time": "%s", - "sampleRate": 100.000000, - "seq": "0:0", - "iKey": "b4e88960-4393-4dd9-ab8e-97e8fe6d7603", - "flags": 0.000000, - "tags": { - "ai.device.os": "Windows", - "ai.device.osVersion": "%s", - "ai.session.id": "%s", - "ai.user.id": "%s", - "ai.user.accountAcquisitionDate": "%s" - }, - "data": { - "baseType": "EventData", - "baseData": { - "ver": 2, - "name": "commandline_test7", - "properties": { %s }, - "measurements": { %s } - } - } -}])", - timestamp, - get_os_version_string(), - session_id, - user_id, - user_timestamp, - properties, - measurements); - } - }; - - static MetricMessage g_metricmessage; - static bool g_should_send_metrics = -#if defined(NDEBUG) && (DISABLE_METRICS == 0) - true -#else - false -#endif - ; - static bool g_should_print_metrics = false; - - bool get_compiled_metrics_enabled() { return DISABLE_METRICS == 0; } - - std::wstring get_SQM_user() - { - auto hkcu_sqmclient = - System::get_registry_string(HKEY_CURRENT_USER, LR"(Software\Microsoft\SQMClient)", L"UserId"); - return hkcu_sqmclient.value_or(L"{}"); - } - - void Metrics::set_user_information(const std::string& user_id, const std::string& first_use_time) - { - g_metricmessage.user_id = user_id; - g_metricmessage.user_timestamp = first_use_time; - } - - void Metrics::init_user_information(std::string& user_id, std::string& first_use_time) - { - user_id = generate_random_UUID(); - first_use_time = get_current_date_time(); - } - - void Metrics::set_send_metrics(bool should_send_metrics) { g_should_send_metrics = should_send_metrics; } - - void Metrics::set_print_metrics(bool should_print_metrics) { g_should_print_metrics = should_print_metrics; } - - void Metrics::track_metric(const std::string& name, double value) { g_metricmessage.track_metric(name, value); } - - void Metrics::track_property(const std::string& name, const std::wstring& value) - { - // Note: this is not valid UTF-16 -> UTF-8, it just yields a close enough approximation for our purposes. - std::string converted_value; - converted_value.resize(value.size()); - std::transform( - value.begin(), value.end(), converted_value.begin(), [](wchar_t ch) { return static_cast(ch); }); - - g_metricmessage.track_property(name, converted_value); - } - - void Metrics::track_property(const std::string& name, const std::string& value) - { - g_metricmessage.track_property(name, value); - } - - void Metrics::upload(const std::string& payload) - { - HINTERNET connect = nullptr, request = nullptr; - BOOL results = FALSE; - - const HINTERNET session = WinHttpOpen( - L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); - if (session) connect = WinHttpConnect(session, L"dc.services.visualstudio.com", INTERNET_DEFAULT_HTTPS_PORT, 0); - - if (connect) - request = WinHttpOpenRequest(connect, - L"POST", - L"/v2/track", - nullptr, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE); - - if (request) - { - if (MAXDWORD <= payload.size()) abort(); - std::wstring hdrs = L"Content-Type: application/json\r\n"; - std::string& p = const_cast(payload); - results = WinHttpSendRequest(request, - hdrs.c_str(), - static_cast(hdrs.size()), - static_cast(&p[0]), - static_cast(payload.size()), - static_cast(payload.size()), - 0); - } - - if (results) - { - results = WinHttpReceiveResponse(request, nullptr); - } - - DWORD http_code = 0, junk = sizeof(DWORD); - - if (results) - { - results = WinHttpQueryHeaders(request, - WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, - nullptr, - &http_code, - &junk, - WINHTTP_NO_HEADER_INDEX); - } - - std::vector response_buffer; - if (results) - { - DWORD available_data = 0, read_data = 0, total_data = 0; - while ((results = WinHttpQueryDataAvailable(request, &available_data)) == TRUE && available_data > 0) - { - response_buffer.resize(response_buffer.size() + available_data); - - results = WinHttpReadData(request, &response_buffer.data()[total_data], available_data, &read_data); - - if (!results) - { - break; - } - - total_data += read_data; - - response_buffer.resize(total_data); - } - } - - if (!results) - { -#ifndef NDEBUG - __debugbreak(); - auto err = GetLastError(); - std::cerr << "[DEBUG] failed to connect to server: " << err << "\n"; -#endif - } - - if (request) WinHttpCloseHandle(request); - if (connect) WinHttpCloseHandle(connect); - if (session) WinHttpCloseHandle(session); - } - - static fs::path get_bindir() - { - wchar_t buf[_MAX_PATH]; - const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH); - if (bytes == 0) std::abort(); - return fs::path(buf, buf + bytes); - } - - void Metrics::flush() - { - const std::string payload = g_metricmessage.format_event_data_template(); - if (g_should_print_metrics) std::cerr << payload << "\n"; - if (!g_should_send_metrics) return; - - // upload(payload); - - wchar_t temp_folder[MAX_PATH]; - GetTempPathW(MAX_PATH, temp_folder); - - const fs::path temp_folder_path = temp_folder; - const fs::path temp_folder_path_exe = temp_folder_path / "vcpkgmetricsuploader.exe"; - - auto& fs = Files::get_real_filesystem(); - - if (true) - { - const fs::path exe_path = [&fs]() -> fs::path { - auto vcpkgdir = get_bindir().parent_path(); - auto path = vcpkgdir / "vcpkgmetricsuploader.exe"; - if (fs.exists(path)) return path; - - path = vcpkgdir / "scripts" / "vcpkgmetricsuploader.exe"; - if (fs.exists(path)) return path; - - return Strings::WEMPTY; - }(); - - std::error_code ec; - fs.copy_file(exe_path, temp_folder_path_exe, fs::copy_options::skip_existing, ec); - if (ec) return; - } - - const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + generate_random_UUID() + ".txt"); - fs.write_contents(vcpkg_metrics_txt_path, payload); - - const std::wstring cmd_line = - Strings::wformat(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native()); - System::cmd_execute_clean(cmd_line); - } -} diff --git a/toolsrc/src/test_install_plan.cpp b/toolsrc/src/test_install_plan.cpp deleted file mode 100644 index 6c9311264..000000000 --- a/toolsrc/src/test_install_plan.cpp +++ /dev/null @@ -1,486 +0,0 @@ -#include "CppUnitTest.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Util.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -using namespace vcpkg; - -namespace Microsoft::VisualStudio::CppUnitTestFramework -{ - template<> - inline std::wstring ToString(const vcpkg::Dependencies::InstallPlanType& t) - { - switch (t) - { - case Dependencies::InstallPlanType::ALREADY_INSTALLED: return L"ALREADY_INSTALLED"; - case Dependencies::InstallPlanType::BUILD_AND_INSTALL: return L"BUILD_AND_INSTALL"; - case Dependencies::InstallPlanType::INSTALL: return L"INSTALL"; - case Dependencies::InstallPlanType::UNKNOWN: return L"UNKNOWN"; - default: return ToString((int)t); - } - } - - template<> - inline std::wstring ToString(const vcpkg::Dependencies::RequestType& t) - { - switch (t) - { - case Dependencies::RequestType::AUTO_SELECTED: return L"AUTO_SELECTED"; - case Dependencies::RequestType::USER_REQUESTED: return L"USER_REQUESTED"; - case Dependencies::RequestType::UNKNOWN: return L"UNKNOWN"; - default: return ToString((int)t); - } - } -} - -namespace UnitTest1 -{ - class InstallPlanTests : public TestClass - { - static std::unique_ptr make_control_file( - const char* name, - const char* depends, - const std::vector>& features = {}) - { - using Pgh = std::unordered_map; - std::vector scf_pghs; - scf_pghs.push_back(Pgh{ - {"Source", name}, - {"Version", "0"}, - {"Build-Depends", depends}, - }); - for (auto&& feature : features) - { - scf_pghs.push_back(Pgh{ - {"Feature", feature.first}, - {"Description", "feature"}, - {"Build-Depends", feature.second}, - }); - } - auto m_pgh = vcpkg::SourceControlFile::parse_control_file(std::move(scf_pghs)); - Assert::IsTrue(m_pgh.has_value()); - return std::move(*m_pgh.get()); - } - - static void features_check(Dependencies::AnyAction* install_action, - std::string pkg_name, - std::vector vec, - const Triplet& triplet = Triplet::X86_WINDOWS) - { - const auto& plan = install_action->install_plan.value_or_exit(VCPKG_LINE_INFO); - const auto& feature_list = plan.feature_list; - - Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); - - Assert::AreEqual(pkg_name.c_str(), - (*plan.any_paragraph.source_control_file.get())->core_paragraph->name.c_str()); - Assert::AreEqual(size_t(vec.size()), feature_list.size()); - - for (auto&& feature_name : vec) - { - if (feature_name == "core" || feature_name == "") - { - Assert::IsTrue(Util::find(feature_list, "core") != feature_list.end() || - Util::find(feature_list, "") != feature_list.end()); - continue; - } - Assert::IsTrue(Util::find(feature_list, feature_name) != feature_list.end()); - } - } - - static void remove_plan_check(Dependencies::AnyAction* remove_action, - std::string pkg_name, - const Triplet& triplet = Triplet::X86_WINDOWS) - { - const auto& plan = remove_action->remove_plan.value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); - Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str()); - } - - static std::unique_ptr make_status_pgh(const char* name, const char* depends = "") - { - using Pgh = std::unordered_map; - return std::make_unique(Pgh{{"Package", name}, - {"Version", "1"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", depends}, - {"Status", "install ok installed"}}); - } - static std::unique_ptr make_status_feature_pgh(const char* name, - const char* feature, - const char* depends = "") - { - using Pgh = std::unordered_map; - return std::make_unique(Pgh{{"Package", name}, - {"Version", "1"}, - {"Feature", feature}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", depends}, - {"Status", "install ok installed"}}); - } - struct PackageSpecMap - { - std::unordered_map map; - Triplet triplet; - PackageSpecMap(const Triplet& t) { triplet = t; } - - PackageSpec emplace(const char* name, - const char* depends = "", - const std::vector>& features = {}) - { - return emplace(std::move(*make_control_file(name, depends, features))); - } - PackageSpec emplace(vcpkg::SourceControlFile&& scf) - { - auto spec = PackageSpec::from_name_and_triplet(scf.core_paragraph->name, triplet); - Assert::IsTrue(spec.has_value()); - map.emplace(scf.core_paragraph->name, std::move(scf)); - return PackageSpec{*spec.get()}; - } - }; - - TEST_METHOD(basic_install_scheme) - { - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - auto spec_a = spec_map.emplace("a", "b"); - auto spec_b = spec_map.emplace("b", "c"); - auto spec_c = spec_map.emplace("c"); - - Dependencies::MapPortFile map_port(spec_map.map); - auto install_plan = - Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(3), install_plan.size()); - Assert::AreEqual("c", install_plan[0].spec.name().c_str()); - Assert::AreEqual("b", install_plan[1].spec.name().c_str()); - Assert::AreEqual("a", install_plan[2].spec.name().c_str()); - } - - TEST_METHOD(multiple_install_scheme) - { - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - auto spec_a = spec_map.emplace("a", "d"); - auto spec_b = spec_map.emplace("b", "d, e"); - auto spec_c = spec_map.emplace("c", "e, h"); - auto spec_d = spec_map.emplace("d", "f, g, h"); - auto spec_e = spec_map.emplace("e", "g"); - auto spec_f = spec_map.emplace("f"); - auto spec_g = spec_map.emplace("g"); - auto spec_h = spec_map.emplace("h"); - - Dependencies::MapPortFile map_port(spec_map.map); - auto install_plan = Dependencies::create_install_plan( - map_port, {spec_a, spec_b, spec_c}, StatusParagraphs(std::move(status_paragraphs))); - - auto iterator_pos = [&](const PackageSpec& spec) -> int { - auto it = std::find_if( - install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec == spec; }); - Assert::IsTrue(it != install_plan.end()); - return (int)(it - install_plan.begin()); - }; - - int a_pos = iterator_pos(spec_a), b_pos = iterator_pos(spec_b), c_pos = iterator_pos(spec_c), - d_pos = iterator_pos(spec_d), e_pos = iterator_pos(spec_e), f_pos = iterator_pos(spec_f), - g_pos = iterator_pos(spec_g), h_pos = iterator_pos(spec_h); - - Assert::IsTrue(a_pos > d_pos); - Assert::IsTrue(b_pos > e_pos); - Assert::IsTrue(b_pos > d_pos); - Assert::IsTrue(c_pos > e_pos); - Assert::IsTrue(c_pos > h_pos); - Assert::IsTrue(d_pos > f_pos); - Assert::IsTrue(d_pos > g_pos); - Assert::IsTrue(d_pos > h_pos); - Assert::IsTrue(e_pos > g_pos); - } - - TEST_METHOD(existing_package_scheme) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - auto spec_a = FullPackageSpec{spec_map.emplace("a")}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(1), install_plan.size()); - auto p = install_plan[0].install_plan.get(); - Assert::IsNotNull(p); - Assert::AreEqual("a", p->spec.name().c_str()); - Assert::AreEqual(Dependencies::InstallPlanType::ALREADY_INSTALLED, p->plan_type); - Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p->request_type); - } - - TEST_METHOD(user_requested_package_scheme) - { - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")}; - auto spec_b = FullPackageSpec{spec_map.emplace("b")}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(2), install_plan.size()); - auto p = install_plan[0].install_plan.get(); - Assert::IsNotNull(p); - Assert::AreEqual("b", p->spec.name().c_str()); - Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p->plan_type); - Assert::AreEqual(Dependencies::RequestType::AUTO_SELECTED, p->request_type); - - auto p2 = install_plan[1].install_plan.get(); - Assert::IsNotNull(p2); - Assert::AreEqual("a", p2->spec.name().c_str()); - Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p2->plan_type); - Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p2->request_type); - } - - TEST_METHOD(long_install_scheme) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("j", "k")); - status_paragraphs.push_back(make_status_pgh("k")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - - auto spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k"); - auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k"); - auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k"); - auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k"); - auto spec_e = spec_map.emplace("e", "f, g, h, j, k"); - auto spec_f = spec_map.emplace("f", "g, h, j, k"); - auto spec_g = spec_map.emplace("g", "h, j, k"); - auto spec_h = spec_map.emplace("h", "j, k"); - auto spec_j = spec_map.emplace("j", "k"); - auto spec_k = spec_map.emplace("k"); - - Dependencies::MapPortFile map_port(spec_map.map); - auto install_plan = - Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(8), install_plan.size()); - Assert::AreEqual("h", install_plan[0].spec.name().c_str()); - Assert::AreEqual("g", install_plan[1].spec.name().c_str()); - Assert::AreEqual("f", install_plan[2].spec.name().c_str()); - Assert::AreEqual("e", install_plan[3].spec.name().c_str()); - Assert::AreEqual("d", install_plan[4].spec.name().c_str()); - Assert::AreEqual("c", install_plan[5].spec.name().c_str()); - Assert::AreEqual("b", install_plan[6].spec.name().c_str()); - Assert::AreEqual("a", install_plan[7].spec.name().c_str()); - } - - TEST_METHOD(basic_feature_test_1) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]")); - status_paragraphs.push_back(make_status_pgh("b")); - status_paragraphs.push_back(make_status_feature_pgh("b", "b1")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(4), install_plan.size()); - remove_plan_check(&install_plan[0], "a"); - remove_plan_check(&install_plan[1], "b"); - features_check(&install_plan[2], "b", {"b1", "core", "b1"}); - features_check(&install_plan[3], "a", {"a1", "core"}); - } - - TEST_METHOD(basic_feature_test_2) - { - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(2), install_plan.size()); - features_check(&install_plan[0], "b", {"b1", "b2", "core"}); - features_check(&install_plan[1], "a", {"a1", "core"}); - } - - TEST_METHOD(basic_feature_test_3) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_c, spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(4), install_plan.size()); - remove_plan_check(&install_plan[0], "a"); - features_check(&install_plan[1], "b", {"core"}); - features_check(&install_plan[2], "a", {"a1", "core"}); - features_check(&install_plan[3], "c", {"core"}); - } - - TEST_METHOD(basic_feature_test_4) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.push_back(make_status_feature_pgh("a", "a1", "")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})}; - auto spec_b = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_c}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(1), install_plan.size()); - features_check(&install_plan[0], "c", {"core"}); - } - - TEST_METHOD(basic_feature_test_5) - { - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - - auto spec_a = - FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(2), install_plan.size()); - features_check(&install_plan[0], "b", {"core", "b2"}); - features_check(&install_plan[1], "a", {"core", "a3", "a2"}); - } - - TEST_METHOD(basic_feature_test_6) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("b")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[core]"), {"core"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a, spec_b}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(3), install_plan.size()); - remove_plan_check(&install_plan[0], "b"); - features_check(&install_plan[1], "b", {"core", "b1"}); - features_check(&install_plan[2], "a", {"core"}); - } - - TEST_METHOD(basic_feature_test_7) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("x", "b")); - status_paragraphs.push_back(make_status_pgh("b")); - - PackageSpecMap spec_map(Triplet::X86_WINDOWS); - - auto spec_a = FullPackageSpec{spec_map.emplace("a")}; - auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; - - auto install_plan = - Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_b}), - StatusParagraphs(std::move(status_paragraphs))); - - Assert::AreEqual(size_t(5), install_plan.size()); - remove_plan_check(&install_plan[0], "x"); - remove_plan_check(&install_plan[1], "b"); - - // TODO: order here may change but A < X, and B anywhere - features_check(&install_plan[2], "b", {"core", "b1"}); - features_check(&install_plan[3], "a", {"core"}); - features_check(&install_plan[4], "x", {"core"}); - } - - TEST_METHOD(basic_feature_test_8) - { - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.spec = - PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; - auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - spec_map.triplet = Triplet::X86_WINDOWS; - auto spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; - auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}), - StatusParagraphs(std::move(status_paragraphs))); - - /*Assert::AreEqual(size_t(8), install_plan.size()); - auto iterator_pos = [&](const PackageSpec& spec, size_t start) -> int { - auto it = std::find_if(install_plan.begin() + start, install_plan.end(), [&](auto& action) { - return action.spec == spec; - }); - Assert::IsTrue(it != install_plan.end()); - return (int)(it - install_plan.begin()); - }; - int a_64_1 = iterator_pos(spec_a_64.package_spec, 0), a_86_1 = iterator_pos(spec_a_86.package_spec, 0), - b_64 = iterator_pos(spec_b_64.package_spec, 0), b_86 = iterator_pos(spec_b_86.package_spec, 0), - c_64 = iterator_pos(spec_c_64.package_spec, 0), c_86 = iterator_pos(spec_c_86.package_spec, 0), - a_64_2 = iterator_pos(spec_a_64.package_spec, a_64_1 + 1), - a_86_2 = iterator_pos(spec_a_86.package_spec, a_86_1 + 1);*/ - - remove_plan_check(&install_plan[0], "a", Triplet::X64_WINDOWS); - remove_plan_check(&install_plan[1], "a"); - features_check(&install_plan[2], "b", {"core"}, Triplet::X64_WINDOWS); - features_check(&install_plan[3], "a", {"a1", "core"}, Triplet::X64_WINDOWS); - features_check(&install_plan[4], "c", {"core"}, Triplet::X64_WINDOWS); - features_check(&install_plan[5], "b", {"core"}); - features_check(&install_plan[6], "a", {"a1", "core"}); - features_check(&install_plan[7], "c", {"core"}); - } - }; -} \ No newline at end of file diff --git a/toolsrc/src/tests.arguments.cpp b/toolsrc/src/tests.arguments.cpp new file mode 100644 index 000000000..25bf0f085 --- /dev/null +++ b/toolsrc/src/tests.arguments.cpp @@ -0,0 +1,58 @@ +#include +#include + +#pragma comment(lib, "version") +#pragma comment(lib, "winhttp") + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +using namespace vcpkg; + +namespace UnitTest1 +{ + class ArgumentTests : public TestClass + { + TEST_METHOD(create_from_arg_sequence_options_lower) + { + std::vector t = {"--vcpkg-root", "C:\\vcpkg", "--debug", "--sendmetrics", "--printmetrics"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + Assert::AreEqual("C:\\vcpkg", v.vcpkg_root_dir.get()->c_str()); + Assert::IsTrue(v.debug && *v.debug.get()); + Assert::IsTrue(v.sendmetrics && v.sendmetrics.get()); + Assert::IsTrue(v.printmetrics && *v.printmetrics.get()); + } + + TEST_METHOD(create_from_arg_sequence_options_upper) + { + std::vector t = {"--VCPKG-ROOT", "C:\\vcpkg", "--DEBUG", "--SENDMETRICS", "--PRINTMETRICS"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + Assert::AreEqual("C:\\vcpkg", v.vcpkg_root_dir.get()->c_str()); + Assert::IsTrue(v.debug && *v.debug.get()); + Assert::IsTrue(v.sendmetrics && v.sendmetrics.get()); + Assert::IsTrue(v.printmetrics && *v.printmetrics.get()); + } + + TEST_METHOD(create_from_arg_sequence_valued_options) + { + std::vector t = {"--a=b", "command", "argument"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + auto opts = v.check_and_get_optional_command_arguments({}, {"--a"}); + Assert::AreEqual("b", opts.settings["--a"].c_str()); + Assert::AreEqual(size_t{1}, v.command_arguments.size()); + Assert::AreEqual("argument", v.command_arguments[0].c_str()); + Assert::AreEqual("command", v.command.c_str()); + } + + TEST_METHOD(create_from_arg_sequence_valued_options2) + { + std::vector t = {"--a", "--b=c"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + auto opts = v.check_and_get_optional_command_arguments({"--a", "--c"}, {"--b", "--d"}); + Assert::AreEqual("c", opts.settings["--b"].c_str()); + Assert::IsTrue(opts.settings.find("--d") == opts.settings.end()); + Assert::IsTrue(opts.switches.find("--a") != opts.switches.end()); + Assert::IsTrue(opts.settings.find("--c") == opts.settings.end()); + Assert::AreEqual(size_t{0}, v.command_arguments.size()); + } + }; +} \ No newline at end of file diff --git a/toolsrc/src/tests.dependencies.cpp b/toolsrc/src/tests.dependencies.cpp new file mode 100644 index 000000000..3f6e0dd10 --- /dev/null +++ b/toolsrc/src/tests.dependencies.cpp @@ -0,0 +1,110 @@ +#include +#include +#include + +#pragma comment(lib, "version") +#pragma comment(lib, "winhttp") + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +using namespace vcpkg; +using Parse::parse_comma_list; + +namespace UnitTest1 +{ + class DependencyTests : public TestClass + { + TEST_METHOD(parse_depends_one) + { + auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)")); + Assert::AreEqual(size_t(1), v.size()); + Assert::AreEqual("libA", v[0].depend.name.c_str()); + Assert::AreEqual("windows", v[0].qualifier.c_str()); + } + + TEST_METHOD(filter_depends) + { + auto deps = expand_qualified_dependencies(parse_comma_list("libA (windows), libB, libC (uwp)")); + auto v = filter_dependencies(deps, Triplet::X64_WINDOWS); + Assert::AreEqual(size_t(2), v.size()); + Assert::AreEqual("libA", v[0].c_str()); + Assert::AreEqual("libB", v[1].c_str()); + + auto v2 = filter_dependencies(deps, Triplet::ARM_UWP); + Assert::AreEqual(size_t(2), v.size()); + Assert::AreEqual("libB", v2[0].c_str()); + Assert::AreEqual("libC", v2[1].c_str()); + } + }; + + class SupportsTests : public TestClass + { + TEST_METHOD(parse_supports_all) + { + auto v = Supports::parse({ + "x64", + "x86", + "arm", + "windows", + "uwp", + "v140", + "v141", + "crt-static", + "crt-dynamic", + }); + Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get())); + + Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::UWP, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::ARM, + Supports::Platform::WINDOWS, + Supports::Linkage::STATIC, + Supports::ToolsetVersion::V141)); + } + + TEST_METHOD(parse_supports_invalid) + { + auto v = Supports::parse({"arm64"}); + Assert::AreEqual(uintptr_t(0), uintptr_t(v.get())); + Assert::AreEqual(size_t(1), v.error().size()); + Assert::AreEqual("arm64", v.error()[0].c_str()); + } + + TEST_METHOD(parse_supports_case_sensitive) + { + auto v = Supports::parse({"Windows"}); + Assert::AreEqual(uintptr_t(0), uintptr_t(v.get())); + Assert::AreEqual(size_t(1), v.error().size()); + Assert::AreEqual("Windows", v.error()[0].c_str()); + } + + TEST_METHOD(parse_supports_some) + { + auto v = Supports::parse({ + "x64", + "x86", + "windows", + }); + Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get())); + + Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::WINDOWS, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + Assert::IsFalse(v.get()->is_supported(System::CPUArchitecture::ARM, + Supports::Platform::WINDOWS, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + Assert::IsFalse(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::UWP, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::WINDOWS, + Supports::Linkage::STATIC, + Supports::ToolsetVersion::V141)); + } + }; +} diff --git a/toolsrc/src/tests.installplan.cpp b/toolsrc/src/tests.installplan.cpp new file mode 100644 index 000000000..120009db5 --- /dev/null +++ b/toolsrc/src/tests.installplan.cpp @@ -0,0 +1,487 @@ +#include +#include + +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +using namespace vcpkg; + +namespace Microsoft::VisualStudio::CppUnitTestFramework +{ + template<> + inline std::wstring ToString(const vcpkg::Dependencies::InstallPlanType& t) + { + switch (t) + { + case Dependencies::InstallPlanType::ALREADY_INSTALLED: return L"ALREADY_INSTALLED"; + case Dependencies::InstallPlanType::BUILD_AND_INSTALL: return L"BUILD_AND_INSTALL"; + case Dependencies::InstallPlanType::INSTALL: return L"INSTALL"; + case Dependencies::InstallPlanType::UNKNOWN: return L"UNKNOWN"; + default: return ToString((int)t); + } + } + + template<> + inline std::wstring ToString(const vcpkg::Dependencies::RequestType& t) + { + switch (t) + { + case Dependencies::RequestType::AUTO_SELECTED: return L"AUTO_SELECTED"; + case Dependencies::RequestType::USER_REQUESTED: return L"USER_REQUESTED"; + case Dependencies::RequestType::UNKNOWN: return L"UNKNOWN"; + default: return ToString((int)t); + } + } +} + +namespace UnitTest1 +{ + class InstallPlanTests : public TestClass + { + static std::unique_ptr make_control_file( + const char* name, + const char* depends, + const std::vector>& features = {}) + { + using Pgh = std::unordered_map; + std::vector scf_pghs; + scf_pghs.push_back(Pgh{ + {"Source", name}, + {"Version", "0"}, + {"Build-Depends", depends}, + }); + for (auto&& feature : features) + { + scf_pghs.push_back(Pgh{ + {"Feature", feature.first}, + {"Description", "feature"}, + {"Build-Depends", feature.second}, + }); + } + auto m_pgh = vcpkg::SourceControlFile::parse_control_file(std::move(scf_pghs)); + Assert::IsTrue(m_pgh.has_value()); + return std::move(*m_pgh.get()); + } + + static void features_check(Dependencies::AnyAction* install_action, + std::string pkg_name, + std::vector vec, + const Triplet& triplet = Triplet::X86_WINDOWS) + { + const auto& plan = install_action->install_plan.value_or_exit(VCPKG_LINE_INFO); + const auto& feature_list = plan.feature_list; + + Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); + + Assert::AreEqual(pkg_name.c_str(), + (*plan.any_paragraph.source_control_file.get())->core_paragraph->name.c_str()); + Assert::AreEqual(size_t(vec.size()), feature_list.size()); + + for (auto&& feature_name : vec) + { + if (feature_name == "core" || feature_name == "") + { + Assert::IsTrue(Util::find(feature_list, "core") != feature_list.end() || + Util::find(feature_list, "") != feature_list.end()); + continue; + } + Assert::IsTrue(Util::find(feature_list, feature_name) != feature_list.end()); + } + } + + static void remove_plan_check(Dependencies::AnyAction* remove_action, + std::string pkg_name, + const Triplet& triplet = Triplet::X86_WINDOWS) + { + const auto& plan = remove_action->remove_plan.value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); + Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str()); + } + + static std::unique_ptr make_status_pgh(const char* name, const char* depends = "") + { + using Pgh = std::unordered_map; + return std::make_unique(Pgh{{"Package", name}, + {"Version", "1"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", depends}, + {"Status", "install ok installed"}}); + } + static std::unique_ptr make_status_feature_pgh(const char* name, + const char* feature, + const char* depends = "") + { + using Pgh = std::unordered_map; + return std::make_unique(Pgh{{"Package", name}, + {"Version", "1"}, + {"Feature", feature}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", depends}, + {"Status", "install ok installed"}}); + } + struct PackageSpecMap + { + std::unordered_map map; + Triplet triplet; + PackageSpecMap(const Triplet& t) { triplet = t; } + + PackageSpec emplace(const char* name, + const char* depends = "", + const std::vector>& features = {}) + { + return emplace(std::move(*make_control_file(name, depends, features))); + } + PackageSpec emplace(vcpkg::SourceControlFile&& scf) + { + auto spec = PackageSpec::from_name_and_triplet(scf.core_paragraph->name, triplet); + Assert::IsTrue(spec.has_value()); + map.emplace(scf.core_paragraph->name, std::move(scf)); + return PackageSpec{*spec.get()}; + } + }; + + TEST_METHOD(basic_install_scheme) + { + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a", "b"); + auto spec_b = spec_map.emplace("b", "c"); + auto spec_c = spec_map.emplace("c"); + + Dependencies::MapPortFile map_port(spec_map.map); + auto install_plan = + Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(3), install_plan.size()); + Assert::AreEqual("c", install_plan[0].spec.name().c_str()); + Assert::AreEqual("b", install_plan[1].spec.name().c_str()); + Assert::AreEqual("a", install_plan[2].spec.name().c_str()); + } + + TEST_METHOD(multiple_install_scheme) + { + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a", "d"); + auto spec_b = spec_map.emplace("b", "d, e"); + auto spec_c = spec_map.emplace("c", "e, h"); + auto spec_d = spec_map.emplace("d", "f, g, h"); + auto spec_e = spec_map.emplace("e", "g"); + auto spec_f = spec_map.emplace("f"); + auto spec_g = spec_map.emplace("g"); + auto spec_h = spec_map.emplace("h"); + + Dependencies::MapPortFile map_port(spec_map.map); + auto install_plan = Dependencies::create_install_plan( + map_port, {spec_a, spec_b, spec_c}, StatusParagraphs(std::move(status_paragraphs))); + + auto iterator_pos = [&](const PackageSpec& spec) -> int { + auto it = std::find_if( + install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec == spec; }); + Assert::IsTrue(it != install_plan.end()); + return (int)(it - install_plan.begin()); + }; + + int a_pos = iterator_pos(spec_a), b_pos = iterator_pos(spec_b), c_pos = iterator_pos(spec_c), + d_pos = iterator_pos(spec_d), e_pos = iterator_pos(spec_e), f_pos = iterator_pos(spec_f), + g_pos = iterator_pos(spec_g), h_pos = iterator_pos(spec_h); + + Assert::IsTrue(a_pos > d_pos); + Assert::IsTrue(b_pos > e_pos); + Assert::IsTrue(b_pos > d_pos); + Assert::IsTrue(c_pos > e_pos); + Assert::IsTrue(c_pos > h_pos); + Assert::IsTrue(d_pos > f_pos); + Assert::IsTrue(d_pos > g_pos); + Assert::IsTrue(d_pos > h_pos); + Assert::IsTrue(e_pos > g_pos); + } + + TEST_METHOD(existing_package_scheme) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = FullPackageSpec{spec_map.emplace("a")}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(1), install_plan.size()); + auto p = install_plan[0].install_plan.get(); + Assert::IsNotNull(p); + Assert::AreEqual("a", p->spec.name().c_str()); + Assert::AreEqual(Dependencies::InstallPlanType::ALREADY_INSTALLED, p->plan_type); + Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p->request_type); + } + + TEST_METHOD(user_requested_package_scheme) + { + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")}; + auto spec_b = FullPackageSpec{spec_map.emplace("b")}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(2), install_plan.size()); + auto p = install_plan[0].install_plan.get(); + Assert::IsNotNull(p); + Assert::AreEqual("b", p->spec.name().c_str()); + Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p->plan_type); + Assert::AreEqual(Dependencies::RequestType::AUTO_SELECTED, p->request_type); + + auto p2 = install_plan[1].install_plan.get(); + Assert::IsNotNull(p2); + Assert::AreEqual("a", p2->spec.name().c_str()); + Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p2->plan_type); + Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p2->request_type); + } + + TEST_METHOD(long_install_scheme) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("j", "k")); + status_paragraphs.push_back(make_status_pgh("k")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + + auto spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k"); + auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k"); + auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k"); + auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k"); + auto spec_e = spec_map.emplace("e", "f, g, h, j, k"); + auto spec_f = spec_map.emplace("f", "g, h, j, k"); + auto spec_g = spec_map.emplace("g", "h, j, k"); + auto spec_h = spec_map.emplace("h", "j, k"); + auto spec_j = spec_map.emplace("j", "k"); + auto spec_k = spec_map.emplace("k"); + + Dependencies::MapPortFile map_port(spec_map.map); + auto install_plan = + Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(8), install_plan.size()); + Assert::AreEqual("h", install_plan[0].spec.name().c_str()); + Assert::AreEqual("g", install_plan[1].spec.name().c_str()); + Assert::AreEqual("f", install_plan[2].spec.name().c_str()); + Assert::AreEqual("e", install_plan[3].spec.name().c_str()); + Assert::AreEqual("d", install_plan[4].spec.name().c_str()); + Assert::AreEqual("c", install_plan[5].spec.name().c_str()); + Assert::AreEqual("b", install_plan[6].spec.name().c_str()); + Assert::AreEqual("a", install_plan[7].spec.name().c_str()); + } + + TEST_METHOD(basic_feature_test_1) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]")); + status_paragraphs.push_back(make_status_pgh("b")); + status_paragraphs.push_back(make_status_feature_pgh("b", "b1")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(4), install_plan.size()); + remove_plan_check(&install_plan[0], "a"); + remove_plan_check(&install_plan[1], "b"); + features_check(&install_plan[2], "b", {"b1", "core", "b1"}); + features_check(&install_plan[3], "a", {"a1", "core"}); + } + + TEST_METHOD(basic_feature_test_2) + { + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(2), install_plan.size()); + features_check(&install_plan[0], "b", {"b1", "b2", "core"}); + features_check(&install_plan[1], "a", {"a1", "core"}); + } + + TEST_METHOD(basic_feature_test_3) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_c, spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(4), install_plan.size()); + remove_plan_check(&install_plan[0], "a"); + features_check(&install_plan[1], "b", {"core"}); + features_check(&install_plan[2], "a", {"a1", "core"}); + features_check(&install_plan[3], "c", {"core"}); + } + + TEST_METHOD(basic_feature_test_4) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.push_back(make_status_feature_pgh("a", "a1", "")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})}; + auto spec_b = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_c}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(1), install_plan.size()); + features_check(&install_plan[0], "c", {"core"}); + } + + TEST_METHOD(basic_feature_test_5) + { + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + + auto spec_a = + FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(2), install_plan.size()); + features_check(&install_plan[0], "b", {"core", "b2"}); + features_check(&install_plan[1], "a", {"core", "a3", "a2"}); + } + + TEST_METHOD(basic_feature_test_6) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("b")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[core]"), {"core"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a, spec_b}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(3), install_plan.size()); + remove_plan_check(&install_plan[0], "b"); + features_check(&install_plan[1], "b", {"core", "b1"}); + features_check(&install_plan[2], "a", {"core"}); + } + + TEST_METHOD(basic_feature_test_7) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("x", "b")); + status_paragraphs.push_back(make_status_pgh("b")); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + + auto spec_a = FullPackageSpec{spec_map.emplace("a")}; + auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; + + auto install_plan = + Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_b}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::AreEqual(size_t(5), install_plan.size()); + remove_plan_check(&install_plan[0], "x"); + remove_plan_check(&install_plan[1], "b"); + + // TODO: order here may change but A < X, and B anywhere + features_check(&install_plan[2], "b", {"core", "b1"}); + features_check(&install_plan[3], "a", {"core"}); + features_check(&install_plan[4], "x", {"core"}); + } + + TEST_METHOD(basic_feature_test_8) + { + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.spec = + PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; + auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + spec_map.triplet = Triplet::X86_WINDOWS; + auto spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; + auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}), + StatusParagraphs(std::move(status_paragraphs))); + + /*Assert::AreEqual(size_t(8), install_plan.size()); + auto iterator_pos = [&](const PackageSpec& spec, size_t start) -> int { + auto it = std::find_if(install_plan.begin() + start, install_plan.end(), [&](auto& action) { + return action.spec == spec; + }); + Assert::IsTrue(it != install_plan.end()); + return (int)(it - install_plan.begin()); + }; + int a_64_1 = iterator_pos(spec_a_64.package_spec, 0), a_86_1 = iterator_pos(spec_a_86.package_spec, 0), + b_64 = iterator_pos(spec_b_64.package_spec, 0), b_86 = iterator_pos(spec_b_86.package_spec, 0), + c_64 = iterator_pos(spec_c_64.package_spec, 0), c_86 = iterator_pos(spec_c_86.package_spec, 0), + a_64_2 = iterator_pos(spec_a_64.package_spec, a_64_1 + 1), + a_86_2 = iterator_pos(spec_a_86.package_spec, a_86_1 + 1);*/ + + remove_plan_check(&install_plan[0], "a", Triplet::X64_WINDOWS); + remove_plan_check(&install_plan[1], "a"); + features_check(&install_plan[2], "b", {"core"}, Triplet::X64_WINDOWS); + features_check(&install_plan[3], "a", {"a1", "core"}, Triplet::X64_WINDOWS); + features_check(&install_plan[4], "c", {"core"}, Triplet::X64_WINDOWS); + features_check(&install_plan[5], "b", {"core"}); + features_check(&install_plan[6], "a", {"a1", "core"}); + features_check(&install_plan[7], "c", {"core"}); + } + }; +} \ No newline at end of file diff --git a/toolsrc/src/tests.packagespec.cpp b/toolsrc/src/tests.packagespec.cpp new file mode 100644 index 000000000..cb5c41af0 --- /dev/null +++ b/toolsrc/src/tests.packagespec.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +#include + +#pragma comment(lib, "version") +#pragma comment(lib, "winhttp") + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace Microsoft::VisualStudio::CppUnitTestFramework +{ + template<> + inline std::wstring ToString(const vcpkg::PackageSpecParseResult& t) + { + return ToString(static_cast(t)); + } + + template<> + inline std::wstring ToString(const vcpkg::PackageSpec& t) + { + return ToString(t.to_string()); + } +} + +namespace Strings = vcpkg::Strings; + +namespace UnitTest1 +{ + using namespace vcpkg; + + class SpecifierConversion : public TestClass + { + TEST_METHOD(full_package_spec_to_feature_specs) + { + auto a_spec = PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + auto b_spec = PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + auto fspecs = FullPackageSpec::to_feature_specs({{a_spec, {"0", "1"}}, {b_spec, {"2", "3"}}}); + + Assert::AreEqual(size_t(6), fspecs.size()); + + std::array features = {"", "0", "1", "", "2", "3"}; + std::array specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec}; + + for (size_t i = 0; i < features.size(); ++i) + { + Assert::AreEqual(features[i], fspecs[i].feature().c_str()); + Assert::AreEqual(*specs[i], fspecs[i].spec()); + } + } + }; + + class SpecifierParsing : public TestClass + { + TEST_METHOD(parsed_specifier_from_string) + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib"); + Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); + auto spec = maybe_spec.get(); + Assert::AreEqual("zlib", spec->name.c_str()); + Assert::AreEqual(size_t(0), spec->features.size()); + Assert::AreEqual("", spec->triplet.c_str()); + } + + TEST_METHOD(parsed_specifier_from_string_with_triplet) + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp"); + Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); + auto spec = maybe_spec.get(); + Assert::AreEqual("zlib", spec->name.c_str()); + Assert::AreEqual("x64-uwp", spec->triplet.c_str()); + } + + TEST_METHOD(parsed_specifier_from_string_with_colons) + { + auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error(); + Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec); + } + + TEST_METHOD(parsed_specifier_from_string_with_feature) + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp"); + Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); + auto spec = maybe_spec.get(); + Assert::AreEqual("zlib", spec->name.c_str()); + Assert::IsTrue(spec->features.size() == 1); + Assert::AreEqual("feature", spec->features.front().c_str()); + Assert::AreEqual("x64-uwp", spec->triplet.c_str()); + } + + TEST_METHOD(parsed_specifier_from_string_with_many_features) + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]"); + Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); + auto spec = maybe_spec.get(); + Assert::AreEqual("zlib", spec->name.c_str()); + Assert::IsTrue(spec->features.size() == 3); + Assert::AreEqual("0", spec->features[0].c_str()); + Assert::AreEqual("1", spec->features[1].c_str()); + Assert::AreEqual("2", spec->features[2].c_str()); + Assert::AreEqual("", spec->triplet.c_str()); + } + + TEST_METHOD(utf8_to_utf16) + { + auto str = vcpkg::Strings::to_utf16("abc"); + Assert::AreEqual(L"abc", str.c_str()); + } + + TEST_METHOD(utf8_to_utf16_with_whitespace) + { + auto str = vcpkg::Strings::to_utf16("abc -x86-windows"); + Assert::AreEqual(L"abc -x86-windows", str.c_str()); + } + }; + + TEST_CLASS(Metrics){}; +} diff --git a/toolsrc/src/tests.paragraph.cpp b/toolsrc/src/tests.paragraph.cpp new file mode 100644 index 000000000..d9301abd0 --- /dev/null +++ b/toolsrc/src/tests.paragraph.cpp @@ -0,0 +1,389 @@ +#include +#include +#include + +#include + +#pragma comment(lib, "version") +#pragma comment(lib, "winhttp") + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace Microsoft::VisualStudio::CppUnitTestFramework +{ + template<> + inline std::wstring ToString(const vcpkg::PackageSpecParseResult& t) + { + return ToString(static_cast(t)); + } +} + +namespace Strings = vcpkg::Strings; + +namespace UnitTest1 +{ + class ControlParsing : public TestClass + { + TEST_METHOD(SourceParagraph_Construct_Minimum) + { + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + }}); + + Assert::IsTrue(m_pgh.has_value()); + auto& pgh = *m_pgh.get(); + + Assert::AreEqual("zlib", pgh->core_paragraph->name.c_str()); + Assert::AreEqual("1.2.8", pgh->core_paragraph->version.c_str()); + Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str()); + Assert::AreEqual("", pgh->core_paragraph->description.c_str()); + Assert::AreEqual(size_t(0), pgh->core_paragraph->depends.size()); + } + + TEST_METHOD(SourceParagraph_Construct_Maximum) + { + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "s"}, + {"Version", "v"}, + {"Maintainer", "m"}, + {"Description", "d"}, + {"Build-Depends", "bd"}, + {"Supports", "x64"}, + }}); + Assert::IsTrue(m_pgh.has_value()); + auto& pgh = *m_pgh.get(); + + Assert::AreEqual("s", pgh->core_paragraph->name.c_str()); + Assert::AreEqual("v", pgh->core_paragraph->version.c_str()); + Assert::AreEqual("m", pgh->core_paragraph->maintainer.c_str()); + Assert::AreEqual("d", pgh->core_paragraph->description.c_str()); + Assert::AreEqual(size_t(1), pgh->core_paragraph->depends.size()); + Assert::AreEqual("bd", pgh->core_paragraph->depends[0].name().c_str()); + Assert::AreEqual(size_t(1), pgh->core_paragraph->supports.size()); + Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str()); + } + + TEST_METHOD(SourceParagraph_Two_Depends) + { + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Build-Depends", "z, openssl"}, + }}); + Assert::IsTrue(m_pgh.has_value()); + auto& pgh = *m_pgh.get(); + + Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size()); + Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str()); + Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str()); + } + + TEST_METHOD(SourceParagraph_Three_Depends) + { + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Build-Depends", "z, openssl, xyz"}, + }}); + Assert::IsTrue(m_pgh.has_value()); + auto& pgh = *m_pgh.get(); + + Assert::AreEqual(size_t(3), pgh->core_paragraph->depends.size()); + Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str()); + Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str()); + Assert::AreEqual("xyz", pgh->core_paragraph->depends[2].name().c_str()); + } + + TEST_METHOD(SourceParagraph_Three_Supports) + { + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Supports", "x64, windows, uwp"}, + }}); + Assert::IsTrue(m_pgh.has_value()); + auto& pgh = *m_pgh.get(); + + Assert::AreEqual(size_t(3), pgh->core_paragraph->supports.size()); + Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str()); + Assert::AreEqual("windows", pgh->core_paragraph->supports[1].c_str()); + Assert::AreEqual("uwp", pgh->core_paragraph->supports[2].c_str()); + } + + TEST_METHOD(SourceParagraph_Construct_Qualified_Depends) + { + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Build-Depends", "libA (windows), libB (uwp)"}, + }}); + Assert::IsTrue(m_pgh.has_value()); + auto& pgh = *m_pgh.get(); + + Assert::AreEqual("zlib", pgh->core_paragraph->name.c_str()); + Assert::AreEqual("1.2.8", pgh->core_paragraph->version.c_str()); + Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str()); + Assert::AreEqual("", pgh->core_paragraph->description.c_str()); + Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size()); + Assert::AreEqual("libA", pgh->core_paragraph->depends[0].name().c_str()); + Assert::AreEqual("windows", pgh->core_paragraph->depends[0].qualifier.c_str()); + Assert::AreEqual("libB", pgh->core_paragraph->depends[1].name().c_str()); + Assert::AreEqual("uwp", pgh->core_paragraph->depends[1].qualifier.c_str()); + } + + TEST_METHOD(BinaryParagraph_Construct_Minimum) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + }); + + Assert::AreEqual("zlib", pgh.spec.name().c_str()); + Assert::AreEqual("1.2.8", pgh.version.c_str()); + Assert::AreEqual("", pgh.maintainer.c_str()); + Assert::AreEqual("", pgh.description.c_str()); + Assert::AreEqual("x86-windows", pgh.spec.triplet().canonical_name().c_str()); + Assert::AreEqual(size_t(0), pgh.depends.size()); + } + + TEST_METHOD(BinaryParagraph_Construct_Maximum) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "s"}, + {"Version", "v"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Maintainer", "m"}, + {"Description", "d"}, + {"Depends", "bd"}, + }); + Assert::AreEqual("s", pgh.spec.name().c_str()); + Assert::AreEqual("v", pgh.version.c_str()); + Assert::AreEqual("m", pgh.maintainer.c_str()); + Assert::AreEqual("d", pgh.description.c_str()); + Assert::AreEqual(size_t(1), pgh.depends.size()); + Assert::AreEqual("bd", pgh.depends[0].c_str()); + } + + TEST_METHOD(BinaryParagraph_Three_Depends) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", "a, b, c"}, + }); + + Assert::AreEqual(size_t(3), pgh.depends.size()); + Assert::AreEqual("a", pgh.depends[0].c_str()); + Assert::AreEqual("b", pgh.depends[1].c_str()); + Assert::AreEqual("c", pgh.depends[2].c_str()); + } + + TEST_METHOD(parse_paragraphs_empty) + { + const char* str = ""; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::IsTrue(pghs.empty()); + } + + TEST_METHOD(parse_paragraphs_one_field) + { + const char* str = "f1: v1"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual(size_t(1), pghs[0].size()); + Assert::AreEqual("v1", pghs[0]["f1"].c_str()); + } + + TEST_METHOD(parse_paragraphs_one_pgh) + { + const char* str = "f1: v1\n" + "f2: v2"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual(size_t(2), pghs[0].size()); + Assert::AreEqual("v1", pghs[0]["f1"].c_str()); + Assert::AreEqual("v2", pghs[0]["f2"].c_str()); + } + + TEST_METHOD(parse_paragraphs_two_pgh) + { + const char* str = "f1: v1\n" + "f2: v2\n" + "\n" + "f3: v3\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(2), pghs.size()); + Assert::AreEqual(size_t(2), pghs[0].size()); + Assert::AreEqual("v1", pghs[0]["f1"].c_str()); + Assert::AreEqual("v2", pghs[0]["f2"].c_str()); + Assert::AreEqual(size_t(2), pghs[1].size()); + Assert::AreEqual("v3", pghs[1]["f3"].c_str()); + Assert::AreEqual("v4", pghs[1]["f4"].c_str()); + } + + TEST_METHOD(parse_paragraphs_field_names) + { + const char* str = "1:\n" + "f:\n" + "F:\n" + "0:\n" + "F-2:\n"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual(size_t(5), pghs[0].size()); + } + + TEST_METHOD(parse_paragraphs_multiple_blank_lines) + { + const char* str = "f1: v1\n" + "f2: v2\n" + "\n" + "\n" + "f3: v3\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(2), pghs.size()); + } + + TEST_METHOD(parse_paragraphs_empty_fields) + { + const char* str = "f1:\n" + "f2: "; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual(size_t(2), pghs[0].size()); + Assert::AreEqual("", pghs[0]["f1"].c_str()); + Assert::AreEqual("", pghs[0]["f2"].c_str()); + Assert::AreEqual(size_t(2), pghs[0].size()); + } + + TEST_METHOD(parse_paragraphs_multiline_fields) + { + const char* str = "f1: simple\n" + " f1\r\n" + "f2:\r\n" + " f2\r\n" + " continue\r\n"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual("simple\n f1", pghs[0]["f1"].c_str()); + Assert::AreEqual("\n f2\n continue", pghs[0]["f2"].c_str()); + } + + TEST_METHOD(parse_paragraphs_crlfs) + { + const char* str = "f1: v1\r\n" + "f2: v2\r\n" + "\r\n" + "f3: v3\r\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(2), pghs.size()); + Assert::AreEqual(size_t(2), pghs[0].size()); + Assert::AreEqual("v1", pghs[0]["f1"].c_str()); + Assert::AreEqual("v2", pghs[0]["f2"].c_str()); + Assert::AreEqual(size_t(2), pghs[1].size()); + Assert::AreEqual("v3", pghs[1]["f3"].c_str()); + Assert::AreEqual("v4", pghs[1]["f4"].c_str()); + } + + TEST_METHOD(parse_paragraphs_comment) + { + const char* str = "f1: v1\r\n" + "#comment\r\n" + "f2: v2\r\n" + "#comment\r\n" + "\r\n" + "#comment\r\n" + "f3: v3\r\n" + "#comment\r\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(2), pghs.size()); + Assert::AreEqual(size_t(2), pghs[0].size()); + Assert::AreEqual("v1", pghs[0]["f1"].c_str()); + Assert::AreEqual("v2", pghs[0]["f2"].c_str()); + Assert::AreEqual(size_t(2), pghs[1].size()); + Assert::AreEqual("v3", pghs[1]["f3"].c_str()); + Assert::AreEqual("v4", pghs[1]["f4"].c_str()); + } + + TEST_METHOD(parse_comment_before_single_slashN) + { + const char* str = "f1: v1\r\n" + "#comment\n"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs[0].size()); + Assert::AreEqual("v1", pghs[0]["f1"].c_str()); + } + + TEST_METHOD(BinaryParagraph_serialize_min) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual(size_t(4), pghs[0].size()); + Assert::AreEqual("zlib", pghs[0]["Package"].c_str()); + Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str()); + Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str()); + Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str()); + } + + TEST_METHOD(BinaryParagraph_serialize_max) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Description", "first line\n second line"}, + {"Maintainer", "abc "}, + {"Depends", "dep"}, + {"Multi-Arch", "same"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual(size_t(7), pghs[0].size()); + Assert::AreEqual("zlib", pghs[0]["Package"].c_str()); + Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str()); + Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str()); + Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str()); + Assert::AreEqual("first line\n second line", pghs[0]["Description"].c_str()); + Assert::AreEqual("dep", pghs[0]["Depends"].c_str()); + } + + TEST_METHOD(BinaryParagraph_serialize_multiple_deps) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", "a, b, c"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str()); + } + }; +} diff --git a/toolsrc/src/tests_arguments.cpp b/toolsrc/src/tests_arguments.cpp deleted file mode 100644 index 14b3c3d4f..000000000 --- a/toolsrc/src/tests_arguments.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "CppUnitTest.h" -#include "VcpkgCmdArguments.h" - -#pragma comment(lib, "version") -#pragma comment(lib, "winhttp") - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -using namespace vcpkg; - -namespace UnitTest1 -{ - class ArgumentTests : public TestClass - { - TEST_METHOD(create_from_arg_sequence_options_lower) - { - std::vector t = {"--vcpkg-root", "C:\\vcpkg", "--debug", "--sendmetrics", "--printmetrics"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - Assert::AreEqual("C:\\vcpkg", v.vcpkg_root_dir.get()->c_str()); - Assert::IsTrue(v.debug && *v.debug.get()); - Assert::IsTrue(v.sendmetrics && v.sendmetrics.get()); - Assert::IsTrue(v.printmetrics && *v.printmetrics.get()); - } - - TEST_METHOD(create_from_arg_sequence_options_upper) - { - std::vector t = {"--VCPKG-ROOT", "C:\\vcpkg", "--DEBUG", "--SENDMETRICS", "--PRINTMETRICS"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - Assert::AreEqual("C:\\vcpkg", v.vcpkg_root_dir.get()->c_str()); - Assert::IsTrue(v.debug && *v.debug.get()); - Assert::IsTrue(v.sendmetrics && v.sendmetrics.get()); - Assert::IsTrue(v.printmetrics && *v.printmetrics.get()); - } - - TEST_METHOD(create_from_arg_sequence_valued_options) - { - std::vector t = {"--a=b", "command", "argument"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - auto opts = v.check_and_get_optional_command_arguments({}, {"--a"}); - Assert::AreEqual("b", opts.settings["--a"].c_str()); - Assert::AreEqual(size_t{1}, v.command_arguments.size()); - Assert::AreEqual("argument", v.command_arguments[0].c_str()); - Assert::AreEqual("command", v.command.c_str()); - } - - TEST_METHOD(create_from_arg_sequence_valued_options2) - { - std::vector t = {"--a", "--b=c"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - auto opts = v.check_and_get_optional_command_arguments({"--a", "--c"}, {"--b", "--d"}); - Assert::AreEqual("c", opts.settings["--b"].c_str()); - Assert::IsTrue(opts.settings.find("--d") == opts.settings.end()); - Assert::IsTrue(opts.switches.find("--a") != opts.switches.end()); - Assert::IsTrue(opts.settings.find("--c") == opts.settings.end()); - Assert::AreEqual(size_t{0}, v.command_arguments.size()); - } - }; -} \ No newline at end of file diff --git a/toolsrc/src/tests_dependencies.cpp b/toolsrc/src/tests_dependencies.cpp deleted file mode 100644 index 6a6981d73..000000000 --- a/toolsrc/src/tests_dependencies.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "CppUnitTest.h" -#include "SourceParagraph.h" -#include "Triplet.h" - -#pragma comment(lib, "version") -#pragma comment(lib, "winhttp") - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -using namespace vcpkg; -using Parse::parse_comma_list; - -namespace UnitTest1 -{ - class DependencyTests : public TestClass - { - TEST_METHOD(parse_depends_one) - { - auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)")); - Assert::AreEqual(size_t(1), v.size()); - Assert::AreEqual("libA", v[0].depend.name.c_str()); - Assert::AreEqual("windows", v[0].qualifier.c_str()); - } - - TEST_METHOD(filter_depends) - { - auto deps = expand_qualified_dependencies(parse_comma_list("libA (windows), libB, libC (uwp)")); - auto v = filter_dependencies(deps, Triplet::X64_WINDOWS); - Assert::AreEqual(size_t(2), v.size()); - Assert::AreEqual("libA", v[0].c_str()); - Assert::AreEqual("libB", v[1].c_str()); - - auto v2 = filter_dependencies(deps, Triplet::ARM_UWP); - Assert::AreEqual(size_t(2), v.size()); - Assert::AreEqual("libB", v2[0].c_str()); - Assert::AreEqual("libC", v2[1].c_str()); - } - }; - - class SupportsTests : public TestClass - { - TEST_METHOD(parse_supports_all) - { - auto v = Supports::parse({ - "x64", - "x86", - "arm", - "windows", - "uwp", - "v140", - "v141", - "crt-static", - "crt-dynamic", - }); - Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get())); - - Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::UWP, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::ARM, - Supports::Platform::WINDOWS, - Supports::Linkage::STATIC, - Supports::ToolsetVersion::V141)); - } - - TEST_METHOD(parse_supports_invalid) - { - auto v = Supports::parse({"arm64"}); - Assert::AreEqual(uintptr_t(0), uintptr_t(v.get())); - Assert::AreEqual(size_t(1), v.error().size()); - Assert::AreEqual("arm64", v.error()[0].c_str()); - } - - TEST_METHOD(parse_supports_case_sensitive) - { - auto v = Supports::parse({"Windows"}); - Assert::AreEqual(uintptr_t(0), uintptr_t(v.get())); - Assert::AreEqual(size_t(1), v.error().size()); - Assert::AreEqual("Windows", v.error()[0].c_str()); - } - - TEST_METHOD(parse_supports_some) - { - auto v = Supports::parse({ - "x64", - "x86", - "windows", - }); - Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get())); - - Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::WINDOWS, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - Assert::IsFalse(v.get()->is_supported(System::CPUArchitecture::ARM, - Supports::Platform::WINDOWS, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - Assert::IsFalse(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::UWP, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::WINDOWS, - Supports::Linkage::STATIC, - Supports::ToolsetVersion::V141)); - } - }; -} diff --git a/toolsrc/src/tests_package_spec.cpp b/toolsrc/src/tests_package_spec.cpp deleted file mode 100644 index a6b9d5b13..000000000 --- a/toolsrc/src/tests_package_spec.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "BinaryParagraph.h" -#include "CppUnitTest.h" -#include "Paragraphs.h" -#include "vcpkg_Strings.h" - -#pragma comment(lib, "version") -#pragma comment(lib, "winhttp") - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace Microsoft::VisualStudio::CppUnitTestFramework -{ - template<> - inline std::wstring ToString(const vcpkg::PackageSpecParseResult& t) - { - return ToString(static_cast(t)); - } - - template<> - inline std::wstring ToString(const vcpkg::PackageSpec& t) - { - return ToString(t.to_string()); - } -} - -namespace Strings = vcpkg::Strings; - -namespace UnitTest1 -{ - using namespace vcpkg; - - class SpecifierConversion : public TestClass - { - TEST_METHOD(full_package_spec_to_feature_specs) - { - auto a_spec = PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - auto b_spec = PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - auto fspecs = FullPackageSpec::to_feature_specs({{a_spec, {"0", "1"}}, {b_spec, {"2", "3"}}}); - - Assert::AreEqual(size_t(6), fspecs.size()); - - std::array features = {"", "0", "1", "", "2", "3"}; - std::array specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec}; - - for (size_t i = 0; i < features.size(); ++i) - { - Assert::AreEqual(features[i], fspecs[i].feature().c_str()); - Assert::AreEqual(*specs[i], fspecs[i].spec()); - } - } - }; - - class SpecifierParsing : public TestClass - { - TEST_METHOD(parsed_specifier_from_string) - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib"); - Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); - auto spec = maybe_spec.get(); - Assert::AreEqual("zlib", spec->name.c_str()); - Assert::AreEqual(size_t(0), spec->features.size()); - Assert::AreEqual("", spec->triplet.c_str()); - } - - TEST_METHOD(parsed_specifier_from_string_with_triplet) - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp"); - Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); - auto spec = maybe_spec.get(); - Assert::AreEqual("zlib", spec->name.c_str()); - Assert::AreEqual("x64-uwp", spec->triplet.c_str()); - } - - TEST_METHOD(parsed_specifier_from_string_with_colons) - { - auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error(); - Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec); - } - - TEST_METHOD(parsed_specifier_from_string_with_feature) - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp"); - Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); - auto spec = maybe_spec.get(); - Assert::AreEqual("zlib", spec->name.c_str()); - Assert::IsTrue(spec->features.size() == 1); - Assert::AreEqual("feature", spec->features.front().c_str()); - Assert::AreEqual("x64-uwp", spec->triplet.c_str()); - } - - TEST_METHOD(parsed_specifier_from_string_with_many_features) - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]"); - Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); - auto spec = maybe_spec.get(); - Assert::AreEqual("zlib", spec->name.c_str()); - Assert::IsTrue(spec->features.size() == 3); - Assert::AreEqual("0", spec->features[0].c_str()); - Assert::AreEqual("1", spec->features[1].c_str()); - Assert::AreEqual("2", spec->features[2].c_str()); - Assert::AreEqual("", spec->triplet.c_str()); - } - - TEST_METHOD(utf8_to_utf16) - { - auto str = vcpkg::Strings::to_utf16("abc"); - Assert::AreEqual(L"abc", str.c_str()); - } - - TEST_METHOD(utf8_to_utf16_with_whitespace) - { - auto str = vcpkg::Strings::to_utf16("abc -x86-windows"); - Assert::AreEqual(L"abc -x86-windows", str.c_str()); - } - }; - - TEST_CLASS(Metrics){}; -} diff --git a/toolsrc/src/tests_paragraph.cpp b/toolsrc/src/tests_paragraph.cpp deleted file mode 100644 index 47a07e12d..000000000 --- a/toolsrc/src/tests_paragraph.cpp +++ /dev/null @@ -1,388 +0,0 @@ -#include "BinaryParagraph.h" -#include "CppUnitTest.h" -#include "Paragraphs.h" -#include "vcpkg_Strings.h" - -#pragma comment(lib, "version") -#pragma comment(lib, "winhttp") - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace Microsoft::VisualStudio::CppUnitTestFramework -{ - template<> - inline std::wstring ToString(const vcpkg::PackageSpecParseResult& t) - { - return ToString(static_cast(t)); - } -} - -namespace Strings = vcpkg::Strings; - -namespace UnitTest1 -{ - class ControlParsing : public TestClass - { - TEST_METHOD(SourceParagraph_Construct_Minimum) - { - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - }}); - - Assert::IsTrue(m_pgh.has_value()); - auto& pgh = *m_pgh.get(); - - Assert::AreEqual("zlib", pgh->core_paragraph->name.c_str()); - Assert::AreEqual("1.2.8", pgh->core_paragraph->version.c_str()); - Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str()); - Assert::AreEqual("", pgh->core_paragraph->description.c_str()); - Assert::AreEqual(size_t(0), pgh->core_paragraph->depends.size()); - } - - TEST_METHOD(SourceParagraph_Construct_Maximum) - { - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "s"}, - {"Version", "v"}, - {"Maintainer", "m"}, - {"Description", "d"}, - {"Build-Depends", "bd"}, - {"Supports", "x64"}, - }}); - Assert::IsTrue(m_pgh.has_value()); - auto& pgh = *m_pgh.get(); - - Assert::AreEqual("s", pgh->core_paragraph->name.c_str()); - Assert::AreEqual("v", pgh->core_paragraph->version.c_str()); - Assert::AreEqual("m", pgh->core_paragraph->maintainer.c_str()); - Assert::AreEqual("d", pgh->core_paragraph->description.c_str()); - Assert::AreEqual(size_t(1), pgh->core_paragraph->depends.size()); - Assert::AreEqual("bd", pgh->core_paragraph->depends[0].name().c_str()); - Assert::AreEqual(size_t(1), pgh->core_paragraph->supports.size()); - Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str()); - } - - TEST_METHOD(SourceParagraph_Two_Depends) - { - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Build-Depends", "z, openssl"}, - }}); - Assert::IsTrue(m_pgh.has_value()); - auto& pgh = *m_pgh.get(); - - Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size()); - Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str()); - Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str()); - } - - TEST_METHOD(SourceParagraph_Three_Depends) - { - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Build-Depends", "z, openssl, xyz"}, - }}); - Assert::IsTrue(m_pgh.has_value()); - auto& pgh = *m_pgh.get(); - - Assert::AreEqual(size_t(3), pgh->core_paragraph->depends.size()); - Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str()); - Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str()); - Assert::AreEqual("xyz", pgh->core_paragraph->depends[2].name().c_str()); - } - - TEST_METHOD(SourceParagraph_Three_Supports) - { - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Supports", "x64, windows, uwp"}, - }}); - Assert::IsTrue(m_pgh.has_value()); - auto& pgh = *m_pgh.get(); - - Assert::AreEqual(size_t(3), pgh->core_paragraph->supports.size()); - Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str()); - Assert::AreEqual("windows", pgh->core_paragraph->supports[1].c_str()); - Assert::AreEqual("uwp", pgh->core_paragraph->supports[2].c_str()); - } - - TEST_METHOD(SourceParagraph_Construct_Qualified_Depends) - { - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Build-Depends", "libA (windows), libB (uwp)"}, - }}); - Assert::IsTrue(m_pgh.has_value()); - auto& pgh = *m_pgh.get(); - - Assert::AreEqual("zlib", pgh->core_paragraph->name.c_str()); - Assert::AreEqual("1.2.8", pgh->core_paragraph->version.c_str()); - Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str()); - Assert::AreEqual("", pgh->core_paragraph->description.c_str()); - Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size()); - Assert::AreEqual("libA", pgh->core_paragraph->depends[0].name().c_str()); - Assert::AreEqual("windows", pgh->core_paragraph->depends[0].qualifier.c_str()); - Assert::AreEqual("libB", pgh->core_paragraph->depends[1].name().c_str()); - Assert::AreEqual("uwp", pgh->core_paragraph->depends[1].qualifier.c_str()); - } - - TEST_METHOD(BinaryParagraph_Construct_Minimum) - { - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - }); - - Assert::AreEqual("zlib", pgh.spec.name().c_str()); - Assert::AreEqual("1.2.8", pgh.version.c_str()); - Assert::AreEqual("", pgh.maintainer.c_str()); - Assert::AreEqual("", pgh.description.c_str()); - Assert::AreEqual("x86-windows", pgh.spec.triplet().canonical_name().c_str()); - Assert::AreEqual(size_t(0), pgh.depends.size()); - } - - TEST_METHOD(BinaryParagraph_Construct_Maximum) - { - vcpkg::BinaryParagraph pgh({ - {"Package", "s"}, - {"Version", "v"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Maintainer", "m"}, - {"Description", "d"}, - {"Depends", "bd"}, - }); - Assert::AreEqual("s", pgh.spec.name().c_str()); - Assert::AreEqual("v", pgh.version.c_str()); - Assert::AreEqual("m", pgh.maintainer.c_str()); - Assert::AreEqual("d", pgh.description.c_str()); - Assert::AreEqual(size_t(1), pgh.depends.size()); - Assert::AreEqual("bd", pgh.depends[0].c_str()); - } - - TEST_METHOD(BinaryParagraph_Three_Depends) - { - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", "a, b, c"}, - }); - - Assert::AreEqual(size_t(3), pgh.depends.size()); - Assert::AreEqual("a", pgh.depends[0].c_str()); - Assert::AreEqual("b", pgh.depends[1].c_str()); - Assert::AreEqual("c", pgh.depends[2].c_str()); - } - - TEST_METHOD(parse_paragraphs_empty) - { - const char* str = ""; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::IsTrue(pghs.empty()); - } - - TEST_METHOD(parse_paragraphs_one_field) - { - const char* str = "f1: v1"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual(size_t(1), pghs[0].size()); - Assert::AreEqual("v1", pghs[0]["f1"].c_str()); - } - - TEST_METHOD(parse_paragraphs_one_pgh) - { - const char* str = "f1: v1\n" - "f2: v2"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual(size_t(2), pghs[0].size()); - Assert::AreEqual("v1", pghs[0]["f1"].c_str()); - Assert::AreEqual("v2", pghs[0]["f2"].c_str()); - } - - TEST_METHOD(parse_paragraphs_two_pgh) - { - const char* str = "f1: v1\n" - "f2: v2\n" - "\n" - "f3: v3\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(2), pghs.size()); - Assert::AreEqual(size_t(2), pghs[0].size()); - Assert::AreEqual("v1", pghs[0]["f1"].c_str()); - Assert::AreEqual("v2", pghs[0]["f2"].c_str()); - Assert::AreEqual(size_t(2), pghs[1].size()); - Assert::AreEqual("v3", pghs[1]["f3"].c_str()); - Assert::AreEqual("v4", pghs[1]["f4"].c_str()); - } - - TEST_METHOD(parse_paragraphs_field_names) - { - const char* str = "1:\n" - "f:\n" - "F:\n" - "0:\n" - "F-2:\n"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual(size_t(5), pghs[0].size()); - } - - TEST_METHOD(parse_paragraphs_multiple_blank_lines) - { - const char* str = "f1: v1\n" - "f2: v2\n" - "\n" - "\n" - "f3: v3\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(2), pghs.size()); - } - - TEST_METHOD(parse_paragraphs_empty_fields) - { - const char* str = "f1:\n" - "f2: "; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual(size_t(2), pghs[0].size()); - Assert::AreEqual("", pghs[0]["f1"].c_str()); - Assert::AreEqual("", pghs[0]["f2"].c_str()); - Assert::AreEqual(size_t(2), pghs[0].size()); - } - - TEST_METHOD(parse_paragraphs_multiline_fields) - { - const char* str = "f1: simple\n" - " f1\r\n" - "f2:\r\n" - " f2\r\n" - " continue\r\n"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual("simple\n f1", pghs[0]["f1"].c_str()); - Assert::AreEqual("\n f2\n continue", pghs[0]["f2"].c_str()); - } - - TEST_METHOD(parse_paragraphs_crlfs) - { - const char* str = "f1: v1\r\n" - "f2: v2\r\n" - "\r\n" - "f3: v3\r\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(2), pghs.size()); - Assert::AreEqual(size_t(2), pghs[0].size()); - Assert::AreEqual("v1", pghs[0]["f1"].c_str()); - Assert::AreEqual("v2", pghs[0]["f2"].c_str()); - Assert::AreEqual(size_t(2), pghs[1].size()); - Assert::AreEqual("v3", pghs[1]["f3"].c_str()); - Assert::AreEqual("v4", pghs[1]["f4"].c_str()); - } - - TEST_METHOD(parse_paragraphs_comment) - { - const char* str = "f1: v1\r\n" - "#comment\r\n" - "f2: v2\r\n" - "#comment\r\n" - "\r\n" - "#comment\r\n" - "f3: v3\r\n" - "#comment\r\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(2), pghs.size()); - Assert::AreEqual(size_t(2), pghs[0].size()); - Assert::AreEqual("v1", pghs[0]["f1"].c_str()); - Assert::AreEqual("v2", pghs[0]["f2"].c_str()); - Assert::AreEqual(size_t(2), pghs[1].size()); - Assert::AreEqual("v3", pghs[1]["f3"].c_str()); - Assert::AreEqual("v4", pghs[1]["f4"].c_str()); - } - - TEST_METHOD(parse_comment_before_single_slashN) - { - const char* str = "f1: v1\r\n" - "#comment\n"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs[0].size()); - Assert::AreEqual("v1", pghs[0]["f1"].c_str()); - } - - TEST_METHOD(BinaryParagraph_serialize_min) - { - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual(size_t(4), pghs[0].size()); - Assert::AreEqual("zlib", pghs[0]["Package"].c_str()); - Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str()); - Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str()); - Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str()); - } - - TEST_METHOD(BinaryParagraph_serialize_max) - { - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Description", "first line\n second line"}, - {"Maintainer", "abc "}, - {"Depends", "dep"}, - {"Multi-Arch", "same"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual(size_t(7), pghs[0].size()); - Assert::AreEqual("zlib", pghs[0]["Package"].c_str()); - Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str()); - Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str()); - Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str()); - Assert::AreEqual("first line\n second line", pghs[0]["Description"].c_str()); - Assert::AreEqual("dep", pghs[0]["Depends"].c_str()); - } - - TEST_METHOD(BinaryParagraph_serialize_multiple_deps) - { - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", "a, b, c"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - Assert::AreEqual(size_t(1), pghs.size()); - Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str()); - } - }; -} diff --git a/toolsrc/src/triplet.cpp b/toolsrc/src/triplet.cpp deleted file mode 100644 index 3b56da02a..000000000 --- a/toolsrc/src/triplet.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "pch.h" - -#include "Triplet.h" -#include "vcpkg_Checks.h" -#include "vcpkg_Strings.h" - -namespace vcpkg -{ - struct TripletInstance - { - TripletInstance(std::string&& s) : value(std::move(s)), hash(std::hash()(value)) {} - - const std::string value; - const size_t hash = 0; - - bool operator==(const TripletInstance& o) const { return o.value == value; } - }; - const TripletInstance Triplet::DEFAULT_INSTANCE({}); -} - -template<> -struct std::hash -{ - size_t operator()(const vcpkg::TripletInstance& t) const { return t.hash; } -}; - -namespace vcpkg -{ - static std::unordered_set g_triplet_instances; - - const Triplet Triplet::X86_WINDOWS = from_canonical_name("x86-windows"); - const Triplet Triplet::X64_WINDOWS = from_canonical_name("x64-windows"); - const Triplet Triplet::X86_UWP = from_canonical_name("x86-uwp"); - const Triplet Triplet::X64_UWP = from_canonical_name("x64-uwp"); - const Triplet Triplet::ARM_UWP = from_canonical_name("arm-uwp"); - - bool Triplet::operator==(const Triplet& other) const { return this->m_instance == other.m_instance; } - - bool operator!=(const Triplet& left, const Triplet& right) { return !(left == right); } - - Triplet Triplet::from_canonical_name(const std::string& triplet_as_string) - { - std::string s(Strings::ascii_to_lowercase(triplet_as_string)); - const auto it = std::find(s.cbegin(), s.cend(), '-'); - Checks::check_exit(VCPKG_LINE_INFO, it != s.cend(), "Invalid triplet: %s", triplet_as_string); - - const auto p = g_triplet_instances.emplace(std::move(s)); - return &*p.first; - } - - const std::string& Triplet::canonical_name() const { return this->m_instance->value; } - - const std::string& Triplet::to_string() const { return this->canonical_name(); } - size_t Triplet::hash_code() const { return m_instance->hash; } -} diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index 706c641fb..748b4f0ee 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -1,16 +1,18 @@ #define WIN32_LEAN_AND_MEAN #include -#include "Paragraphs.h" -#include "metrics.h" -#include "vcpkg_Chrono.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Files.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_Input.h" -#include "vcpkg_Strings.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #pragma warning(push) #pragma warning(disable : 4768) #include @@ -19,12 +21,15 @@ #include #include +#pragma comment(lib, "ole32") +#pragma comment(lib, "shell32") + using namespace vcpkg; void invalid_command(const std::string& cmd) { System::println(System::Color::error, "invalid command: %s", cmd); - Commands::Help::print_usage(); + Help::print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); } @@ -33,13 +38,26 @@ static void inner(const VcpkgCmdArguments& args) Metrics::g_metrics.lock()->track_property("command", args.command); if (args.command.empty()) { - Commands::Help::print_usage(); + Help::print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); } - if (const auto command_function = Commands::find(args.command, Commands::get_available_commands_type_c())) + static const auto find_command = [&](auto&& commands) { + auto it = Util::find_if(commands, [&](auto&& commandc) { + return Strings::case_insensitive_ascii_compare(commandc.name, args.command); + }); + using std::end; + if (it != end(commands)) + { + return &*it; + } + else + return static_cast(nullptr); + }; + + if (const auto command_function = find_command(Commands::get_available_commands_type_c())) { - return command_function(args); + return command_function->function(args); } fs::path vcpkg_root_dir; @@ -74,9 +92,9 @@ static void inner(const VcpkgCmdArguments& args) Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Changing the working dir failed"); Commands::Version::warn_if_vcpkg_version_mismatch(paths); - if (const auto command_function = Commands::find(args.command, Commands::get_available_commands_type_b())) + if (const auto command_function = find_command(Commands::get_available_commands_type_b())) { - return command_function(args, paths); + return command_function->function(args, paths); } Triplet default_triplet; @@ -100,9 +118,9 @@ static void inner(const VcpkgCmdArguments& args) Input::check_triplet(default_triplet, paths); - if (const auto command_function = Commands::find(args.command, Commands::get_available_commands_type_a())) + if (const auto command_function = find_command(Commands::get_available_commands_type_a())) { - return command_function(args, paths, default_triplet); + return command_function->function(args, paths, default_triplet); } return invalid_command(args.command); @@ -202,7 +220,7 @@ int wmain(const int argc, const wchar_t* const* const argv) SetConsoleCP(65001); SetConsoleOutputCP(65001); - *GlobalState::timer.lock() = ElapsedTime::create_started(); + *GlobalState::timer.lock() = Chrono::ElapsedTime::create_started(); const std::string trimmed_command_line = trim_path_from_command_line(Strings::to_utf8(GetCommandLineW())); diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp new file mode 100644 index 000000000..73c7b9b1a --- /dev/null +++ b/toolsrc/src/vcpkg/base/checks.cpp @@ -0,0 +1,81 @@ +#include "pch.h" + +#include +#include + +#include +#include + +namespace vcpkg::Checks +{ + [[noreturn]] static void cleanup_and_exit(const int exit_code) + { + const auto elapsed_us = GlobalState::timer.lock()->microseconds(); + + auto metrics = Metrics::g_metrics.lock(); + metrics->track_metric("elapsed_us", elapsed_us); + GlobalState::debugging = false; + metrics->flush(); + + SetConsoleCP(GlobalState::g_init_console_cp); + SetConsoleOutputCP(GlobalState::g_init_console_output_cp); + + fflush(nullptr); + + ::TerminateProcess(::GetCurrentProcess(), exit_code); + } + + static BOOL ctrl_handler(DWORD fdw_ctrl_type) + { + { + auto locked_metrics = Metrics::g_metrics.lock(); + locked_metrics->track_property("CtrlHandler", std::to_string(fdw_ctrl_type)); + locked_metrics->track_property("error", "CtrlHandler was fired."); + } + cleanup_and_exit(EXIT_FAILURE); + } + + void register_console_ctrl_handler() + { + SetConsoleCtrlHandler(reinterpret_cast(ctrl_handler), TRUE); + } + + [[noreturn]] void unreachable(const LineInfo& line_info) + { + System::println(System::Color::error, "Error: Unreachable code was reached"); + System::println(System::Color::error, line_info.to_string()); // Always print line_info here +#ifndef NDEBUG + std::abort(); +#else + cleanup_and_exit(EXIT_FAILURE); +#endif + } + + [[noreturn]] void exit_with_code(const LineInfo& line_info, const int exit_code) + { + Debug::println(System::Color::error, line_info.to_string()); + cleanup_and_exit(exit_code); + } + + [[noreturn]] void exit_with_message(const LineInfo& line_info, const CStringView error_message) + { + System::println(System::Color::error, error_message); + exit_fail(line_info); + } + + void check_exit(const LineInfo& line_info, bool expression) + { + if (!expression) + { + exit_with_message(line_info, Strings::EMPTY); + } + } + + void check_exit(const LineInfo& line_info, bool expression, const CStringView error_message) + { + if (!expression) + { + exit_with_message(line_info, error_message); + } + } +} diff --git a/toolsrc/src/vcpkg/base/chrono.cpp b/toolsrc/src/vcpkg/base/chrono.cpp new file mode 100644 index 000000000..5d28909fc --- /dev/null +++ b/toolsrc/src/vcpkg/base/chrono.cpp @@ -0,0 +1,61 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg::Chrono +{ + static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos) + { + using std::chrono::duration_cast; + using std::chrono::hours; + using std::chrono::microseconds; + using std::chrono::milliseconds; + using std::chrono::minutes; + using std::chrono::nanoseconds; + using std::chrono::seconds; + + const double nanos_as_double = static_cast(nanos.count()); + + if (duration_cast(nanos) > hours()) + { + const auto t = nanos_as_double / duration_cast(hours(1)).count(); + return Strings::format("%.4g h", t); + } + + if (duration_cast(nanos) > minutes()) + { + const auto t = nanos_as_double / duration_cast(minutes(1)).count(); + return Strings::format("%.4g min", t); + } + + if (duration_cast(nanos) > seconds()) + { + const auto t = nanos_as_double / duration_cast(seconds(1)).count(); + return Strings::format("%.4g s", t); + } + + if (duration_cast(nanos) > milliseconds()) + { + const auto t = nanos_as_double / duration_cast(milliseconds(1)).count(); + return Strings::format("%.4g ms", t); + } + + if (duration_cast(nanos) > microseconds()) + { + const auto t = nanos_as_double / duration_cast(microseconds(1)).count(); + return Strings::format("%.4g us", t); + } + + return Strings::format("%.4g ns", nanos_as_double); + } + + ElapsedTime ElapsedTime::create_started() + { + ElapsedTime t; + t.m_start_tick = std::chrono::high_resolution_clock::now(); + return t; + } + + std::string ElapsedTime::to_string() const { return format_time_userfriendly(elapsed()); } +} diff --git a/toolsrc/src/vcpkg/base/cofffilereader.cpp b/toolsrc/src/vcpkg/base/cofffilereader.cpp new file mode 100644 index 000000000..5e9c86998 --- /dev/null +++ b/toolsrc/src/vcpkg/base/cofffilereader.cpp @@ -0,0 +1,308 @@ +#include "pch.h" + +#include +#include + +using namespace std; + +namespace vcpkg::CoffFileReader +{ + template + static T reinterpret_bytes(const char* data) + { + return (*reinterpret_cast(&data[0])); + } + + template + static T read_value_from_stream(fstream& fs) + { + T data; + fs.read(reinterpret_cast(&data), sizeof data); + return data; + } + + template + static T peek_value_from_stream(fstream& fs) + { + const fpos_t original_pos = fs.tellg().seekpos(); + T data; + fs.read(reinterpret_cast(&data), sizeof data); + fs.seekg(original_pos); + return data; + } + + static void verify_equal_strings( + const LineInfo& line_info, const char* expected, const char* actual, int size, const char* label) + { + Checks::check_exit(line_info, + memcmp(expected, actual, size) == 0, + "Incorrect string (%s) found. Expected: (%s) but found (%s)", + label, + expected, + actual); + } + + static void read_and_verify_PE_signature(fstream& fs) + { + static const size_t OFFSET_TO_PE_SIGNATURE_OFFSET = 0x3c; + + static const char* PE_SIGNATURE = "PE\0\0"; + static const size_t PE_SIGNATURE_SIZE = 4; + + fs.seekg(OFFSET_TO_PE_SIGNATURE_OFFSET, ios_base::beg); + const int32_t offset_to_PE_signature = read_value_from_stream(fs); + + fs.seekg(offset_to_PE_signature); + char signature[PE_SIGNATURE_SIZE]; + fs.read(signature, PE_SIGNATURE_SIZE); + verify_equal_strings(VCPKG_LINE_INFO, PE_SIGNATURE, signature, PE_SIGNATURE_SIZE, "PE_SIGNATURE"); + fs.seekg(offset_to_PE_signature + PE_SIGNATURE_SIZE, ios_base::beg); + } + + static fpos_t align_to_size(const uint64_t unaligned, const uint64_t alignment_size) + { + fpos_t aligned = unaligned - 1; + aligned /= alignment_size; + aligned += 1; + aligned *= alignment_size; + return aligned; + } + + struct CoffFileHeader + { + static const size_t HEADER_SIZE = 20; + + static CoffFileHeader read(fstream& fs) + { + CoffFileHeader ret; + ret.data.resize(HEADER_SIZE); + fs.read(&ret.data[0], HEADER_SIZE); + return ret; + } + + MachineType machine_type() const + { + static const size_t MACHINE_TYPE_OFFSET = 0; + static const size_t MACHINE_TYPE_SIZE = 2; + + std::string machine_field_as_string = data.substr(MACHINE_TYPE_OFFSET, MACHINE_TYPE_SIZE); + const uint16_t machine = reinterpret_bytes(machine_field_as_string.c_str()); + return to_machine_type(machine); + } + + private: + std::string data; + }; + + struct ArchiveMemberHeader + { + static const size_t HEADER_SIZE = 60; + + static ArchiveMemberHeader read(fstream& fs) + { + static const size_t HEADER_END_OFFSET = 58; + static const char* HEADER_END = "`\n"; + static const size_t HEADER_END_SIZE = 2; + + ArchiveMemberHeader ret; + ret.data.resize(HEADER_SIZE); + fs.read(&ret.data[0], HEADER_SIZE); + + if (ret.data[0] != '\0') // Due to freeglut. github issue #223 + { + const std::string header_end = ret.data.substr(HEADER_END_OFFSET, HEADER_END_SIZE); + verify_equal_strings( + VCPKG_LINE_INFO, HEADER_END, header_end.c_str(), HEADER_END_SIZE, "LIB HEADER_END"); + } + + return ret; + } + + std::string name() const + { + static const size_t HEADER_NAME_OFFSET = 0; + static const size_t HEADER_NAME_SIZE = 16; + return data.substr(HEADER_NAME_OFFSET, HEADER_NAME_SIZE); + } + + uint64_t member_size() const + { + static const size_t ALIGNMENT_SIZE = 2; + + static const size_t HEADER_SIZE_OFFSET = 48; + static const size_t HEADER_SIZE_FIELD_SIZE = 10; + const std::string as_string = data.substr(HEADER_SIZE_OFFSET, HEADER_SIZE_FIELD_SIZE); + // This is in ASCII decimal representation + const uint64_t value = std::strtoull(as_string.c_str(), nullptr, 10); + + const uint64_t aligned = align_to_size(value, ALIGNMENT_SIZE); + return aligned; + } + + std::string data; + }; + + struct OffsetsArray + { + static OffsetsArray read(fstream& fs, const uint32_t offset_count) + { + static const size_t OFFSET_WIDTH = 4; + + std::string raw_offsets; + const size_t raw_offset_size = offset_count * OFFSET_WIDTH; + raw_offsets.resize(raw_offset_size); + fs.read(&raw_offsets[0], raw_offset_size); + + OffsetsArray ret; + for (uint32_t i = 0; i < offset_count; ++i) + { + const std::string value_as_string = raw_offsets.substr(OFFSET_WIDTH * i, OFFSET_WIDTH * (i + 1)); + const uint32_t value = reinterpret_bytes(value_as_string.c_str()); + + // Ignore offsets that point to offset 0. See vcpkg github #223 #288 #292 + if (value != 0) + { + ret.data.push_back(value); + } + } + + // Sort the offsets, because it is possible for them to be unsorted. See vcpkg github #292 + std::sort(ret.data.begin(), ret.data.end()); + return ret; + } + + std::vector data; + }; + + struct ImportHeader + { + static const size_t HEADER_SIZE = 20; + + static ImportHeader read(fstream& fs) + { + static const size_t SIG1_OFFSET = 0; + static const uint16_t SIG1 = static_cast(MachineType::UNKNOWN); + static const size_t SIG1_SIZE = 2; + + static const size_t SIG2_OFFSET = 2; + static const uint16_t SIG2 = 0xFFFF; + static const size_t SIG2_SIZE = 2; + + ImportHeader ret; + ret.data.resize(HEADER_SIZE); + fs.read(&ret.data[0], HEADER_SIZE); + + const std::string sig1_as_string = ret.data.substr(SIG1_OFFSET, SIG1_SIZE); + const uint16_t sig1 = reinterpret_bytes(sig1_as_string.c_str()); + Checks::check_exit(VCPKG_LINE_INFO, sig1 == SIG1, "Sig1 was incorrect. Expected %s but got %s", SIG1, sig1); + + const std::string sig2_as_string = ret.data.substr(SIG2_OFFSET, SIG2_SIZE); + const uint16_t sig2 = reinterpret_bytes(sig2_as_string.c_str()); + Checks::check_exit(VCPKG_LINE_INFO, sig2 == SIG2, "Sig2 was incorrect. Expected %s but got %s", SIG2, sig2); + + return ret; + } + + MachineType machine_type() const + { + static const size_t MACHINE_TYPE_OFFSET = 6; + static const size_t MACHINE_TYPE_SIZE = 2; + + std::string machine_field_as_string = data.substr(MACHINE_TYPE_OFFSET, MACHINE_TYPE_SIZE); + const uint16_t machine = reinterpret_bytes(machine_field_as_string.c_str()); + return to_machine_type(machine); + } + + private: + std::string data; + }; + + static void read_and_verify_archive_file_signature(fstream& fs) + { + static const char* FILE_START = "!\n"; + static const size_t FILE_START_SIZE = 8; + + fs.seekg(fs.beg); + + char file_start[FILE_START_SIZE]; + fs.read(file_start, FILE_START_SIZE); + verify_equal_strings(VCPKG_LINE_INFO, FILE_START, file_start, FILE_START_SIZE, "LIB FILE_START"); + } + + DllInfo read_dll(const fs::path& path) + { + std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate); + Checks::check_exit(VCPKG_LINE_INFO, fs.is_open(), "Could not open file %s for reading", path.generic_string()); + + read_and_verify_PE_signature(fs); + CoffFileHeader header = CoffFileHeader::read(fs); + const MachineType machine = header.machine_type(); + return {machine}; + } + + struct Marker + { + void set_to_offset(const fpos_t position) { this->m_absolute_position = position; } + + void set_to_current_pos(fstream& fs) { this->m_absolute_position = fs.tellg().seekpos(); } + + void seek_to_marker(fstream& fs) const { fs.seekg(this->m_absolute_position, ios_base::beg); } + + void advance_by(const uint64_t offset) { this->m_absolute_position += offset; } + + private: + fpos_t m_absolute_position = 0; + }; + + LibInfo read_lib(const fs::path& path) + { + std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate); + Checks::check_exit(VCPKG_LINE_INFO, fs.is_open(), "Could not open file %s for reading", path.generic_string()); + + read_and_verify_archive_file_signature(fs); + + Marker marker; + marker.set_to_current_pos(fs); + + // First Linker Member + const ArchiveMemberHeader first_linker_member_header = ArchiveMemberHeader::read(fs); + Checks::check_exit(VCPKG_LINE_INFO, + first_linker_member_header.name().substr(0, 2) == "/ ", + "Could not find proper first linker member"); + marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + first_linker_member_header.member_size()); + marker.seek_to_marker(fs); + + const ArchiveMemberHeader second_linker_member_header = ArchiveMemberHeader::read(fs); + Checks::check_exit(VCPKG_LINE_INFO, + second_linker_member_header.name().substr(0, 2) == "/ ", + "Could not find proper second linker member"); + // The first 4 bytes contains the number of archive members + const uint32_t archive_member_count = read_value_from_stream(fs); + const OffsetsArray offsets = OffsetsArray::read(fs, archive_member_count); + marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + second_linker_member_header.member_size()); + marker.seek_to_marker(fs); + + const bool hasLongnameMemberHeader = peek_value_from_stream(fs) == 0x2F2F; + if (hasLongnameMemberHeader) + { + const ArchiveMemberHeader longnames_member_header = ArchiveMemberHeader::read(fs); + marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + longnames_member_header.member_size()); + marker.seek_to_marker(fs); + } + + std::set machine_types; + // Next we have the obj and pseudo-object files + for (const uint32_t offset : offsets.data) + { + marker.set_to_offset(offset + ArchiveMemberHeader::HEADER_SIZE); // Skip the header, no need to read it. + marker.seek_to_marker(fs); + const uint16_t first_two_bytes = peek_value_from_stream(fs); + const bool isImportHeader = to_machine_type(first_two_bytes) == MachineType::UNKNOWN; + const MachineType machine = + isImportHeader ? ImportHeader::read(fs).machine_type() : CoffFileHeader::read(fs).machine_type(); + machine_types.insert(machine); + } + + return {std::vector(machine_types.cbegin(), machine_types.cend())}; + } +} diff --git a/toolsrc/src/vcpkg/base/enums.cpp b/toolsrc/src/vcpkg/base/enums.cpp new file mode 100644 index 000000000..aa124f3aa --- /dev/null +++ b/toolsrc/src/vcpkg/base/enums.cpp @@ -0,0 +1,14 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg::Enums +{ + std::string nullvalue_to_string(const CStringView enum_name) { return Strings::format("%s_NULLVALUE", enum_name); } + + [[noreturn]] void nullvalue_used(const LineInfo& line_info, const CStringView enum_name) + { + Checks::exit_with_message(line_info, "NULLVALUE of enum %s was used", enum_name); + } +} diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp new file mode 100644 index 000000000..f8b239eaa --- /dev/null +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -0,0 +1,208 @@ +#include "pch.h" + +#include +#include +#include + +namespace vcpkg::Files +{ + static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])"); + + struct RealFilesystem final : Filesystem + { + virtual Expected read_contents(const fs::path& file_path) const override + { + std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary); + if (file_stream.fail()) + { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + file_stream.seekg(0, file_stream.end); + auto length = file_stream.tellg(); + file_stream.seekg(0, file_stream.beg); + + if (length > SIZE_MAX) + { + return std::make_error_code(std::errc::file_too_large); + } + + std::string output; + output.resize(static_cast(length)); + file_stream.read(&output[0], length); + file_stream.close(); + + return std::move(output); + } + virtual Expected> read_lines(const fs::path& file_path) const override + { + std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary); + if (file_stream.fail()) + { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + std::vector output; + std::string line; + while (std::getline(file_stream, line)) + { + output.push_back(line); + } + file_stream.close(); + + return std::move(output); + } + virtual fs::path find_file_recursively_up(const fs::path& starting_dir, + const std::string& filename) const override + { + fs::path current_dir = starting_dir; + for (; !current_dir.empty(); current_dir = current_dir.parent_path()) + { + const fs::path candidate = current_dir / filename; + if (exists(candidate)) + { + break; + } + } + + return current_dir; + } + + virtual std::vector get_files_recursive(const fs::path& dir) const override + { + std::vector ret; + + fs::stdfs::recursive_directory_iterator b(dir), e{}; + for (; b != e; ++b) + { + ret.push_back(b->path()); + } + + return ret; + } + + virtual std::vector get_files_non_recursive(const fs::path& dir) const override + { + std::vector ret; + + fs::stdfs::directory_iterator b(dir), e{}; + for (; b != e; ++b) + { + ret.push_back(b->path()); + } + + return ret; + } + + virtual void write_lines(const fs::path& file_path, const std::vector& lines) override + { + std::fstream output(file_path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + for (const std::string& line : lines) + { + output << line << "\n"; + } + output.close(); + } + + virtual void rename(const fs::path& oldpath, const fs::path& newpath) override + { + fs::stdfs::rename(oldpath, newpath); + } + virtual bool remove(const fs::path& path) override { return fs::stdfs::remove(path); } + virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); } + virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec) override + { + // Working around the currently buggy remove_all() + std::uintmax_t out = fs::stdfs::remove_all(path, ec); + + for (int i = 0; i < 5 && this->exists(path); i++) + { + using namespace std::chrono_literals; + std::this_thread::sleep_for(i * 100ms); + out += fs::stdfs::remove_all(path, ec); + } + + if (this->exists(path)) + { + System::println(System::Color::warning, + "Some files in %s were unable to be removed. Close any editors operating in this " + "directory and retry.", + path.string()); + } + + return out; + } + virtual bool exists(const fs::path& path) const override { return fs::stdfs::exists(path); } + virtual bool is_directory(const fs::path& path) const override { return fs::stdfs::is_directory(path); } + virtual bool is_regular_file(const fs::path& path) const override { return fs::stdfs::is_regular_file(path); } + virtual bool is_empty(const fs::path& path) const override { return fs::stdfs::is_empty(path); } + virtual bool create_directory(const fs::path& path, std::error_code& ec) override + { + return fs::stdfs::create_directory(path, ec); + } + virtual bool create_directories(const fs::path& path, std::error_code& ec) override + { + return fs::stdfs::create_directories(path, ec); + } + virtual void copy(const fs::path& oldpath, const fs::path& newpath, fs::copy_options opts) override + { + fs::stdfs::copy(oldpath, newpath, opts); + } + virtual bool copy_file(const fs::path& oldpath, + const fs::path& newpath, + fs::copy_options opts, + std::error_code& ec) override + { + return fs::stdfs::copy_file(oldpath, newpath, opts, ec); + } + + virtual fs::file_status status(const fs::path& path, std::error_code& ec) const override + { + return fs::stdfs::status(path, ec); + } + virtual void write_contents(const fs::path& file_path, const std::string& data) override + { + FILE* f = nullptr; + auto ec = _wfopen_s(&f, file_path.native().c_str(), L"wb"); + Checks::check_exit( + VCPKG_LINE_INFO, ec == 0, "Error: Could not open file for writing: %s", file_path.u8string().c_str()); + auto count = fwrite(data.data(), sizeof(data[0]), data.size(), f); + fclose(f); + + Checks::check_exit(VCPKG_LINE_INFO, count == data.size()); + } + }; + + Filesystem& get_real_filesystem() + { + static RealFilesystem real_fs; + return real_fs; + } + + bool has_invalid_chars_for_filesystem(const std::string& s) + { + return std::regex_search(s, FILESYSTEM_INVALID_CHARACTERS_REGEX); + } + + void print_paths(const std::vector& paths) + { + System::println(); + for (const fs::path& p : paths) + { + System::println(" %s", p.generic_string()); + } + System::println(); + } + + std::vector find_from_PATH(const std::wstring& name) + { + const std::wstring cmd = Strings::wformat(L"where.exe %s", name); + auto out = System::cmd_execute_and_capture_output(cmd); + if (out.exit_code != 0) + { + return {}; + } + + return Util::fmap(Strings::split(out.output, "\n"), [](auto&& s) { return fs::path(s); }); + } +} diff --git a/toolsrc/src/vcpkg/base/lineinfo.cpp b/toolsrc/src/vcpkg/base/lineinfo.cpp new file mode 100644 index 000000000..7435ed666 --- /dev/null +++ b/toolsrc/src/vcpkg/base/lineinfo.cpp @@ -0,0 +1,9 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg +{ + std::string LineInfo::to_string() const { return Strings::format("%s(%d)", this->file_name, this->line_number); } +} diff --git a/toolsrc/src/vcpkg/base/machinetype.cpp b/toolsrc/src/vcpkg/base/machinetype.cpp new file mode 100644 index 000000000..2b7bd5e3a --- /dev/null +++ b/toolsrc/src/vcpkg/base/machinetype.cpp @@ -0,0 +1,41 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg +{ + MachineType to_machine_type(const uint16_t value) + { + const MachineType t = static_cast(value); + switch (t) + { + case MachineType::UNKNOWN: + case MachineType::AM33: + case MachineType::AMD64: + case MachineType::ARM: + case MachineType::ARM64: + case MachineType::ARMNT: + case MachineType::EBC: + case MachineType::I386: + case MachineType::IA64: + case MachineType::M32R: + case MachineType::MIPS16: + case MachineType::MIPSFPU: + case MachineType::MIPSFPU16: + case MachineType::POWERPC: + case MachineType::POWERPCFP: + case MachineType::R4000: + case MachineType::RISCV32: + case MachineType::RISCV64: + case MachineType::RISCV128: + case MachineType::SH3: + case MachineType::SH3DSP: + case MachineType::SH4: + case MachineType::SH5: + case MachineType::THUMB: + case MachineType::WCEMIPSV2: return t; + default: Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown machine type code 0x%x", value); + } + } +} diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp new file mode 100644 index 000000000..2a6d3dff2 --- /dev/null +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -0,0 +1,174 @@ +#include "pch.h" + +#include +#include +#include + +namespace vcpkg::Strings::details +{ + // To disambiguate between two overloads + static const auto isspace = [](const char c) { return std::isspace(c); }; + + // Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower() + static char tolower_char(const char c) { return static_cast(std::tolower(c)); } + +#if defined(_WIN32) + static _locale_t& c_locale() + { + static _locale_t c_locale_impl = _create_locale(LC_ALL, "C"); + return c_locale_impl; + } +#endif + + std::string format_internal(const char* fmtstr, ...) + { + va_list args; + va_start(args, fmtstr); + +#if defined(_WIN32) + const int sz = _vscprintf_l(fmtstr, c_locale(), args); +#else + const int sz = vsnprintf(nullptr, 0, fmtstr, args); +#endif + Checks::check_exit(VCPKG_LINE_INFO, sz > 0); + + std::string output(sz, '\0'); + +#if defined(_WIN32) + _vsnprintf_s_l(&output.at(0), output.size() + 1, output.size(), fmtstr, c_locale(), args); +#else + vsnprintf(&output.at(0), output.size() + 1, fmtstr, args); +#endif + va_end(args); + + return output; + } + + std::wstring wformat_internal(const wchar_t* fmtstr, ...) + { + va_list args; + va_start(args, fmtstr); + +#if defined(_WIN32) + const int sz = _vscwprintf_l(fmtstr, c_locale(), args); +#else + const int sz = vswprintf(nullptr, 0, fmtstr, args); +#endif + Checks::check_exit(VCPKG_LINE_INFO, sz > 0); + + std::wstring output(sz, L'\0'); + +#if defined(_WIN32) + _vsnwprintf_s_l(&output.at(0), output.size() + 1, output.size(), fmtstr, c_locale(), args); +#else + vswprintf(&output.at(0), output.size() + 1, fmtstr, args); +#endif + va_end(args); + + return output; + } +} + +namespace vcpkg::Strings +{ + std::wstring to_utf16(const CStringView s) + { + const int size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, nullptr, 0); + std::wstring output; + output.resize(size - 1); + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, output.data(), size - 1); + return output; + } + + std::string to_utf8(const CWStringView w) + { + const int size = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, nullptr, 0, nullptr, nullptr); + std::string output; + output.resize(size - 1); + WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, output.data(), size - 1, nullptr, nullptr); + return output; + } + + std::string::const_iterator case_insensitive_ascii_find(const std::string& s, const std::string& pattern) + { + const std::string pattern_as_lower_case(ascii_to_lowercase(pattern)); + return search(s.begin(), + s.end(), + pattern_as_lower_case.begin(), + pattern_as_lower_case.end(), + [](const char a, const char b) { return details::tolower_char(a) == b; }); + } + + bool case_insensitive_ascii_contains(const std::string& s, const std::string& pattern) + { + return case_insensitive_ascii_find(s, pattern) != s.end(); + } + + bool case_insensitive_ascii_compare(const CStringView left, const CStringView right) + { + return _stricmp(left.c_str(), right.c_str()) == 0; + } + + std::string ascii_to_lowercase(const std::string& input) + { + std::string output(input); + std::transform(output.begin(), output.end(), output.begin(), &details::tolower_char); + return output; + } + + bool case_insensitive_ascii_starts_with(const std::string& s, const std::string& pattern) + { + return _strnicmp(s.c_str(), pattern.c_str(), pattern.size()) == 0; + } + + void trim(std::string* s) + { + s->erase(std::find_if_not(s->rbegin(), s->rend(), details::isspace).base(), s->end()); + s->erase(s->begin(), std::find_if_not(s->begin(), s->end(), details::isspace)); + } + + std::string trimmed(const std::string& s) + { + auto whitespace_back = std::find_if_not(s.rbegin(), s.rend(), details::isspace).base(); + auto whitespace_front = std::find_if_not(s.begin(), whitespace_back, details::isspace); + return std::string(whitespace_front, whitespace_back); + } + + void trim_all_and_remove_whitespace_strings(std::vector* strings) + { + for (std::string& s : *strings) + { + trim(&s); + } + + Util::erase_remove_if(*strings, [](const std::string& s) { return s.empty(); }); + } + + std::vector split(const std::string& s, const std::string& delimiter) + { + std::vector output; + + if (delimiter.empty()) + { + output.push_back(s); + return output; + } + + const size_t delimiter_length = delimiter.length(); + size_t i = 0; + for (size_t pos = s.find(delimiter); pos != std::string::npos; pos = s.find(delimiter, pos)) + { + output.push_back(s.substr(i, pos - i)); + pos += delimiter_length; + i = pos; + } + + // Add the rest of the string after the last delimiter, unless there is nothing after it + if (i != s.length()) + { + output.push_back(s.substr(i, s.length())); + } + + return output; + } +} diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp new file mode 100644 index 000000000..716c2a6a6 --- /dev/null +++ b/toolsrc/src/vcpkg/base/system.cpp @@ -0,0 +1,350 @@ +#include "pch.h" + +#include +#include +#include + +#pragma comment(lib, "Advapi32") + +namespace vcpkg::System +{ + tm get_current_date_time() + { + using std::chrono::system_clock; + std::time_t now_time = system_clock::to_time_t(system_clock::now()); + tm parts; + localtime_s(&parts, &now_time); + return parts; + } + + fs::path get_exe_path_of_current_process() + { + wchar_t buf[_MAX_PATH]; + const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH); + if (bytes == 0) std::abort(); + return fs::path(buf, buf + bytes); + } + + Optional to_cpu_architecture(CStringView arch) + { + if (Strings::case_insensitive_ascii_compare(arch, "x86")) return CPUArchitecture::X86; + if (Strings::case_insensitive_ascii_compare(arch, "x64")) return CPUArchitecture::X64; + if (Strings::case_insensitive_ascii_compare(arch, "amd64")) return CPUArchitecture::X64; + if (Strings::case_insensitive_ascii_compare(arch, "arm")) return CPUArchitecture::ARM; + if (Strings::case_insensitive_ascii_compare(arch, "arm64")) return CPUArchitecture::ARM64; + return nullopt; + } + + CPUArchitecture get_host_processor() + { + auto w6432 = get_environment_variable(L"PROCESSOR_ARCHITEW6432"); + if (const auto p = w6432.get()) return to_cpu_architecture(Strings::to_utf8(*p)).value_or_exit(VCPKG_LINE_INFO); + + const auto procarch = get_environment_variable(L"PROCESSOR_ARCHITECTURE").value_or_exit(VCPKG_LINE_INFO); + return to_cpu_architecture(Strings::to_utf8(procarch)).value_or_exit(VCPKG_LINE_INFO); + } + + std::vector get_supported_host_architectures() + { + std::vector supported_architectures; + supported_architectures.push_back(get_host_processor()); + + // AMD64 machines support to run x86 applications + if (supported_architectures.back() == CPUArchitecture::X64) + { + supported_architectures.push_back(CPUArchitecture::X86); + } + + return supported_architectures; + } + + int cmd_execute_clean(const CWStringView cmd_line) + { + static const std::wstring SYSTEM_ROOT = get_environment_variable(L"SystemRoot").value_or_exit(VCPKG_LINE_INFO); + static const std::wstring SYSTEM_32 = SYSTEM_ROOT + LR"(\system32)"; + static const std::wstring NEW_PATH = Strings::wformat( + LR"(Path=%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)", SYSTEM_32, SYSTEM_ROOT, SYSTEM_32, SYSTEM_32); + + std::vector env_wstrings = { + L"ALLUSERSPROFILE", + L"APPDATA", + L"CommonProgramFiles", + L"CommonProgramFiles(x86)", + L"CommonProgramW6432", + L"COMPUTERNAME", + L"ComSpec", + L"HOMEDRIVE", + L"HOMEPATH", + L"LOCALAPPDATA", + L"LOGONSERVER", + L"NUMBER_OF_PROCESSORS", + L"OS", + L"PATHEXT", + L"PROCESSOR_ARCHITECTURE", + L"PROCESSOR_ARCHITEW6432", + L"PROCESSOR_IDENTIFIER", + L"PROCESSOR_LEVEL", + L"PROCESSOR_REVISION", + L"ProgramData", + L"ProgramFiles", + L"ProgramFiles(x86)", + L"ProgramW6432", + L"PROMPT", + L"PSModulePath", + L"PUBLIC", + L"SystemDrive", + L"SystemRoot", + L"TEMP", + L"TMP", + L"USERDNSDOMAIN", + L"USERDOMAIN", + L"USERDOMAIN_ROAMINGPROFILE", + L"USERNAME", + L"USERPROFILE", + L"windir", + // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe + L"HTTP_PROXY", + L"HTTPS_PROXY", + // Enables find_package(CUDA) in CMake + L"CUDA_PATH", + }; + + // Flush stdout before launching external process + fflush(nullptr); + + std::wstring env_cstr; + + for (auto&& env_wstring : env_wstrings) + { + const Optional value = System::get_environment_variable(env_wstring); + const auto v = value.get(); + if (!v || v->empty()) continue; + + env_cstr.append(env_wstring); + env_cstr.push_back(L'='); + env_cstr.append(*v); + env_cstr.push_back(L'\0'); + } + + env_cstr.append(NEW_PATH); + env_cstr.push_back(L'\0'); + + STARTUPINFOW startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFOW)); + startup_info.cb = sizeof(STARTUPINFOW); + + PROCESS_INFORMATION process_info; + memset(&process_info, 0, sizeof(PROCESS_INFORMATION)); + + // Basically we are wrapping it in quotes + std::wstring actual_cmd_line = Strings::wformat(LR"###(cmd.exe /c "%s")###", cmd_line); + Debug::println("CreateProcessW(%s)", Strings::to_utf8(actual_cmd_line)); + bool succeeded = TRUE == CreateProcessW(nullptr, + actual_cmd_line.data(), + nullptr, + nullptr, + FALSE, + BELOW_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, + env_cstr.data(), + nullptr, + &startup_info, + &process_info); + + Checks::check_exit(VCPKG_LINE_INFO, succeeded, "Process creation failed with error code: %lu", GetLastError()); + + CloseHandle(process_info.hThread); + + const DWORD result = WaitForSingleObject(process_info.hProcess, INFINITE); + Checks::check_exit(VCPKG_LINE_INFO, result != WAIT_FAILED, "WaitForSingleObject failed"); + + DWORD exit_code = 0; + GetExitCodeProcess(process_info.hProcess, &exit_code); + + Debug::println("CreateProcessW() returned %lu", exit_code); + return static_cast(exit_code); + } + + int cmd_execute(const CWStringView cmd_line) + { + // Flush stdout before launching external process + fflush(nullptr); + + // Basically we are wrapping it in quotes + const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); + Debug::println("_wsystem(%s)", Strings::to_utf8(actual_cmd_line)); + const int exit_code = _wsystem(actual_cmd_line.c_str()); + Debug::println("_wsystem() returned %d", exit_code); + return exit_code; + } + + // On Win7, output from powershell calls contain a byte order mark, so we strip it out if it is present + static void remove_byte_order_marks(std::wstring* s) + { + const wchar_t* a = s->c_str(); + // This is the UTF-8 byte-order mark + while (s->size() >= 3 && a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF) + { + s->erase(0, 3); + } + } + + ExitCodeAndOutput cmd_execute_and_capture_output(const CWStringView cmd_line) + { + // Flush stdout before launching external process + fflush(stdout); + + const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s 2>&1")###", cmd_line); + + Debug::println("_wpopen(%s)", Strings::to_utf8(actual_cmd_line)); + std::wstring output; + wchar_t buf[1024]; + const auto pipe = _wpopen(actual_cmd_line.c_str(), L"r"); + if (pipe == nullptr) + { + return {1, Strings::to_utf8(output)}; + } + while (fgetws(buf, 1024, pipe)) + { + output.append(buf); + } + if (!feof(pipe)) + { + return {1, Strings::to_utf8(output)}; + } + + const auto ec = _pclose(pipe); + Debug::println("_pclose() returned %d", ec); + remove_byte_order_marks(&output); + return {ec, Strings::to_utf8(output)}; + } + + std::wstring create_powershell_script_cmd(const fs::path& script_path, const CWStringView args) + { + // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned + return Strings::wformat( + LR"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.native(), args); + } + + void println() { println(Strings::EMPTY); } + + void print(const CStringView message) { fputs(message.c_str(), stdout); } + + void println(const CStringView message) + { + print(message); + putchar('\n'); + } + + void print(const Color c, const CStringView message) + { + const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info{}; + GetConsoleScreenBufferInfo(console_handle, &console_screen_buffer_info); + const auto original_color = console_screen_buffer_info.wAttributes; + + SetConsoleTextAttribute(console_handle, static_cast(c) | (original_color & 0xF0)); + print(message); + SetConsoleTextAttribute(console_handle, original_color); + } + + void println(const Color c, const CStringView message) + { + print(c, message); + putchar('\n'); + } + + Optional get_environment_variable(const CWStringView varname) noexcept + { + const auto sz = GetEnvironmentVariableW(varname.c_str(), nullptr, 0); + if (sz == 0) return nullopt; + + std::wstring ret(sz, L'\0'); + + Checks::check_exit(VCPKG_LINE_INFO, MAXDWORD >= ret.size()); + const auto sz2 = GetEnvironmentVariableW(varname.c_str(), ret.data(), static_cast(ret.size())); + Checks::check_exit(VCPKG_LINE_INFO, sz2 + 1 == sz); + ret.pop_back(); + return ret; + } + + static bool is_string_keytype(DWORD hkey_type) + { + return hkey_type == REG_SZ || hkey_type == REG_MULTI_SZ || hkey_type == REG_EXPAND_SZ; + } + + Optional get_registry_string(HKEY base, const CWStringView sub_key, const CWStringView valuename) + { + HKEY k = nullptr; + const LSTATUS ec = RegOpenKeyExW(base, sub_key.c_str(), NULL, KEY_READ, &k); + if (ec != ERROR_SUCCESS) return nullopt; + + DWORD dw_buffer_size = 0; + DWORD dw_type = 0; + auto rc = RegQueryValueExW(k, valuename.c_str(), nullptr, &dw_type, nullptr, &dw_buffer_size); + if (rc != ERROR_SUCCESS || !is_string_keytype(dw_type) || dw_buffer_size == 0 || + dw_buffer_size % sizeof(wchar_t) != 0) + return nullopt; + std::wstring ret; + ret.resize(dw_buffer_size / sizeof(wchar_t)); + + rc = RegQueryValueExW( + k, valuename.c_str(), nullptr, &dw_type, reinterpret_cast(ret.data()), &dw_buffer_size); + if (rc != ERROR_SUCCESS || !is_string_keytype(dw_type) || dw_buffer_size != sizeof(wchar_t) * ret.size()) + return nullopt; + + ret.pop_back(); // remove extra trailing null byte + return ret; + } + + static const fs::path& get_program_files() + { + static const fs::path PATH = System::get_environment_variable(L"PROGRAMFILES").value_or_exit(VCPKG_LINE_INFO); + return PATH; + } + + const fs::path& get_program_files_32_bit() + { + static const fs::path PATH = []() -> fs::path { + auto value = System::get_environment_variable(L"ProgramFiles(x86)"); + if (auto v = value.get()) + { + return std::move(*v); + } + return get_program_files(); + }(); + return PATH; + } + + const fs::path& get_program_files_platform_bitness() + { + static const fs::path PATH = []() -> fs::path { + auto value = System::get_environment_variable(L"ProgramW6432"); + if (auto v = value.get()) + { + return std::move(*v); + } + return get_program_files(); + }(); + return PATH; + } +} + +namespace vcpkg::Debug +{ + void println(const CStringView message) + { + if (GlobalState::debugging) + { + System::println("[DEBUG] %s", message); + } + } + + void println(const System::Color c, const CStringView message) + { + if (GlobalState::debugging) + { + System::println(c, "[DEBUG] %s", message); + } + } +} diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp new file mode 100644 index 000000000..ef943011f --- /dev/null +++ b/toolsrc/src/vcpkg/binaryparagraph.cpp @@ -0,0 +1,123 @@ +#include "pch.h" + +#include +#include +#include + +namespace vcpkg +{ + namespace Fields + { + static const std::string PACKAGE = "Package"; + static const std::string VERSION = "Version"; + static const std::string ARCHITECTURE = "Architecture"; + static const std::string MULTI_ARCH = "Multi-Arch"; + } + + namespace Fields + { + static const std::string FEATURE = "Feature"; + static const std::string DESCRIPTION = "Description"; + static const std::string MAINTAINER = "Maintainer"; + static const std::string DEPENDS = "Depends"; + static const std::string DEFAULTFEATURES = "Default-Features"; + } + + BinaryParagraph::BinaryParagraph() = default; + + BinaryParagraph::BinaryParagraph(std::unordered_map fields) + { + using namespace vcpkg::Parse; + + ParagraphParser parser(std::move(fields)); + + { + std::string name; + parser.required_field(Fields::PACKAGE, name); + std::string architecture; + parser.required_field(Fields::ARCHITECTURE, architecture); + this->spec = PackageSpec::from_name_and_triplet(name, Triplet::from_canonical_name(architecture)) + .value_or_exit(VCPKG_LINE_INFO); + } + + // one or the other + this->version = parser.optional_field(Fields::VERSION); + this->feature = parser.optional_field(Fields::FEATURE); + + this->description = parser.optional_field(Fields::DESCRIPTION); + this->maintainer = parser.optional_field(Fields::MAINTAINER); + + std::string multi_arch; + parser.required_field(Fields::MULTI_ARCH, multi_arch); + + this->depends = parse_comma_list(parser.optional_field(Fields::DEPENDS)); + if (this->feature.empty()) + { + this->default_features = parse_comma_list(parser.optional_field(Fields::DEFAULTFEATURES)); + } + + if (const auto err = parser.error_info(this->spec.to_string())) + { + System::println( + System::Color::error, "Error: while parsing the Binary Paragraph for %s", this->spec.to_string()); + print_error_message(err); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + // prefer failing above when possible because it gives better information + Checks::check_exit(VCPKG_LINE_INFO, multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch); + } + + BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const Triplet& triplet) + { + this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO); + this->version = spgh.version; + this->description = spgh.description; + this->maintainer = spgh.maintainer; + this->depends = filter_dependencies(spgh.depends, triplet); + } + + BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const FeatureParagraph& fpgh, const Triplet& triplet) + { + this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO); + this->version = Strings::EMPTY; + this->feature = fpgh.name; + this->description = fpgh.description; + this->maintainer = Strings::EMPTY; + this->depends = filter_dependencies(fpgh.depends, triplet); + } + + std::string BinaryParagraph::displayname() const + { + const auto f = this->feature.empty() ? "core" : this->feature; + return Strings::format("%s[%s]:%s", this->spec.name(), f, this->spec.triplet()); + } + + std::string BinaryParagraph::dir() const { return this->spec.dir(); } + + std::string BinaryParagraph::fullstem() const + { + return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.triplet()); + } + + void serialize(const BinaryParagraph& pgh, std::string& out_str) + { + out_str.append("Package: ").append(pgh.spec.name()).push_back('\n'); + if (!pgh.version.empty()) + out_str.append("Version: ").append(pgh.version).push_back('\n'); + else if (!pgh.feature.empty()) + out_str.append("Feature: ").append(pgh.feature).push_back('\n'); + if (!pgh.depends.empty()) + { + out_str.append("Depends: "); + out_str.append(Strings::join(", ", pgh.depends)); + out_str.push_back('\n'); + } + + out_str.append("Architecture: ").append(pgh.spec.triplet().to_string()).push_back('\n'); + out_str.append("Multi-Arch: same\n"); + + if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n'); + if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n'); + } +} diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp new file mode 100644 index 000000000..99ec541c1 --- /dev/null +++ b/toolsrc/src/vcpkg/build.cpp @@ -0,0 +1,533 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using vcpkg::Build::BuildResult; +using vcpkg::Parse::ParseControlErrorInfo; +using vcpkg::Parse::ParseExpected; + +namespace vcpkg::Build::Command +{ + using Dependencies::InstallPlanAction; + using Dependencies::InstallPlanType; + + static const std::string OPTION_CHECKS_ONLY = "--checks-only"; + + void perform_and_exit(const FullPackageSpec& full_spec, + const fs::path& port_dir, + const std::unordered_set& options, + const VcpkgPaths& paths) + { + const PackageSpec& spec = full_spec.package_spec; + if (options.find(OPTION_CHECKS_ONLY) != options.end()) + { + const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, spec.triplet()); + const auto build_info = Build::read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec)); + const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info); + Checks::check_exit(VCPKG_LINE_INFO, error_count == 0); + Checks::exit_success(VCPKG_LINE_INFO); + } + + const ParseExpected source_control_file = + Paragraphs::try_load_port(paths.get_filesystem(), port_dir); + + if (!source_control_file.has_value()) + { + print_error_message(source_control_file.error()); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + for (const std::string& str : full_spec.features) + { + System::println("%s \n", str); + } + const auto& scf = source_control_file.value_or_exit(VCPKG_LINE_INFO); + Checks::check_exit(VCPKG_LINE_INFO, + spec.name() == scf->core_paragraph->name, + "The Name: field inside the CONTROL does not match the port directory: '%s' != '%s'", + scf->core_paragraph->name, + spec.name()); + + const StatusParagraphs status_db = database_load_check(paths); + const Build::BuildPackageOptions build_package_options{Build::UseHeadVersion::NO, Build::AllowDownloads::YES}; + + const Build::BuildPackageConfig build_config{ + *scf->core_paragraph, spec.triplet(), paths.port_dir(spec), build_package_options}; + + const auto result = Build::build_package(paths, build_config, status_db); + if (result.code == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES) + { + System::println(System::Color::error, + "The build command requires all dependencies to be already installed."); + System::println("The following dependencies are missing:"); + System::println(); + for (const auto& p : result.unmet_dependencies) + { + System::println(" %s", p); + } + System::println(); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + if (result.code != BuildResult::SUCCEEDED) + { + System::println(System::Color::error, Build::create_error_message(result.code, spec)); + System::println(Build::create_user_troubleshooting_message(spec)); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + static const std::string EXAMPLE = Help::create_example_string("build zlib:x64-windows"); + // Build only takes a single package and all dependencies must already be installed + args.check_exact_arg_count(1, EXAMPLE); + const std::string command_argument = args.command_arguments.at(0); + const FullPackageSpec spec = Input::check_and_get_full_package_spec(command_argument, default_triplet, EXAMPLE); + Input::check_triplet(spec.package_spec.triplet(), paths); + const std::unordered_set options = + args.check_and_get_optional_command_arguments({OPTION_CHECKS_ONLY}); + perform_and_exit(spec, paths.port_dir(spec.package_spec), options, paths); + } +} + +namespace vcpkg::Build +{ + static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage"; + static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs"; + static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT"; + static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder"; + static const std::string NAME_ALLOW_OBSOLETE_MSVCRT = "PolicyAllowObsoleteMsvcrt"; + + const std::string& to_string(BuildPolicy policy) + { + switch (policy) + { + case BuildPolicy::EMPTY_PACKAGE: return NAME_EMPTY_PACKAGE; + case BuildPolicy::DLLS_WITHOUT_LIBS: return NAME_DLLS_WITHOUT_LIBS; + case BuildPolicy::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT; + case BuildPolicy::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER; + case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return NAME_ALLOW_OBSOLETE_MSVCRT; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + CStringView to_cmake_variable(BuildPolicy policy) + { + switch (policy) + { + case BuildPolicy::EMPTY_PACKAGE: return "VCPKG_POLICY_EMPTY_PACKAGE"; + case BuildPolicy::DLLS_WITHOUT_LIBS: return "VCPKG_POLICY_DLLS_WITHOUT_LIBS"; + case BuildPolicy::ONLY_RELEASE_CRT: return "VCPKG_POLICY_ONLY_RELEASE_CRT"; + case BuildPolicy::EMPTY_INCLUDE_FOLDER: return "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER"; + case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return "VCPKG_POLICY_ALLOW_OBSOLETE_MSVCRT"; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + Optional to_linkage_type(const std::string& str) + { + if (str == "dynamic") return LinkageType::DYNAMIC; + if (str == "static") return LinkageType::STATIC; + return nullopt; + } + + namespace BuildInfoRequiredField + { + static const std::string CRT_LINKAGE = "CRTLinkage"; + static const std::string LIBRARY_LINKAGE = "LibraryLinkage"; + } + + CWStringView to_vcvarsall_target(const std::string& cmake_system_name) + { + if (cmake_system_name == Strings::EMPTY) return Strings::WEMPTY; + if (cmake_system_name == "Windows") return Strings::WEMPTY; + if (cmake_system_name == "WindowsStore") return L"store"; + + Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported vcvarsall target %s", cmake_system_name); + } + + CWStringView to_vcvarsall_toolchain(const std::string& target_architecture, const Toolset& toolset) + { + auto maybe_target_arch = System::to_cpu_architecture(target_architecture); + Checks::check_exit( + VCPKG_LINE_INFO, maybe_target_arch.has_value(), "Invalid architecture string: %s", target_architecture); + auto target_arch = maybe_target_arch.value_or_exit(VCPKG_LINE_INFO); + auto host_architectures = System::get_supported_host_architectures(); + + for (auto&& host : host_architectures) + { + auto it = Util::find_if(toolset.supported_architectures, [&](const ToolsetArchOption& opt) { + return host == opt.host_arch && target_arch == opt.target_arch; + }); + if (it != toolset.supported_architectures.end()) return it->name; + } + + Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported toolchain combination %s", target_architecture); + } + + std::wstring make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) + { + const wchar_t* tonull = L" >nul"; + if (GlobalState::debugging) + { + tonull = Strings::WEMPTY; + } + + const auto arch = to_vcvarsall_toolchain(pre_build_info.target_architecture, toolset); + const auto target = to_vcvarsall_target(pre_build_info.cmake_system_name); + + return Strings::wformat(LR"("%s" %s %s %s %s 2>&1)", + toolset.vcvarsall.native(), + Strings::join(L" ", toolset.vcvarsall_options), + arch, + target, + tonull); + } + + static void create_binary_feature_control_file(const SourceParagraph& source_paragraph, + const FeatureParagraph& feature_paragraph, + const Triplet& triplet, + BinaryControlFile& bcf) + { + BinaryParagraph bpgh(source_paragraph, feature_paragraph, triplet); + bcf.features.emplace_back(std::move(bpgh)); + } + + static void create_binary_control_file(const SourceParagraph& source_paragraph, + const Triplet& triplet, + const BuildInfo& build_info, + BinaryControlFile& bcf) + { + BinaryParagraph bpgh(source_paragraph, triplet); + if (const auto p_ver = build_info.version.get()) + { + bpgh.version = *p_ver; + } + bcf.core_paragraph = std::move(bpgh); + } + + static void write_binary_control_file(const VcpkgPaths& paths, BinaryControlFile bcf) + { + std::string start = Strings::serialize(bcf.core_paragraph); + for (auto&& feature : bcf.features) + { + start += "\n" + Strings::serialize(feature); + } + const fs::path binary_control_file = paths.packages / bcf.core_paragraph.dir() / "CONTROL"; + paths.get_filesystem().write_contents(binary_control_file, start); + } + + ExtendedBuildResult build_package(const VcpkgPaths& paths, + const BuildPackageConfig& config, + const StatusParagraphs& status_db) + { + const PackageSpec spec = + PackageSpec::from_name_and_triplet(config.src.name, config.triplet).value_or_exit(VCPKG_LINE_INFO); + + const Triplet& triplet = config.triplet; + { + std::vector missing_specs; + for (auto&& dep : filter_dependencies(config.src.depends, triplet)) + { + if (status_db.find_installed(dep, triplet) == status_db.end()) + { + missing_specs.push_back( + PackageSpec::from_name_and_triplet(dep, triplet).value_or_exit(VCPKG_LINE_INFO)); + } + } + // Fail the build if any dependencies were missing + if (!missing_specs.empty()) + { + return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_specs)}; + } + } + + const fs::path& cmake_exe_path = paths.get_cmake_exe(); + const fs::path& git_exe_path = paths.get_git_exe(); + + const fs::path ports_cmake_script_path = paths.ports_cmake; + const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet); + const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); + const auto cmd_set_environment = make_build_env_cmd(pre_build_info, toolset); + + std::string features; + if (GlobalState::feature_packages) + { + if (config.feature_list) + { + for (auto&& feature : *config.feature_list) + { + features.append(feature + ";"); + } + if (features.size() > 0) + { + features.pop_back(); + } + } + } + + const std::wstring cmd_launch_cmake = make_cmake_cmd( + cmake_exe_path, + ports_cmake_script_path, + { + {L"CMD", L"BUILD"}, + {L"PORT", config.src.name}, + {L"CURRENT_PORT_DIR", config.port_dir / "/."}, + {L"TARGET_TRIPLET", triplet.canonical_name()}, + {L"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()}, + {L"VCPKG_USE_HEAD_VERSION", to_bool(config.build_package_options.use_head_version) ? L"1" : L"0"}, + {L"_VCPKG_NO_DOWNLOADS", !to_bool(config.build_package_options.allow_downloads) ? L"1" : L"0"}, + {L"GIT", git_exe_path}, + {L"FEATURES", features}, + }); + + const std::wstring command = Strings::wformat(LR"(%s && %s)", cmd_set_environment, cmd_launch_cmake); + + const auto timer = Chrono::ElapsedTime::create_started(); + + const int return_code = System::cmd_execute_clean(command); + const auto buildtimeus = timer.microseconds(); + const auto spec_string = spec.to_string(); + + { + auto locked_metrics = Metrics::g_metrics.lock(); + locked_metrics->track_metric("buildtimeus-" + spec_string, buildtimeus); + if (return_code != 0) + { + locked_metrics->track_property("error", "build failed"); + locked_metrics->track_property("build_error", spec_string); + return {BuildResult::BUILD_FAILED, {}}; + } + } + + const BuildInfo build_info = read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec)); + const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info); + + BinaryControlFile bcf; + + create_binary_control_file(config.src, triplet, build_info, bcf); + + if (error_count != 0) + { + return {BuildResult::POST_BUILD_CHECKS_FAILED, {}}; + } + if (GlobalState::feature_packages) + { + if (config.feature_list) + { + for (auto&& feature : *config.feature_list) + { + for (auto&& f_pgh : config.scf->feature_paragraphs) + { + if (f_pgh->name == feature) + create_binary_feature_control_file(*config.scf->core_paragraph, *f_pgh, triplet, bcf); + } + } + } + } + + write_binary_control_file(paths, bcf); + + // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name; + // delete_directory(port_buildtrees_dir); + + return {BuildResult::SUCCEEDED, {}}; + } + + const std::string& to_string(const BuildResult build_result) + { + static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string("vcpkg::Commands::Build::BuildResult"); + static const std::string SUCCEEDED_STRING = "SUCCEEDED"; + static const std::string BUILD_FAILED_STRING = "BUILD_FAILED"; + static const std::string FILE_CONFLICTS_STRING = "FILE_CONFLICTS"; + static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED"; + static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES"; + + switch (build_result) + { + case BuildResult::NULLVALUE: return NULLVALUE_STRING; + case BuildResult::SUCCEEDED: return SUCCEEDED_STRING; + case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING; + case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING; + case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; + case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + std::string create_error_message(const BuildResult build_result, const PackageSpec& spec) + { + return Strings::format("Error: Building package %s failed with: %s", spec, Build::to_string(build_result)); + } + + std::string create_user_troubleshooting_message(const PackageSpec& spec) + { + return Strings::format("Please ensure you're using the latest portfiles with `.\\vcpkg update`, then\n" + "submit an issue at https://github.com/Microsoft/vcpkg/issues including:\n" + " Package: %s\n" + " Vcpkg version: %s\n" + "\n" + "Additionally, attach any relevant sections from the log files above.", + spec, + Commands::Version::version()); + } + + static BuildInfo inner_create_buildinfo(std::unordered_map pgh) + { + Parse::ParagraphParser parser(std::move(pgh)); + + BuildInfo build_info; + + { + std::string crt_linkage_as_string; + parser.required_field(BuildInfoRequiredField::CRT_LINKAGE, crt_linkage_as_string); + + auto crtlinkage = to_linkage_type(crt_linkage_as_string); + if (const auto p = crtlinkage.get()) + build_info.crt_linkage = *p; + else + Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid crt linkage type: [%s]", crt_linkage_as_string); + } + + { + std::string library_linkage_as_string; + parser.required_field(BuildInfoRequiredField::LIBRARY_LINKAGE, library_linkage_as_string); + auto liblinkage = to_linkage_type(library_linkage_as_string); + if (const auto p = liblinkage.get()) + build_info.library_linkage = *p; + else + Checks::exit_with_message( + VCPKG_LINE_INFO, "Invalid library linkage type: [%s]", library_linkage_as_string); + } + std::string version = parser.optional_field("Version"); + if (!version.empty()) build_info.version = std::move(version); + + std::map policies; + for (auto policy : G_ALL_POLICIES) + { + const auto setting = parser.optional_field(to_string(policy)); + if (setting.empty()) continue; + if (setting == "enabled") + policies.emplace(policy, true); + else if (setting == "disabled") + policies.emplace(policy, false); + else + Checks::exit_with_message( + VCPKG_LINE_INFO, "Unknown setting for policy '%s': %s", to_string(policy), setting); + } + + if (const auto err = parser.error_info("PostBuildInformation")) + { + print_error_message(err); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + build_info.policies = BuildPolicies(std::move(policies)); + + return build_info; + } + + BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath) + { + const Expected> pghs = + Paragraphs::get_single_paragraph(fs, filepath); + Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package"); + return inner_create_buildinfo(*pghs.get()); + } + + PreBuildInfo PreBuildInfo::from_triplet_file(const VcpkgPaths& paths, const Triplet& triplet) + { + static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb"; + + const fs::path& cmake_exe_path = paths.get_cmake_exe(); + const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake"; + const fs::path triplet_file_path = paths.triplets / (triplet.canonical_name() + ".cmake"); + + const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe_path, + ports_cmake_script_path, + { + {L"CMAKE_TRIPLET_FILE", triplet_file_path}, + }); + + const std::wstring command = Strings::wformat(LR"(%s)", cmd_launch_cmake); + const auto ec_data = System::cmd_execute_and_capture_output(command); + Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0); + + const std::vector lines = Strings::split(ec_data.output, "\n"); + + PreBuildInfo pre_build_info; + + const auto e = lines.cend(); + auto cur = std::find(lines.cbegin(), e, FLAG_GUID); + if (cur != e) ++cur; + + for (; cur != e; ++cur) + { + auto&& line = *cur; + + const std::vector s = Strings::split(line, "="); + Checks::check_exit(VCPKG_LINE_INFO, + s.size() == 1 || s.size() == 2, + "Expected format is [VARIABLE_NAME=VARIABLE_VALUE], but was [%s]", + line); + + const bool variable_with_no_value = s.size() == 1; + const std::string variable_name = s.at(0); + const std::string variable_value = variable_with_no_value ? Strings::EMPTY : s.at(1); + + if (variable_name == "VCPKG_TARGET_ARCHITECTURE") + { + pre_build_info.target_architecture = variable_value; + continue; + } + + if (variable_name == "VCPKG_CMAKE_SYSTEM_NAME") + { + pre_build_info.cmake_system_name = variable_value; + continue; + } + + if (variable_name == "VCPKG_CMAKE_SYSTEM_VERSION") + { + pre_build_info.cmake_system_version = variable_value; + continue; + } + + if (variable_name == "VCPKG_PLATFORM_TOOLSET") + { + pre_build_info.platform_toolset = + variable_value.empty() ? nullopt : Optional{variable_value}; + continue; + } + + if (variable_name == "VCPKG_VISUAL_STUDIO_PATH") + { + pre_build_info.visual_studio_path = + variable_value.empty() ? nullopt : Optional{variable_value}; + continue; + } + + Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line); + } + + return pre_build_info; + } +} diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp new file mode 100644 index 000000000..ad2a5b227 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp @@ -0,0 +1,78 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Autocomplete +{ + std::vector autocomplete_install( + const std::vector>& source_paragraphs, const std::string& start_with) + { + std::vector results; + const auto& istartswith = Strings::case_insensitive_ascii_starts_with; + + for (const auto& source_control_file : source_paragraphs) + { + auto&& sp = *source_control_file->core_paragraph; + + if (istartswith(sp.name, start_with)) + { + results.push_back(sp.name); + } + } + return results; + } + + std::vector autocomplete_remove(std::vector installed_packages, + const std::string& start_with) + { + std::vector results; + const auto& istartswith = Strings::case_insensitive_ascii_starts_with; + + for (const auto& installed_package : installed_packages) + { + auto sp = installed_package->package.displayname(); + + if (istartswith(sp, start_with)) + { + results.push_back(sp); + } + } + return results; + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format("The argument should be a command line to autocomplete.\n%s", + Help::create_example_string("autocomplete install z")); + + args.check_min_arg_count(1, EXAMPLE); + args.check_max_arg_count(2, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + const std::string requested_command = args.command_arguments.at(0); + const std::string start_with = + args.command_arguments.size() > 1 ? args.command_arguments.at(1) : Strings::EMPTY; + std::vector results; + if (requested_command == "install") + { + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + auto& source_paragraphs = sources_and_errors.paragraphs; + + results = autocomplete_install(source_paragraphs, start_with); + } + else if (requested_command == "remove") + { + const StatusParagraphs status_db = database_load_check(paths); + std::vector installed_packages = get_installed_ports(status_db); + results = autocomplete_remove(installed_packages, start_with); + } + + System::println(Strings::join(" ", results)); + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.buildexternal.cpp b/toolsrc/src/vcpkg/commands.buildexternal.cpp new file mode 100644 index 000000000..3991beb62 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.buildexternal.cpp @@ -0,0 +1,23 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Commands::BuildExternal +{ + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + static const std::string EXAMPLE = + Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"); + args.check_exact_arg_count(2, EXAMPLE); + const FullPackageSpec spec = + Input::check_and_get_full_package_spec(args.command_arguments.at(0), default_triplet, EXAMPLE); + Input::check_triplet(spec.package_spec.triplet(), paths); + const std::unordered_set options = args.check_and_get_optional_command_arguments({}); + + const fs::path port_dir = args.command_arguments.at(1); + Build::Command::perform_and_exit(spec, port_dir, options, paths); + } +} diff --git a/toolsrc/src/vcpkg/commands.cache.cpp b/toolsrc/src/vcpkg/commands.cache.cpp new file mode 100644 index 000000000..77f0a20f6 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.cache.cpp @@ -0,0 +1,69 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Cache +{ + static std::vector read_all_binary_paragraphs(const VcpkgPaths& paths) + { + std::vector output; + for (auto&& path : paths.get_filesystem().get_files_non_recursive(paths.packages)) + { + const Expected> pghs = + Paragraphs::get_single_paragraph(paths.get_filesystem(), path / "CONTROL"); + if (const auto p = pghs.get()) + { + const BinaryParagraph binary_paragraph = BinaryParagraph(*p); + output.push_back(binary_paragraph); + } + } + + return output; + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format( + "The argument should be a substring to search for, or no argument to display all cached libraries.\n%s", + Help::create_example_string("cache png")); + args.check_max_arg_count(1, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + const std::vector binary_paragraphs = read_all_binary_paragraphs(paths); + if (binary_paragraphs.empty()) + { + System::println("No packages are cached."); + Checks::exit_success(VCPKG_LINE_INFO); + } + + if (args.command_arguments.size() == 0) + { + for (const BinaryParagraph& binary_paragraph : binary_paragraphs) + { + const std::string displayname = binary_paragraph.displayname(); + System::println(displayname); + } + } + else + { + // At this point there is 1 argument + for (const BinaryParagraph& binary_paragraph : binary_paragraphs) + { + const std::string displayname = binary_paragraph.displayname(); + if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end()) + { + continue; + } + + System::println(displayname); + } + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp new file mode 100644 index 000000000..1c98d1d83 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.ci.cpp @@ -0,0 +1,62 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::CI +{ + using Build::BuildResult; + using Dependencies::InstallPlanAction; + using Dependencies::InstallPlanType; + + static std::vector load_all_package_specs(Files::Filesystem& fs, + const fs::path& ports_directory, + const Triplet& triplet) + { + auto ports = Paragraphs::load_all_ports(fs, ports_directory); + return Util::fmap(ports, [&](auto&& control_file) -> PackageSpec { + return PackageSpec::from_name_and_triplet(control_file->core_paragraph->name, triplet) + .value_or_exit(VCPKG_LINE_INFO); + }); + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + static const std::string EXAMPLE = Help::create_example_string("ci x64-windows"); + args.check_max_arg_count(1, EXAMPLE); + const Triplet triplet = args.command_arguments.size() == 1 + ? Triplet::from_canonical_name(args.command_arguments.at(0)) + : default_triplet; + Input::check_triplet(triplet, paths); + args.check_and_get_optional_command_arguments({}); + const std::vector specs = load_all_package_specs(paths.get_filesystem(), paths.ports, triplet); + + StatusParagraphs status_db = database_load_check(paths); + const auto& paths_port_file = Dependencies::PathsPortFile(paths); + std::vector install_plan = + Dependencies::create_install_plan(paths_port_file, specs, status_db); + Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty"); + + const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO, Build::AllowDownloads::YES}; + + const std::vector action_plan = + Util::fmap(install_plan, [](InstallPlanAction& install_action) { + return Dependencies::AnyAction(std::move(install_action)); + }); + + Install::perform_and_exit_ex( + action_plan, install_plan_options, Install::KeepGoing::YES, Install::PrintSummary::YES, paths, status_db); + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.contact.cpp b/toolsrc/src/vcpkg/commands.contact.cpp new file mode 100644 index 000000000..421b8a230 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.contact.cpp @@ -0,0 +1,22 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg::Commands::Contact +{ + const std::string& email() + { + static const std::string S_EMAIL = R"(vcpkg@microsoft.com)"; + return S_EMAIL; + } + + void perform_and_exit(const VcpkgCmdArguments& args) + { + args.check_exact_arg_count(0); + args.check_and_get_optional_command_arguments({}); + + System::println("Send an email to %s with any feedback.", email()); + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp new file mode 100644 index 000000000..9d969ea28 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.cpp @@ -0,0 +1,56 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands +{ + Span> get_available_commands_type_a() + { + static std::vector> t = { + {"install", &Install::perform_and_exit}, + {"ci", &CI::perform_and_exit}, + {"remove", &Remove::perform_and_exit}, + {"build", &Build::Command::perform_and_exit}, + {"env", &Env::perform_and_exit}, + {"build-external", &BuildExternal::perform_and_exit}, + {"export", &Export::perform_and_exit}, + }; + return t; + } + + Span> get_available_commands_type_b() + { + static std::vector> t = { + {"/?", &Help::perform_and_exit}, + {"help", &Help::perform_and_exit}, + {"search", &Search::perform_and_exit}, + {"list", &List::perform_and_exit}, + {"integrate", &Integrate::perform_and_exit}, + {"owns", &Owns::perform_and_exit}, + {"update", &Update::perform_and_exit}, + {"depend-info", &DependInfo::perform_and_exit}, + {"edit", &Edit::perform_and_exit}, + {"create", &Create::perform_and_exit}, + {"import", &Import::perform_and_exit}, + {"cache", &Cache::perform_and_exit}, + {"portsdiff", &PortsDiff::perform_and_exit}, + {"autocomplete", &Autocomplete::perform_and_exit}}; + return t; + } + + Span> get_available_commands_type_c() + { + static std::vector> t = { + {"version", &Version::perform_and_exit}, + {"contact", &Contact::perform_and_exit}, + {"hash", &Hash::perform_and_exit}, + }; + return t; + } +} diff --git a/toolsrc/src/vcpkg/commands.create.cpp b/toolsrc/src/vcpkg/commands.create.cpp new file mode 100644 index 000000000..d923cf555 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.create.cpp @@ -0,0 +1,39 @@ +#include "pch.h" + +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Create +{ + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Help::create_example_string( + R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###"); + args.check_max_arg_count(3, EXAMPLE); + args.check_min_arg_count(2, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + const std::string port_name = args.command_arguments.at(0); + const std::string url = args.command_arguments.at(1); + + const fs::path& cmake_exe = paths.get_cmake_exe(); + + std::vector cmake_args{{L"CMD", L"CREATE"}, {L"PORT", port_name}, {L"URL", url}}; + + if (args.command_arguments.size() >= 3) + { + const std::string& zip_file_name = args.command_arguments.at(2); + Checks::check_exit(VCPKG_LINE_INFO, + !Files::has_invalid_chars_for_filesystem(zip_file_name), + R"(Filename cannot contain invalid chars %s, but was %s)", + Files::FILESYSTEM_INVALID_CHARACTERS, + zip_file_name); + cmake_args.push_back({L"FILENAME", zip_file_name}); + } + + const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe, paths.ports_cmake, cmake_args); + Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute_clean(cmd_launch_cmake)); + } +} diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp new file mode 100644 index 000000000..89c7e0c7f --- /dev/null +++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp @@ -0,0 +1,56 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::DependInfo +{ + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Help::create_example_string(R"###(depend-info [pat])###"); + args.check_max_arg_count(1, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + std::vector> source_control_files = + Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); + + if (args.command_arguments.size() == 1) + { + const std::string filter = args.command_arguments.at(0); + + Util::erase_remove_if(source_control_files, + [&](const std::unique_ptr& source_control_file) { + + const SourceParagraph& source_paragraph = *source_control_file->core_paragraph; + + if (Strings::case_insensitive_ascii_contains(source_paragraph.name, filter)) + { + return false; + } + + for (const Dependency& dependency : source_paragraph.depends) + { + if (Strings::case_insensitive_ascii_contains(dependency.name(), filter)) + { + return false; + } + } + + return true; + }); + } + + for (auto&& source_control_file : source_control_files) + { + const SourceParagraph& source_paragraph = *source_control_file->core_paragraph; + const auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name(); }); + System::println("%s: %s", source_paragraph.name, s); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp new file mode 100644 index 000000000..9497bd76e --- /dev/null +++ b/toolsrc/src/vcpkg/commands.edit.cpp @@ -0,0 +1,87 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Commands::Edit +{ + static std::vector find_from_registry() + { + static const std::array REGKEYS = { + LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)", + LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1)", + LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)", + }; + + std::vector output; + for (auto&& keypath : REGKEYS) + { + const Optional code_installpath = + System::get_registry_string(HKEY_LOCAL_MACHINE, keypath, L"InstallLocation"); + if (const auto c = code_installpath.get()) + { + const fs::path install_path = fs::path(*c); + output.push_back(install_path / "Code - Insiders.exe"); + output.push_back(install_path / "Code.exe"); + } + } + return output; + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string OPTION_BUILDTREES = "--buildtrees"; + + static const fs::path VS_CODE_INSIDERS = fs::path{"Microsoft VS Code Insiders"} / "Code - Insiders.exe"; + static const fs::path VS_CODE = fs::path{"Microsoft VS Code"} / "Code.exe"; + + auto& fs = paths.get_filesystem(); + + static const std::string EXAMPLE = Help::create_example_string("edit zlib"); + args.check_exact_arg_count(1, EXAMPLE); + const std::unordered_set options = + args.check_and_get_optional_command_arguments({OPTION_BUILDTREES}); + const std::string port_name = args.command_arguments.at(0); + + const fs::path portpath = paths.ports / port_name; + Checks::check_exit(VCPKG_LINE_INFO, fs.is_directory(portpath), R"(Could not find port named "%s")", port_name); + + std::vector candidate_paths; + const std::vector from_path = Files::find_from_PATH(L"EDITOR"); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(System::get_program_files_platform_bitness() / VS_CODE_INSIDERS); + candidate_paths.push_back(System::get_program_files_32_bit() / VS_CODE_INSIDERS); + candidate_paths.push_back(System::get_program_files_platform_bitness() / VS_CODE); + candidate_paths.push_back(System::get_program_files_32_bit() / VS_CODE); + + const std::vector from_registry = find_from_registry(); + candidate_paths.insert(candidate_paths.end(), from_registry.cbegin(), from_registry.cend()); + + auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { return fs.exists(p); }); + if (it == candidate_paths.cend()) + { + System::println(System::Color::error, + "Error: Visual Studio Code was not found and the environment variable EDITOR is not set."); + System::println("The following paths were examined:"); + Files::print_paths(candidate_paths); + System::println("You can also set the environmental variable EDITOR to your editor of choice."); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + const fs::path env_editor = *it; + if (options.find(OPTION_BUILDTREES) != options.cend()) + { + const auto buildtrees_current_dir = paths.buildtrees / port_name; + + const std::wstring cmd_line = + Strings::wformat(LR"("%s" "%s" -n)", env_editor, buildtrees_current_dir.native()); + Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute(cmd_line)); + } + + const std::wstring cmd_line = Strings::wformat( + LR"("%s" "%s" "%s" -n)", env_editor, portpath.native(), (portpath / "portfile.cmake").native()); + Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute(cmd_line)); + } +} diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp new file mode 100644 index 000000000..089881588 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.env.cpp @@ -0,0 +1,22 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Commands::Env +{ + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + static const std::string EXAMPLE = Help::create_example_string(R"(env --triplet x64-windows)"); + args.check_exact_arg_count(0, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet); + const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); + System::cmd_execute_clean(Build::make_build_env_cmd(pre_build_info, toolset) + L" && cmd"); + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp new file mode 100644 index 000000000..35a3c97a7 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.exportifw.cpp @@ -0,0 +1,482 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Export::IFW +{ + using Dependencies::ExportPlanAction; + using Dependencies::ExportPlanType; + using Install::InstallDir; + + static std::string create_release_date() + { + const tm date_time = System::get_current_date_time(); + + // Format is: YYYY-mm-dd + // 10 characters + 1 null terminating character will be written for a total of 11 chars + char mbstr[11]; + const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d", &date_time); + Checks::check_exit(VCPKG_LINE_INFO, + bytes_written == 10, + "Expected 10 bytes to be written, but %u were written", + bytes_written); + const std::string date_time_as_string(mbstr); + return date_time_as_string; + } + + fs::path get_packages_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_packages_dir_path.has_value() + ? fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-packages"); + } + + fs::path get_repository_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_repository_dir_path.has_value() + ? fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-repository"); + } + + fs::path get_config_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_config_file_path.has_value() + ? fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-configuration.xml"); + } + + fs::path get_installer_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + return ifw_options.maybe_installer_file_path.has_value() + ? fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO)) + : paths.root / (export_id + "-ifw-installer.exe"); + } + + fs::path export_real_package(const fs::path& ifw_packages_dir_path, + const ExportPlanAction& action, + Files::Filesystem& fs) + { + std::error_code ec; + + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + // Prepare meta dir + const fs::path package_xml_file_path = + ifw_packages_dir_path / + Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" / + "package.xml"; + const fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + + auto deps = Strings::join( + ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; }); + + if (!deps.empty()) deps = "\n " + deps + ""; + + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + %s + %s + %s + packages.%s:,triplets.%s:%s + true + +)###", + action.spec.to_string(), + binary_paragraph.version, + create_release_date(), + action.spec.name(), + action.spec.triplet().canonical_name(), + deps)); + + // Return dir path for export package data + return ifw_packages_dir_path / + Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" / + "installed"; + } + + void export_unique_packages(const fs::path& raw_exported_dir_path, + std::map unique_packages, + Files::Filesystem& fs) + { + std::error_code ec; + + // packages + + fs::path package_xml_file_path = raw_exported_dir_path / "packages" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + Packages + 1.0.0 + %s + +)###", + create_release_date())); + + for (auto package = unique_packages.begin(); package != unique_packages.end(); ++package) + { + const ExportPlanAction& action = *(package->second); + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + package_xml_file_path = + raw_exported_dir_path / Strings::format("packages.%s", package->first) / "meta" / "package.xml"; + package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + %s + %s + %s + %s + +)###", + action.spec.name(), + binary_paragraph.description, + binary_paragraph.version, + create_release_date())); + } + } + + void export_unique_triplets(const fs::path& raw_exported_dir_path, + std::set unique_triplets, + Files::Filesystem& fs) + { + std::error_code ec; + + // triplets + + fs::path package_xml_file_path = raw_exported_dir_path / "triplets" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + Triplets + 1.0.0 + %s + +)###", + create_release_date())); + + for (const std::string& triplet : unique_triplets) + { + package_xml_file_path = + raw_exported_dir_path / Strings::format("triplets.%s", triplet) / "meta" / "package.xml"; + package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + %s + 1.0.0 + %s + +)###", + triplet, + create_release_date())); + } + } + + void export_integration(const fs::path& raw_exported_dir_path, Files::Filesystem& fs) + { + std::error_code ec; + + // integration + fs::path package_xml_file_path = raw_exported_dir_path / "integration" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + Integration + 1.0.0 + %s + +)###", + create_release_date())); + } + + void export_config(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + const fs::path config_xml_file_path = get_config_file_path(export_id, ifw_options, paths); + + fs::path config_xml_dir_path = config_xml_file_path.parent_path(); + fs.create_directories(config_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for configuration file %s", + config_xml_file_path.generic_string()); + + std::string formatted_repo_url; + std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); + if (!ifw_repo_url.empty()) + { + formatted_repo_url = Strings::format(R"###( + + + %s + + )###", + ifw_repo_url); + } + + fs.write_contents(config_xml_file_path, + Strings::format( + R"###( + + vcpkg + 1.0.0 + vcpkg + @RootDir@/src/vcpkg%s + +)###", + formatted_repo_url)); + } + + void export_maintenance_tool(const fs::path& ifw_packages_dir_path, const VcpkgPaths& paths) + { + System::println("Exporting maintenance tool... "); + + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + const fs::path& installerbase_exe = paths.get_ifw_installerbase_exe(); + fs::path tempmaintenancetool = ifw_packages_dir_path / "maintenance" / "data" / "tempmaintenancetool.exe"; + fs.create_directories(tempmaintenancetool.parent_path(), ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + tempmaintenancetool.generic_string()); + fs.copy_file(installerbase_exe, tempmaintenancetool, fs::copy_options::overwrite_existing, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not write package file %s", tempmaintenancetool.generic_string()); + + fs::path package_xml_file_path = ifw_packages_dir_path / "maintenance" / "meta" / "package.xml"; + fs::path package_xml_dir_path = package_xml_file_path.parent_path(); + fs.create_directories(package_xml_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not create directory for package file %s", + package_xml_file_path.generic_string()); + fs.write_contents(package_xml_file_path, + Strings::format( + R"###( + + Maintenance Tool + Maintenance Tool + 1.0.0 + %s + + true + true + true + +)###", + create_release_date())); + const fs::path script_source = paths.root / "scripts" / "ifw" / "maintenance.qs"; + const fs::path script_destination = ifw_packages_dir_path / "maintenance" / "meta" / "maintenance.qs"; + fs.copy_file(script_source, script_destination, fs::copy_options::overwrite_existing, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not write package file %s", script_destination.generic_string()); + + System::println("Exporting maintenance tool... done"); + } + + void do_repository(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + const fs::path& repogen_exe = paths.get_ifw_repogen_exe(); + const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); + const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths); + + System::println("Generating repository %s...", repository_dir.generic_string()); + + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + fs.remove_all(repository_dir, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not remove outdated repository directory %s", repository_dir.generic_string()); + + const std::wstring cmd_line = Strings::wformat(LR"("%s" --packages "%s" "%s" > nul)", + repogen_exe.native(), + packages_dir.native(), + repository_dir.native()); + + const int exit_code = System::cmd_execute_clean(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW repository generating failed"); + + System::println(System::Color::success, "Generating repository %s... done.", repository_dir.generic_string()); + } + + void do_installer(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths) + { + const fs::path& binarycreator_exe = paths.get_ifw_binarycreator_exe(); + const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); + const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths); + const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths); + const fs::path installer_file = get_installer_file_path(export_id, ifw_options, paths); + + System::println("Generating installer %s...", installer_file.generic_string()); + + std::wstring cmd_line; + + std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); + if (!ifw_repo_url.empty()) + { + cmd_line = Strings::wformat(LR"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)", + binarycreator_exe.native(), + config_file.native(), + repository_dir.native(), + installer_file.native()); + } + else + { + cmd_line = Strings::wformat(LR"("%s" --config "%s" --packages "%s" "%s" > nul)", + binarycreator_exe.native(), + config_file.native(), + packages_dir.native(), + installer_file.native()); + } + + const int exit_code = System::cmd_execute_clean(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW installer generating failed"); + + System::println(System::Color::success, "Generating installer %s... done.", installer_file.generic_string()); + } + + void do_export(const std::vector& export_plan, + const std::string& export_id, + const Options& ifw_options, + const VcpkgPaths& paths) + { + std::error_code ec; + Files::Filesystem& fs = paths.get_filesystem(); + + // Prepare packages directory + const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); + + fs.remove_all(ifw_packages_dir_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Could not remove outdated packages directory %s", + ifw_packages_dir_path.generic_string()); + + fs.create_directory(ifw_packages_dir_path, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create packages directory %s", ifw_packages_dir_path.generic_string()); + + // Export maintenance tool + export_maintenance_tool(ifw_packages_dir_path, paths); + + System::println("Exporting packages %s... ", ifw_packages_dir_path.generic_string()); + + // execute the plan + std::map unique_packages; + std::set unique_triplets; + for (const ExportPlanAction& action : export_plan) + { + if (action.plan_type != ExportPlanType::ALREADY_BUILT) + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + const std::string display_name = action.spec.to_string(); + System::println("Exporting package %s... ", display_name); + + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + unique_packages[action.spec.name()] = &action; + unique_triplets.insert(action.spec.triplet().canonical_name()); + + // Export real package and return data dir for installation + fs::path ifw_package_dir_path = export_real_package(ifw_packages_dir_path, action, fs); + + // Copy package data + const InstallDir dirs = InstallDir::from_destination_root(ifw_package_dir_path, + action.spec.triplet().to_string(), + ifw_package_dir_path / "vcpkg" / "info" / + (binary_paragraph.fullstem() + ".list")); + + Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); + System::println("Exporting package %s... done", display_name); + } + + System::println("Exporting packages %s... done", ifw_packages_dir_path.generic_string()); + + const fs::path config_file = get_config_file_path(export_id, ifw_options, paths); + + System::println("Generating configuration %s...", config_file.generic_string()); + + // Unique packages + export_unique_packages(ifw_packages_dir_path, unique_packages, fs); + + // Unique triplets + export_unique_triplets(ifw_packages_dir_path, unique_triplets, fs); + + // Copy files needed for integration + export_integration_files(ifw_packages_dir_path / "integration" / "data", paths); + // Integration + export_integration(ifw_packages_dir_path, fs); + + // Configuration + export_config(export_id, ifw_options, paths); + + System::println("Generating configuration %s... done.", config_file.generic_string()); + + // Do repository (optional) + std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); + if (!ifw_repo_url.empty()) + { + do_repository(export_id, ifw_options, paths); + } + + // Do installer + do_installer(export_id, ifw_options, paths); + } +} diff --git a/toolsrc/src/vcpkg/commands.hash.cpp b/toolsrc/src/vcpkg/commands.hash.cpp new file mode 100644 index 000000000..394a02e23 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.hash.cpp @@ -0,0 +1,55 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Commands::Hash +{ + static void do_file_hash(fs::path const& path, std::wstring const& hash_type) + { + const auto cmd_line = Strings::wformat(LR"(CertUtil.exe -hashfile "%s" %s)", path.c_str(), hash_type); + const auto ec_data = System::cmd_execute_and_capture_output(cmd_line); + Checks::check_exit( + VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::to_utf8(cmd_line)); + + std::string const& output = ec_data.output; + + const auto start = output.find_first_of("\r\n"); + Checks::check_exit(VCPKG_LINE_INFO, + start != std::string::npos, + "Unexpected output format from command: %s", + Strings::to_utf8(cmd_line)); + + const auto end = output.find_first_of("\r\n", start + 1); + Checks::check_exit(VCPKG_LINE_INFO, + end != std::string::npos, + "Unexpected output format from command: %s", + Strings::to_utf8(cmd_line)); + + auto hash = output.substr(start, end - start); + Util::erase_remove_if(hash, isspace); + System::println(hash); + } + + void perform_and_exit(const VcpkgCmdArguments& args) + { + static const std::string EXAMPLE = Strings::format("The argument should be a file path\n%s", + Help::create_example_string("hash boost_1_62_0.tar.bz2")); + args.check_min_arg_count(1, EXAMPLE); + args.check_max_arg_count(2, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + if (args.command_arguments.size() == 1) + { + do_file_hash(args.command_arguments[0], L"SHA512"); + } + if (args.command_arguments.size() == 2) + { + do_file_hash(args.command_arguments[0], Strings::to_utf16(args.command_arguments[1])); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.import.cpp b/toolsrc/src/vcpkg/commands.import.cpp new file mode 100644 index 000000000..119aee022 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.import.cpp @@ -0,0 +1,120 @@ +#include "pch.h" + +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Import +{ + struct Binaries + { + std::vector dlls; + std::vector libs; + }; + + static void check_is_directory(const LineInfo& line_info, const Files::Filesystem& fs, const fs::path& dirpath) + { + Checks::check_exit(line_info, fs.is_directory(dirpath), "The path %s is not a directory", dirpath.string()); + } + + static Binaries find_binaries_in_dir(const Files::Filesystem& fs, const fs::path& path) + { + auto files = fs.get_files_recursive(path); + + check_is_directory(VCPKG_LINE_INFO, fs, path); + + Binaries binaries; + for (auto&& file : files) + { + if (fs.is_directory(file)) continue; + const auto ext = file.extension(); + if (ext == ".dll") + binaries.dlls.push_back(std::move(file)); + else if (ext == ".lib") + binaries.libs.push_back(std::move(file)); + } + return binaries; + } + + static void copy_files_into_directory(Files::Filesystem& fs, + const std::vector& files, + const fs::path& destination_folder) + { + std::error_code ec; + fs.create_directory(destination_folder, ec); + + for (auto const& src_path : files) + { + const fs::path dest_path = destination_folder / src_path.filename(); + fs.copy(src_path, dest_path, fs::copy_options::overwrite_existing); + } + } + + static void place_library_files_in(Files::Filesystem& fs, + const fs::path& include_directory, + const fs::path& project_directory, + const fs::path& destination_path) + { + check_is_directory(VCPKG_LINE_INFO, fs, include_directory); + check_is_directory(VCPKG_LINE_INFO, fs, project_directory); + check_is_directory(VCPKG_LINE_INFO, fs, destination_path); + const Binaries debug_binaries = find_binaries_in_dir(fs, project_directory / "Debug"); + const Binaries release_binaries = find_binaries_in_dir(fs, project_directory / "Release"); + + const fs::path destination_include_directory = destination_path / "include"; + fs.copy(include_directory, + destination_include_directory, + fs::copy_options::recursive | fs::copy_options::overwrite_existing); + + copy_files_into_directory(fs, release_binaries.dlls, destination_path / "bin"); + copy_files_into_directory(fs, release_binaries.libs, destination_path / "lib"); + + std::error_code ec; + fs.create_directory(destination_path / "debug", ec); + copy_files_into_directory(fs, debug_binaries.dlls, destination_path / "debug" / "bin"); + copy_files_into_directory(fs, debug_binaries.libs, destination_path / "debug" / "lib"); + } + + static void do_import(const VcpkgPaths& paths, + const fs::path& include_directory, + const fs::path& project_directory, + const BinaryParagraph& control_file_data) + { + auto& fs = paths.get_filesystem(); + const fs::path library_destination_path = paths.package_dir(control_file_data.spec); + std::error_code ec; + fs.create_directory(library_destination_path, ec); + place_library_files_in(paths.get_filesystem(), include_directory, project_directory, library_destination_path); + + const fs::path control_file_path = library_destination_path / "CONTROL"; + fs.write_contents(control_file_path, Strings::serialize(control_file_data)); + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Help::create_example_string( + R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"); + args.check_exact_arg_count(3, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + const fs::path control_file_path(args.command_arguments[0]); + const fs::path include_directory(args.command_arguments[1]); + const fs::path project_directory(args.command_arguments[2]); + + const Expected> pghs = + Paragraphs::get_single_paragraph(paths.get_filesystem(), control_file_path); + Checks::check_exit(VCPKG_LINE_INFO, + pghs.get() != nullptr, + "Invalid control file %s for package", + control_file_path.generic_string()); + + StatusParagraph spgh; + spgh.package = BinaryParagraph(*pghs.get()); + auto& control_file_data = spgh.package; + + do_import(paths, include_directory, project_directory, control_file_data); + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp new file mode 100644 index 000000000..7f6639222 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -0,0 +1,342 @@ +#include "pch.h" + +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Integrate +{ + static const std::array OLD_SYSTEM_TARGET_FILES = { + System::get_program_files_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets", + System::get_program_files_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets"}; + static const fs::path SYSTEM_WIDE_TARGETS_FILE = + System::get_program_files_32_bit() / "MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props"; + + static std::string create_appdata_targets_shortcut(const std::string& target_path) noexcept + { + return Strings::format(R"###( + + + +)###", + target_path, + target_path); + } + + static std::string create_system_targets_shortcut() noexcept + { + return R"###( + + + + $(LOCALAPPDATA)\vcpkg\vcpkg.user + + + +)###"; + } + + static std::string create_nuget_targets_file_contents(const fs::path& msbuild_vcpkg_targets_file) noexcept + { + const std::string as_string = msbuild_vcpkg_targets_file.string(); + + return Strings::format(R"###( + + + + + + +)###", + as_string, + as_string); + } + + static std::string create_nuget_props_file_contents() noexcept + { + return R"###( + + + true + + +)###"; + } + + static std::string get_nuget_id(const fs::path& vcpkg_root_dir) + { + std::string dir_id = vcpkg_root_dir.generic_string(); + std::replace(dir_id.begin(), dir_id.end(), '/', '.'); + dir_id.erase(1, 1); // Erasing the ":" + + // NuGet id cannot have invalid characters. We will only use alphanumeric and dot. + Util::erase_remove_if(dir_id, [](char c) { return !isalnum(c) && (c != '.'); }); + + const std::string nuget_id = "vcpkg." + dir_id; + return nuget_id; + } + + static std::string create_nuspec_file_contents(const fs::path& vcpkg_root_dir, + const std::string& nuget_id, + const std::string& nupkg_version) + { + static constexpr auto CONTENT_TEMPLATE = R"( + + + @NUGET_ID@ + @VERSION@ + vcpkg + + This package imports all libraries currently installed in @VCPKG_DIR@. This package does not contain any libraries and instead refers to the folder directly (like a symlink). + + + + + + + +)"; + + std::string content = std::regex_replace(CONTENT_TEMPLATE, std::regex("@NUGET_ID@"), nuget_id); + content = std::regex_replace(content, std::regex("@VCPKG_DIR@"), vcpkg_root_dir.string()); + content = std::regex_replace(content, std::regex("@VERSION@"), nupkg_version); + return content; + } + + enum class ElevationPromptChoice + { + YES, + NO + }; + + static ElevationPromptChoice elevated_cmd_execute(const std::string& param) + { + SHELLEXECUTEINFOW sh_ex_info = {0}; + sh_ex_info.cbSize = sizeof(sh_ex_info); + sh_ex_info.fMask = SEE_MASK_NOCLOSEPROCESS; + sh_ex_info.hwnd = nullptr; + sh_ex_info.lpVerb = L"runas"; + sh_ex_info.lpFile = L"cmd"; // Application to start + + auto wparam = Strings::to_utf16(param); + sh_ex_info.lpParameters = wparam.c_str(); // Additional parameters + sh_ex_info.lpDirectory = nullptr; + sh_ex_info.nShow = SW_HIDE; + sh_ex_info.hInstApp = nullptr; + + if (!ShellExecuteExW(&sh_ex_info)) + { + return ElevationPromptChoice::NO; + } + if (sh_ex_info.hProcess == nullptr) + { + return ElevationPromptChoice::NO; + } + WaitForSingleObject(sh_ex_info.hProcess, INFINITE); + CloseHandle(sh_ex_info.hProcess); + return ElevationPromptChoice::YES; + } + + static fs::path get_appdata_targets_path() + { + static const fs::path LOCAL_APP_DATA = + fs::path(System::get_environment_variable(L"LOCALAPPDATA").value_or_exit(VCPKG_LINE_INFO)); + return LOCAL_APP_DATA / "vcpkg" / "vcpkg.user.targets"; + } + + static void integrate_install(const VcpkgPaths& paths) + { + auto& fs = paths.get_filesystem(); + + // TODO: This block of code should eventually be removed + for (auto&& old_system_wide_targets_file : OLD_SYSTEM_TARGET_FILES) + { + if (fs.exists(old_system_wide_targets_file)) + { + const std::string param = + Strings::format(R"(/c DEL "%s" /Q > nul)", old_system_wide_targets_file.string()); + const ElevationPromptChoice user_choice = elevated_cmd_execute(param); + switch (user_choice) + { + case ElevationPromptChoice::YES: break; + case ElevationPromptChoice::NO: + System::println(System::Color::warning, "Warning: Previous integration file was not removed"); + Checks::exit_fail(VCPKG_LINE_INFO); + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + } + + std::error_code ec; + const fs::path tmp_dir = paths.buildsystems / "tmp"; + fs.create_directory(paths.buildsystems, ec); + fs.create_directory(tmp_dir, ec); + + bool should_install_system = true; + const Expected system_wide_file_contents = fs.read_contents(SYSTEM_WIDE_TARGETS_FILE); + static const std::regex RE(R"###()###"); + if (const auto contents_data = system_wide_file_contents.get()) + { + std::match_results match; + const auto found = std::regex_search(*contents_data, match, RE); + if (found) + { + const int ver = atoi(match[1].str().c_str()); + if (ver >= 1) should_install_system = false; + } + } + + if (should_install_system) + { + const fs::path sys_src_path = tmp_dir / "vcpkg.system.targets"; + fs.write_contents(sys_src_path, create_system_targets_shortcut()); + + const std::string param = Strings::format(R"(/c mkdir "%s" & copy "%s" "%s" /Y > nul)", + SYSTEM_WIDE_TARGETS_FILE.parent_path().string(), + sys_src_path.string(), + SYSTEM_WIDE_TARGETS_FILE.string()); + const ElevationPromptChoice user_choice = elevated_cmd_execute(param); + switch (user_choice) + { + case ElevationPromptChoice::YES: break; + case ElevationPromptChoice::NO: + System::println(System::Color::warning, "Warning: integration was not applied"); + Checks::exit_fail(VCPKG_LINE_INFO); + default: Checks::unreachable(VCPKG_LINE_INFO); + } + + Checks::check_exit(VCPKG_LINE_INFO, + fs.exists(SYSTEM_WIDE_TARGETS_FILE), + "Error: failed to copy targets file to %s", + SYSTEM_WIDE_TARGETS_FILE.string()); + } + + const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets"; + fs.write_contents(appdata_src_path, + create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string())); + auto appdata_dst_path = get_appdata_targets_path(); + + const auto rc = fs.copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing, ec); + + if (!rc || ec) + { + System::println(System::Color::error, + "Error: Failed to copy file: %s -> %s", + appdata_src_path.string(), + appdata_dst_path.string()); + Checks::exit_fail(VCPKG_LINE_INFO); + } + System::println(System::Color::success, "Applied user-wide integration for this vcpkg root."); + const fs::path cmake_toolchain = paths.buildsystems / "vcpkg.cmake"; + System::println("\n" + "All MSBuild C++ projects can now #include any installed libraries.\n" + "Linking will be handled automatically.\n" + "Installing new libraries will make them instantly available.\n" + "\n" + "CMake projects should use -DCMAKE_TOOLCHAIN_FILE=%s", + cmake_toolchain.generic_string()); + + Checks::exit_success(VCPKG_LINE_INFO); + } + + static void integrate_remove(Files::Filesystem& fs) + { + const fs::path path = get_appdata_targets_path(); + + std::error_code ec; + const bool was_deleted = fs.remove(path, ec); + + Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %d", ec.message()); + + if (was_deleted) + { + System::println(System::Color::success, "User-wide integration was removed"); + } + else + { + System::println(System::Color::success, "User-wide integration is not installed"); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } + + static void integrate_project(const VcpkgPaths& paths) + { + auto& fs = paths.get_filesystem(); + + const fs::path& nuget_exe = paths.get_nuget_exe(); + + const fs::path& buildsystems_dir = paths.buildsystems; + const fs::path tmp_dir = buildsystems_dir / "tmp"; + std::error_code ec; + fs.create_directory(buildsystems_dir, ec); + fs.create_directory(tmp_dir, ec); + + const fs::path targets_file_path = tmp_dir / "vcpkg.nuget.targets"; + const fs::path props_file_path = tmp_dir / "vcpkg.nuget.props"; + const fs::path nuspec_file_path = tmp_dir / "vcpkg.nuget.nuspec"; + const std::string nuget_id = get_nuget_id(paths.root); + const std::string nupkg_version = "1.0.0"; + + fs.write_contents(targets_file_path, create_nuget_targets_file_contents(paths.buildsystems_msbuild_targets)); + fs.write_contents(props_file_path, create_nuget_props_file_contents()); + fs.write_contents(nuspec_file_path, create_nuspec_file_contents(paths.root, nuget_id, nupkg_version)); + + // Using all forward slashes for the command line + const std::wstring cmd_line = Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" > nul)", + nuget_exe.native(), + buildsystems_dir.native(), + nuspec_file_path.native()); + + const int exit_code = System::cmd_execute_clean(cmd_line); + + const fs::path nuget_package = buildsystems_dir / Strings::format("%s.%s.nupkg", nuget_id, nupkg_version); + Checks::check_exit( + VCPKG_LINE_INFO, exit_code == 0 && fs.exists(nuget_package), "Error: NuGet package creation failed"); + System::println(System::Color::success, "Created nupkg: %s", nuget_package.string()); + + auto source_path = buildsystems_dir.u8string(); + source_path = std::regex_replace(source_path, std::regex("`"), "``"); + + System::println(R"( +With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: + Install-Package %s -Source "%s" +)", + nuget_id, + source_path); + + Checks::exit_success(VCPKG_LINE_INFO); + } + + const char* const INTEGRATE_COMMAND_HELPSTRING = + " vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on " + "first use\n" + " vcpkg integrate remove Remove user-wide integration\n" + " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n"; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format("Commands:\n" + "%s", + INTEGRATE_COMMAND_HELPSTRING); + args.check_exact_arg_count(1, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + if (args.command_arguments[0] == "install") + { + return integrate_install(paths); + } + if (args.command_arguments[0] == "remove") + { + return integrate_remove(paths.get_filesystem()); + } + if (args.command_arguments[0] == "project") + { + return integrate_project(paths); + } + + Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown parameter %s for integrate", args.command_arguments[0]); + } +} diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp new file mode 100644 index 000000000..d0e8e00b4 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.list.cpp @@ -0,0 +1,75 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Commands::List +{ + static const std::string OPTION_FULLDESC = "--x-full-desc"; // TODO: This should find a better home, eventually + + static void do_print(const StatusParagraph& pgh, bool full_desc) + { + if (full_desc) + { + System::println("%-30s %-16s %s", pgh.package.displayname(), pgh.package.version, pgh.package.description); + } + else + { + System::println("%-30s %-16s %s", + vcpkg::shorten_text(pgh.package.displayname(), 30), + vcpkg::shorten_text(pgh.package.version, 16), + vcpkg::shorten_text(pgh.package.description, 71)); + } + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format( + "The argument should be a substring to search for, or no argument to display all installed libraries.\n%s", + Help::create_example_string("list png")); + args.check_max_arg_count(1, EXAMPLE); + const std::unordered_set options = + args.check_and_get_optional_command_arguments({OPTION_FULLDESC}); + + const StatusParagraphs status_paragraphs = database_load_check(paths); + std::vector installed_packages = get_installed_ports(status_paragraphs); + + if (installed_packages.empty()) + { + System::println("No packages are installed. Did you mean `search`?"); + Checks::exit_success(VCPKG_LINE_INFO); + } + + std::sort(installed_packages.begin(), + installed_packages.end(), + [](const StatusParagraph* lhs, const StatusParagraph* rhs) -> bool { + return lhs->package.displayname() < rhs->package.displayname(); + }); + + if (args.command_arguments.size() == 0) + { + for (const StatusParagraph* status_paragraph : installed_packages) + { + do_print(*status_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + } + } + else + { + // At this point there is 1 argument + for (const StatusParagraph* status_paragraph : installed_packages) + { + const std::string displayname = status_paragraph->package.displayname(); + if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end()) + { + continue; + } + + do_print(*status_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + } + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.owns.cpp b/toolsrc/src/vcpkg/commands.owns.cpp new file mode 100644 index 000000000..69079e829 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.owns.cpp @@ -0,0 +1,38 @@ +#include "pch.h" + +#include +#include +#include +#include + +namespace vcpkg::Commands::Owns +{ + static void search_file(const VcpkgPaths& paths, const std::string& file_substr, const StatusParagraphs& status_db) + { + const std::vector installed_files = get_installed_files(paths, status_db); + for (const StatusParagraphAndAssociatedFiles& pgh_and_file : installed_files) + { + const StatusParagraph& pgh = pgh_and_file.pgh; + + for (const std::string& file : pgh_and_file.files) + { + if (file.find(file_substr) != std::string::npos) + { + System::println("%s: %s", pgh.package.displayname(), file); + } + } + } + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format("The argument should be a pattern to search for. %s", + Help::create_example_string("owns zlib.dll")); + args.check_exact_arg_count(1, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + StatusParagraphs status_db = database_load_check(paths); + search_file(paths, args.command_arguments[0], status_db); + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp new file mode 100644 index 000000000..b70ad2b80 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp @@ -0,0 +1,182 @@ +#include "pch.h" + +#include +#include +#include + +#include +#include +#include + +namespace vcpkg::Commands::PortsDiff +{ + struct UpdatedPort + { + static bool compare_by_name(const UpdatedPort& left, const UpdatedPort& right) + { + return left.port < right.port; + } + + std::string port; + VersionDiff version_diff; + }; + + template + struct SetElementPresence + { + static SetElementPresence create(std::vector left, std::vector right) + { + // TODO: This can be done with one pass instead of three passes + SetElementPresence output; + std::set_difference( + left.cbegin(), left.cend(), right.cbegin(), right.cend(), std::back_inserter(output.only_left)); + std::set_intersection( + left.cbegin(), left.cend(), right.cbegin(), right.cend(), std::back_inserter(output.both)); + std::set_difference( + right.cbegin(), right.cend(), left.cbegin(), left.cend(), std::back_inserter(output.only_right)); + + return output; + } + + std::vector only_left; + std::vector both; + std::vector only_right; + }; + + static std::vector find_updated_ports( + const std::vector& ports, + const std::map& previous_names_and_versions, + const std::map& current_names_and_versions) + { + std::vector output; + for (const std::string& name : ports) + { + const VersionT& previous_version = previous_names_and_versions.at(name); + const VersionT& current_version = current_names_and_versions.at(name); + if (previous_version == current_version) + { + continue; + } + + output.push_back({name, VersionDiff(previous_version, current_version)}); + } + + return output; + } + + static void do_print_name_and_version(const std::vector& ports_to_print, + const std::map& names_and_versions) + { + for (const std::string& name : ports_to_print) + { + const VersionT& version = names_and_versions.at(name); + System::println(" - %-14s %-16s", name, version); + } + } + + static std::map read_ports_from_commit(const VcpkgPaths& paths, + const std::wstring& git_commit_id) + { + std::error_code ec; + auto& fs = paths.get_filesystem(); + const fs::path& git_exe = paths.get_git_exe(); + const fs::path dot_git_dir = paths.root / ".git"; + const std::wstring ports_dir_name_as_string = paths.ports.filename().native(); + const fs::path temp_checkout_path = + paths.root / Strings::wformat(L"%s-%s", ports_dir_name_as_string, git_commit_id); + fs.create_directory(temp_checkout_path, ec); + const std::wstring checkout_this_dir = + Strings::wformat(LR"(.\%s)", ports_dir_name_as_string); // Must be relative to the root of the repository + + const std::wstring cmd = + Strings::wformat(LR"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & "%s" reset >NUL)", + git_exe.native(), + dot_git_dir.native(), + temp_checkout_path.native(), + git_commit_id, + checkout_this_dir, + L".vcpkg-root", + git_exe.native()); + System::cmd_execute_clean(cmd); + const std::map names_and_versions = Paragraphs::load_all_port_names_and_versions( + paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string); + fs.remove_all(temp_checkout_path, ec); + return names_and_versions; + } + + static void check_commit_exists(const fs::path& git_exe, const std::wstring& git_commit_id) + { + static const std::string VALID_COMMIT_OUTPUT = "commit\n"; + + const std::wstring cmd = Strings::wformat(LR"("%s" cat-file -t %s)", git_exe.native(), git_commit_id); + const System::ExitCodeAndOutput output = System::cmd_execute_and_capture_output(cmd); + Checks::check_exit(VCPKG_LINE_INFO, + output.output == VALID_COMMIT_OUTPUT, + "Invalid commit id %s", + Strings::to_utf8(git_commit_id)); + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format("The argument should be a branch/tag/hash to checkout.\n%s", + Help::create_example_string("portsdiff mybranchname")); + args.check_min_arg_count(1, EXAMPLE); + args.check_max_arg_count(2, EXAMPLE); + args.check_and_get_optional_command_arguments({}); + + const fs::path& git_exe = paths.get_git_exe(); + + const std::wstring git_commit_id_for_previous_snapshot = Strings::to_utf16(args.command_arguments.at(0)); + const std::wstring git_commit_id_for_current_snapshot = + args.command_arguments.size() < 2 ? L"HEAD" : Strings::to_utf16(args.command_arguments.at(1)); + + check_commit_exists(git_exe, git_commit_id_for_current_snapshot); + check_commit_exists(git_exe, git_commit_id_for_previous_snapshot); + + const std::map current_names_and_versions = + read_ports_from_commit(paths, git_commit_id_for_current_snapshot); + const std::map previous_names_and_versions = + read_ports_from_commit(paths, git_commit_id_for_previous_snapshot); + + // Already sorted, so set_difference can work on std::vector too + const std::vector current_ports = Util::extract_keys(current_names_and_versions); + const std::vector previous_ports = Util::extract_keys(previous_names_and_versions); + + const SetElementPresence setp = + SetElementPresence::create(current_ports, previous_ports); + + const std::vector& added_ports = setp.only_left; + if (!added_ports.empty()) + { + System::println("\nThe following %d ports were added:", added_ports.size()); + do_print_name_and_version(added_ports, current_names_and_versions); + } + + const std::vector& removed_ports = setp.only_right; + if (!removed_ports.empty()) + { + System::println("\nThe following %d ports were removed:", removed_ports.size()); + do_print_name_and_version(removed_ports, previous_names_and_versions); + } + + const std::vector& common_ports = setp.both; + const std::vector updated_ports = + find_updated_ports(common_ports, previous_names_and_versions, current_names_and_versions); + + if (!updated_ports.empty()) + { + System::println("\nThe following %d ports were updated:", updated_ports.size()); + for (const UpdatedPort& p : updated_ports) + { + System::println(" - %-14s %-16s", p.port, p.version_diff.to_string()); + } + } + + if (added_ports.empty() && removed_ports.empty() && updated_ports.empty()) + { + System::println("There were no changes in the ports between the two commits."); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp new file mode 100644 index 000000000..3ba8707de --- /dev/null +++ b/toolsrc/src/vcpkg/commands.search.cpp @@ -0,0 +1,146 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Search +{ + static const std::string OPTION_GRAPH = "--graph"; // TODO: This should find a better home, eventually + static const std::string OPTION_FULLDESC = "--x-full-desc"; // TODO: This should find a better home, eventually + + static std::string replace_dashes_with_underscore(const std::string& input) + { + std::string output = input; + std::replace(output.begin(), output.end(), '-', '_'); + return output; + } + + static std::string create_graph_as_string( + const std::vector>& source_control_files) + { + int empty_node_count = 0; + + std::string s; + s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;"); + + for (const auto& source_control_file : source_control_files) + { + const SourceParagraph& source_paragraph = *source_control_file->core_paragraph; + if (source_paragraph.depends.empty()) + { + empty_node_count++; + continue; + } + + const std::string name = replace_dashes_with_underscore(source_paragraph.name); + s.append(Strings::format("%s;", name)); + for (const Dependency& d : source_paragraph.depends) + { + const std::string dependency_name = replace_dashes_with_underscore(d.name()); + s.append(Strings::format("%s -> %s;", name, dependency_name)); + } + } + + s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count)); + return s; + } + static void do_print(const SourceParagraph& source_paragraph, bool full_desc) + { + if (full_desc) + { + System::println( + "%-20s %-16s %s", source_paragraph.name, source_paragraph.version, source_paragraph.description); + } + else + { + System::println("%-20s %-16s %s", + vcpkg::shorten_text(source_paragraph.name, 20), + vcpkg::shorten_text(source_paragraph.version, 16), + vcpkg::shorten_text(source_paragraph.description, 81)); + } + } + + static void do_print(const std::string& name, const FeatureParagraph& feature_paragraph, bool full_desc) + { + if (full_desc) + { + System::println("%-37s %s", name + "[" + feature_paragraph.name + "]", feature_paragraph.description); + } + else + { + System::println("%-37s %s", + vcpkg::shorten_text(name + "[" + feature_paragraph.name + "]", 37), + vcpkg::shorten_text(feature_paragraph.description, 81)); + } + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + static const std::string EXAMPLE = Strings::format( + "The argument should be a substring to search for, or no argument to display all libraries.\n%s", + Help::create_example_string("search png")); + args.check_max_arg_count(1, EXAMPLE); + const std::unordered_set options = + args.check_and_get_optional_command_arguments({OPTION_GRAPH, OPTION_FULLDESC}); + + auto source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); + + if (options.find(OPTION_GRAPH) != options.cend()) + { + const std::string graph_as_string = create_graph_as_string(source_paragraphs); + System::println(graph_as_string); + Checks::exit_success(VCPKG_LINE_INFO); + } + + if (args.command_arguments.empty()) + { + for (const auto& source_control_file : source_paragraphs) + { + do_print(*source_control_file->core_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + for (auto&& feature_paragraph : source_control_file->feature_paragraphs) + { + do_print(source_control_file->core_paragraph->name, + *feature_paragraph, + options.find(OPTION_FULLDESC) != options.cend()); + } + } + } + else + { + const auto& icontains = Strings::case_insensitive_ascii_contains; + + // At this point there is 1 argument + auto&& args_zero = args.command_arguments[0]; + for (const auto& source_control_file : source_paragraphs) + { + auto&& sp = *source_control_file->core_paragraph; + + bool contains_name = icontains(sp.name, args_zero); + if (contains_name || icontains(sp.description, args_zero)) + { + do_print(sp, options.find(OPTION_FULLDESC) != options.cend()); + } + + for (auto&& feature_paragraph : source_control_file->feature_paragraphs) + { + if (contains_name || icontains(feature_paragraph->name, args_zero) || + icontains(feature_paragraph->description, args_zero)) + { + do_print(sp.name, *feature_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + } + } + } + } + + System::println( + "\nIf your library is not listed, please open an issue at and/or consider making a pull request:\n" + " https://github.com/Microsoft/vcpkg/issues"); + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.version.cpp b/toolsrc/src/vcpkg/commands.version.cpp new file mode 100644 index 000000000..c6cc82d1f --- /dev/null +++ b/toolsrc/src/vcpkg/commands.version.cpp @@ -0,0 +1,67 @@ +#include "pch.h" + +#include +#include +#include + +#define STRINGIFY(...) #__VA_ARGS__ +#define MACRO_TO_STRING(X) STRINGIFY(X) + +#define VCPKG_VERSION_AS_STRING MACRO_TO_STRING(VCPKG_VERSION) + +namespace vcpkg::Commands::Version +{ + const std::string& version() + { + static const std::string S_VERSION = +#include "../VERSION.txt" + + +std::string(VCPKG_VERSION_AS_STRING) +#ifndef NDEBUG + + std::string("-debug") +#endif + + std::string(Metrics::get_compiled_metrics_enabled() ? Strings::EMPTY : "-external"); + return S_VERSION; + } + + void warn_if_vcpkg_version_mismatch(const VcpkgPaths& paths) + { + auto version_file = paths.get_filesystem().read_contents(paths.root / "toolsrc" / "VERSION.txt"); + if (const auto version_contents = version_file.get()) + { + int maj1, min1, rev1; + const auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1); + + int maj2, min2, rev2; + const auto num2 = sscanf_s(Version::version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2); + + if (num1 == 3 && num2 == 3) + { + if (maj1 != maj2 || min1 != min2 || rev1 != rev2) + { + System::println(System::Color::warning, + "Warning: Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use " + ".\\bootstrap-vcpkg.bat to update.", + maj2, + min2, + rev2, + maj1, + min1, + rev1); + } + } + } + } + + void perform_and_exit(const VcpkgCmdArguments& args) + { + args.check_exact_arg_count(0); + args.check_and_get_optional_command_arguments({}); + + System::println("Vcpkg package management program version %s\n" + "\n" + "See LICENSE.txt for license information.", + version()); + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp new file mode 100644 index 000000000..da8f1e178 --- /dev/null +++ b/toolsrc/src/vcpkg/dependencies.cpp @@ -0,0 +1,669 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Dependencies +{ + struct FeatureNodeEdges + { + std::vector remove_edges; + std::vector build_edges; + bool plus = false; + }; + + struct Cluster : Util::MoveOnlyBase + { + std::vector status_paragraphs; + Optional source_control_file; + PackageSpec spec; + std::unordered_map edges; + std::unordered_set to_install_features; + std::unordered_set original_features; + bool will_remove = false; + bool transient_uninstalled = true; + RequestType request_type = RequestType::AUTO_SELECTED; + }; + + struct ClusterPtr + { + Cluster* ptr; + + Cluster* operator->() const { return ptr; } + }; + + bool operator==(const ClusterPtr& l, const ClusterPtr& r) { return l.ptr == r.ptr; } +} + +template<> +struct std::hash +{ + size_t operator()(const vcpkg::Dependencies::ClusterPtr& value) const + { + return std::hash()(value.ptr->spec); + } +}; + +namespace vcpkg::Dependencies +{ + struct GraphPlan + { + Graphs::Graph remove_graph; + Graphs::Graph install_graph; + }; + + struct ClusterGraph : Util::MoveOnlyBase + { + explicit ClusterGraph(std::unordered_map&& ports) + : m_ports(std::move(ports)) + { + } + + Cluster& get(const PackageSpec& spec) + { + auto it = m_graph.find(spec); + if (it == m_graph.end()) + { + // Load on-demand from m_ports + auto it_ports = m_ports.find(spec.name()); + if (it_ports != m_ports.end()) + { + auto& clust = m_graph[spec]; + clust.spec = spec; + cluster_from_scf(*it_ports->second, clust); + return clust; + } + return m_graph[spec]; + } + return it->second; + } + + private: + void cluster_from_scf(const SourceControlFile& scf, Cluster& out_cluster) const + { + FeatureNodeEdges core_dependencies; + core_dependencies.build_edges = + filter_dependencies_to_specs(scf.core_paragraph->depends, out_cluster.spec.triplet()); + out_cluster.edges.emplace("core", std::move(core_dependencies)); + + for (const auto& feature : scf.feature_paragraphs) + { + FeatureNodeEdges added_edges; + added_edges.build_edges = filter_dependencies_to_specs(feature->depends, out_cluster.spec.triplet()); + out_cluster.edges.emplace(feature->name, std::move(added_edges)); + } + out_cluster.source_control_file = &scf; + } + + std::unordered_map m_graph; + std::unordered_map m_ports; + }; + + std::vector AnyParagraph::dependencies(const Triplet& triplet) const + { + auto to_package_specs = [&](const std::vector& dependencies_as_string) { + return Util::fmap(dependencies_as_string, [&](const std::string s) { + return PackageSpec::from_name_and_triplet(s, triplet).value_or_exit(VCPKG_LINE_INFO); + }); + }; + + if (auto p = this->status_paragraph.get()) + { + return to_package_specs(p->package.depends); + } + + if (auto p = this->binary_control_file.get()) + { + auto deps = Util::fmap_flatten(p->features, [](const BinaryParagraph& pgh) { return pgh.depends; }); + deps.insert(deps.end(), p->core_paragraph.depends.begin(), p->core_paragraph.depends.end()); + return to_package_specs(deps); + } + + if (auto p = this->source_paragraph.get()) + { + return to_package_specs(filter_dependencies(p->depends, triplet)); + } + + Checks::exit_with_message(VCPKG_LINE_INFO, + "Cannot get dependencies because there was none of: source/binary/status paragraphs"); + } + + std::string to_output_string(RequestType request_type, const CStringView s) + { + switch (request_type) + { + case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s); + case RequestType::USER_REQUESTED: return Strings::format(" %s", s); + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + InstallPlanAction::InstallPlanAction() : plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {} + + InstallPlanAction::InstallPlanAction(const PackageSpec& spec, + const SourceControlFile& any_paragraph, + const std::unordered_set& features, + const RequestType& request_type) + : spec(spec), plan_type(InstallPlanType::BUILD_AND_INSTALL), request_type(request_type), feature_list(features) + { + this->any_paragraph.source_control_file = &any_paragraph; + } + + InstallPlanAction::InstallPlanAction(const PackageSpec& spec, + const std::unordered_set& features, + const RequestType& request_type) + : spec(spec), plan_type(InstallPlanType::ALREADY_INSTALLED), request_type(request_type), feature_list(features) + { + } + + InstallPlanAction::InstallPlanAction(const PackageSpec& spec, + const AnyParagraph& any_paragraph, + const RequestType& request_type) + : spec(spec), any_paragraph(any_paragraph), plan_type(InstallPlanType::UNKNOWN), request_type(request_type) + { + if (auto p = any_paragraph.status_paragraph.get()) + { + this->plan_type = InstallPlanType::ALREADY_INSTALLED; + return; + } + + if (auto p = any_paragraph.binary_control_file.get()) + { + this->plan_type = InstallPlanType::INSTALL; + return; + } + + if (auto p = any_paragraph.source_paragraph.get()) + { + this->plan_type = InstallPlanType::BUILD_AND_INSTALL; + return; + } + } + + std::string InstallPlanAction::displayname() const + { + if (this->feature_list.empty()) + { + return this->spec.to_string(); + } + + const std::string features = Strings::join(",", this->feature_list); + return Strings::format("%s[%s]:%s", this->spec.name(), features, this->spec.triplet()); + } + + bool InstallPlanAction::compare_by_name(const InstallPlanAction* left, const InstallPlanAction* right) + { + return left->spec.name() < right->spec.name(); + } + + RemovePlanAction::RemovePlanAction() : plan_type(RemovePlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {} + + RemovePlanAction::RemovePlanAction(const PackageSpec& spec, + const RemovePlanType& plan_type, + const RequestType& request_type) + : spec(spec), plan_type(plan_type), request_type(request_type) + { + } + + const PackageSpec& AnyAction::spec() const + { + if (const auto p = install_plan.get()) + { + return p->spec; + } + + if (const auto p = remove_plan.get()) + { + return p->spec; + } + + Checks::exit_with_message(VCPKG_LINE_INFO, "Null action"); + } + + bool ExportPlanAction::compare_by_name(const ExportPlanAction* left, const ExportPlanAction* right) + { + return left->spec.name() < right->spec.name(); + } + + ExportPlanAction::ExportPlanAction() : plan_type(ExportPlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {} + + ExportPlanAction::ExportPlanAction(const PackageSpec& spec, + const AnyParagraph& any_paragraph, + const RequestType& request_type) + : spec(spec), any_paragraph(any_paragraph), plan_type(ExportPlanType::UNKNOWN), request_type(request_type) + { + if (auto p = any_paragraph.binary_control_file.get()) + { + this->plan_type = ExportPlanType::ALREADY_BUILT; + return; + } + + if (auto p = any_paragraph.source_paragraph.get()) + { + this->plan_type = ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT; + return; + } + } + + bool RemovePlanAction::compare_by_name(const RemovePlanAction* left, const RemovePlanAction* right) + { + return left->spec.name() < right->spec.name(); + } + + MapPortFile::MapPortFile(const std::unordered_map& map) : ports(map) {} + + const SourceControlFile& MapPortFile::get_control_file(const std::string& spec) const + { + auto scf = ports.find(spec); + if (scf == ports.end()) + { + Checks::exit_fail(VCPKG_LINE_INFO); + } + return scf->second; + } + + PathsPortFile::PathsPortFile(const VcpkgPaths& paths) : ports(paths) {} + + const SourceControlFile& PathsPortFile::get_control_file(const std::string& spec) const + { + auto cache_it = cache.find(spec); + if (cache_it != cache.end()) + { + return cache_it->second; + } + Parse::ParseExpected source_control_file = + Paragraphs::try_load_port(ports.get_filesystem(), ports.port_dir(spec)); + + if (auto scf = source_control_file.get()) + { + auto it = cache.emplace(spec, std::move(*scf->get())); + return it.first->second; + } + print_error_message(source_control_file.error()); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + std::vector create_install_plan(const PortFileProvider& port_file_provider, + const std::vector& specs, + const StatusParagraphs& status_db) + { + struct InstallAdjacencyProvider final : Graphs::AdjacencyProvider + { + const PortFileProvider& port_file_provider; + const StatusParagraphs& status_db; + const std::unordered_set& specs_as_set; + + InstallAdjacencyProvider(const PortFileProvider& port_file_provider, + const StatusParagraphs& s, + const std::unordered_set& specs_as_set) + : port_file_provider(port_file_provider), status_db(s), specs_as_set(specs_as_set) + { + } + + std::vector adjacency_list(const InstallPlanAction& plan) const override + { + if (plan.any_paragraph.status_paragraph.get()) return std::vector{}; + return plan.any_paragraph.dependencies(plan.spec.triplet()); + } + + InstallPlanAction load_vertex_data(const PackageSpec& spec) const override + { + const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() + ? RequestType::USER_REQUESTED + : RequestType::AUTO_SELECTED; + auto it = status_db.find_installed(spec); + if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type}; + return InstallPlanAction{ + spec, + {nullopt, nullopt, *port_file_provider.get_control_file(spec.name()).core_paragraph}, + request_type}; + } + }; + + const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); + std::vector toposort = + Graphs::topological_sort(specs, InstallAdjacencyProvider{port_file_provider, status_db, specs_as_set}); + Util::erase_remove_if(toposort, [](const InstallPlanAction& plan) { + return plan.request_type == RequestType::AUTO_SELECTED && + plan.plan_type == InstallPlanType::ALREADY_INSTALLED; + }); + + return toposort; + } + + std::vector create_remove_plan(const std::vector& specs, + const StatusParagraphs& status_db) + { + struct RemoveAdjacencyProvider final : Graphs::AdjacencyProvider + { + const StatusParagraphs& status_db; + const std::vector& installed_ports; + const std::unordered_set& specs_as_set; + + RemoveAdjacencyProvider(const StatusParagraphs& status_db, + const std::vector& installed_ports, + const std::unordered_set& specs_as_set) + : status_db(status_db), installed_ports(installed_ports), specs_as_set(specs_as_set) + { + } + + std::vector adjacency_list(const RemovePlanAction& plan) const override + { + if (plan.plan_type == RemovePlanType::NOT_INSTALLED) + { + return {}; + } + + const PackageSpec& spec = plan.spec; + std::vector dependents; + for (const StatusParagraph* an_installed_package : installed_ports) + { + if (an_installed_package->package.spec.triplet() != spec.triplet()) continue; + + const std::vector& deps = an_installed_package->package.depends; + if (std::find(deps.begin(), deps.end(), spec.name()) == deps.end()) continue; + + dependents.push_back(an_installed_package->package.spec); + } + + return dependents; + } + + RemovePlanAction load_vertex_data(const PackageSpec& spec) const override + { + const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() + ? RequestType::USER_REQUESTED + : RequestType::AUTO_SELECTED; + const StatusParagraphs::const_iterator it = status_db.find_installed(spec); + if (it == status_db.end()) + { + return RemovePlanAction{spec, RemovePlanType::NOT_INSTALLED, request_type}; + } + return RemovePlanAction{spec, RemovePlanType::REMOVE, request_type}; + } + }; + + const std::vector& installed_ports = get_installed_ports(status_db); + const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); + return Graphs::topological_sort(specs, RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set}); + } + + std::vector create_export_plan(const VcpkgPaths& paths, + const std::vector& specs, + const StatusParagraphs& status_db) + { + struct ExportAdjacencyProvider final : Graphs::AdjacencyProvider + { + const VcpkgPaths& paths; + const StatusParagraphs& status_db; + const std::unordered_set& specs_as_set; + + ExportAdjacencyProvider(const VcpkgPaths& p, + const StatusParagraphs& s, + const std::unordered_set& specs_as_set) + : paths(p), status_db(s), specs_as_set(specs_as_set) + { + } + + std::vector adjacency_list(const ExportPlanAction& plan) const override + { + return plan.any_paragraph.dependencies(plan.spec.triplet()); + } + + ExportPlanAction load_vertex_data(const PackageSpec& spec) const override + { + const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() + ? RequestType::USER_REQUESTED + : RequestType::AUTO_SELECTED; + + Expected maybe_bpgh = Paragraphs::try_load_cached_control_package(paths, spec); + if (auto bcf = maybe_bpgh.get()) + return ExportPlanAction{spec, AnyParagraph{nullopt, std::move(*bcf), nullopt}, request_type}; + + auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec)); + if (auto scf = maybe_scf.get()) + return ExportPlanAction{spec, {nullopt, nullopt, *scf->get()->core_paragraph}, request_type}; + else + print_error_message(maybe_scf.error()); + + Checks::exit_with_message(VCPKG_LINE_INFO, "Could not find package %s", spec); + } + }; + + const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); + std::vector toposort = + Graphs::topological_sort(specs, ExportAdjacencyProvider{paths, status_db, specs_as_set}); + return toposort; + } + + enum class MarkPlusResult + { + FEATURE_NOT_FOUND, + SUCCESS, + }; + + MarkPlusResult mark_plus(const std::string& feature, + Cluster& cluster, + ClusterGraph& pkg_to_cluster, + GraphPlan& graph_plan); + void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan); + + MarkPlusResult mark_plus(const std::string& feature, Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan) + { + if (feature.empty()) + { + // Indicates that core was not specified in the reference + return mark_plus("core", cluster, graph, graph_plan); + } + + auto it = cluster.edges.find(feature); + if (it == cluster.edges.end()) return MarkPlusResult::FEATURE_NOT_FOUND; + + if (cluster.edges[feature].plus) return MarkPlusResult::SUCCESS; + + if (cluster.original_features.find(feature) == cluster.original_features.end()) + { + cluster.transient_uninstalled = true; + } + + if (!cluster.transient_uninstalled) + { + return MarkPlusResult::SUCCESS; + } + cluster.edges[feature].plus = true; + + if (!cluster.original_features.empty()) + { + mark_minus(cluster, graph, graph_plan); + } + + graph_plan.install_graph.add_vertex({&cluster}); + auto& tracked = cluster.to_install_features; + tracked.insert(feature); + + if (feature != "core") + { + // All features implicitly depend on core + auto res = mark_plus("core", cluster, graph, graph_plan); + + // Should be impossible for "core" to not exist + Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS); + } + + for (auto&& depend : cluster.edges[feature].build_edges) + { + auto& depend_cluster = graph.get(depend.spec()); + auto res = mark_plus(depend.feature(), depend_cluster, graph, graph_plan); + + Checks::check_exit(VCPKG_LINE_INFO, + res == MarkPlusResult::SUCCESS, + "Error: Unable to satisfy dependency %s of %s", + depend, + FeatureSpec(cluster.spec, feature)); + + if (&depend_cluster == &cluster) continue; + graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster}); + } + + return MarkPlusResult::SUCCESS; + } + + void mark_minus(Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan) + { + if (cluster.will_remove) return; + cluster.will_remove = true; + + graph_plan.remove_graph.add_vertex({&cluster}); + for (auto&& pair : cluster.edges) + { + auto& remove_edges_edges = pair.second.remove_edges; + for (auto&& depend : remove_edges_edges) + { + auto& depend_cluster = graph.get(depend.spec()); + graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster}); + mark_minus(depend_cluster, graph, graph_plan); + } + } + + cluster.transient_uninstalled = true; + for (auto&& original_feature : cluster.original_features) + { + auto res = mark_plus(original_feature, cluster, graph, graph_plan); + if (res != MarkPlusResult::SUCCESS) + { + System::println(System::Color::warning, + "Warning: could not reinstall feature %s", + FeatureSpec{cluster.spec, original_feature}); + } + } + } + + static ClusterGraph create_feature_install_graph(const std::unordered_map& map, + const StatusParagraphs& status_db) + { + std::unordered_map ptr_map; + for (auto&& p : map) + ptr_map.emplace(p.first, &p.second); + ClusterGraph graph(std::move(ptr_map)); + + auto installed_ports = get_installed_ports(status_db); + + for (auto&& status_paragraph : installed_ports) + { + Cluster& cluster = graph.get(status_paragraph->package.spec); + + cluster.transient_uninstalled = false; + + cluster.status_paragraphs.emplace_back(status_paragraph); + + auto& status_paragraph_feature = status_paragraph->package.feature; + // In this case, empty string indicates the "core" paragraph for a package. + if (status_paragraph_feature.empty()) + { + cluster.original_features.insert("core"); + } + else + { + cluster.original_features.insert(status_paragraph_feature); + } + } + + for (auto&& status_paragraph : installed_ports) + { + auto& spec = status_paragraph->package.spec; + auto& status_paragraph_feature = status_paragraph->package.feature; + auto reverse_edges = FeatureSpec::from_strings_and_triplet(status_paragraph->package.depends, + status_paragraph->package.spec.triplet()); + + for (auto&& dependency : reverse_edges) + { + auto& dep_cluster = graph.get(dependency.spec()); + + auto depends_name = dependency.feature(); + if (depends_name.empty()) depends_name = "core"; + + auto& target_node = dep_cluster.edges[depends_name]; + target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature}); + } + } + return graph; + } + + std::vector create_feature_install_plan(const std::unordered_map& map, + const std::vector& specs, + const StatusParagraphs& status_db) + { + ClusterGraph graph = create_feature_install_graph(map, status_db); + + GraphPlan graph_plan; + for (auto&& spec : specs) + { + Cluster& spec_cluster = graph.get(spec.spec()); + spec_cluster.request_type = RequestType::USER_REQUESTED; + auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan); + + Checks::check_exit( + VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); + + graph_plan.install_graph.add_vertex(ClusterPtr{&spec_cluster}); + } + + Graphs::GraphAdjacencyProvider adjacency_remove_graph(graph_plan.remove_graph.adjacency_list()); + auto remove_vertex_list = graph_plan.remove_graph.vertex_list(); + auto remove_toposort = Graphs::topological_sort(remove_vertex_list, adjacency_remove_graph); + + Graphs::GraphAdjacencyProvider adjacency_install_graph(graph_plan.install_graph.adjacency_list()); + auto insert_vertex_list = graph_plan.install_graph.vertex_list(); + auto insert_toposort = Graphs::topological_sort(insert_vertex_list, adjacency_install_graph); + + std::vector plan; + + for (auto&& p_cluster : remove_toposort) + { + auto scf = *p_cluster->source_control_file.get(); + auto spec = PackageSpec::from_name_and_triplet(scf->core_paragraph->name, p_cluster->spec.triplet()) + .value_or_exit(VCPKG_LINE_INFO); + plan.emplace_back(RemovePlanAction{ + std::move(spec), + RemovePlanType::REMOVE, + p_cluster->request_type, + }); + } + + for (auto&& p_cluster : insert_toposort) + { + if (p_cluster->transient_uninstalled) + { + // If it will be transiently uninstalled, we need to issue a full installation command + auto pscf = p_cluster->source_control_file.value_or_exit(VCPKG_LINE_INFO); + Checks::check_exit(VCPKG_LINE_INFO, pscf != nullptr); + plan.emplace_back(InstallPlanAction{ + p_cluster->spec, + *pscf, + p_cluster->to_install_features, + p_cluster->request_type, + }); + } + else + { + // If the package isn't transitively installed, still include it if the user explicitly requested it + if (p_cluster->request_type != RequestType::USER_REQUESTED) continue; + plan.emplace_back(InstallPlanAction{ + p_cluster->spec, + p_cluster->original_features, + p_cluster->request_type, + }); + } + } + + return plan; + } +} diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp new file mode 100644 index 000000000..de6464c87 --- /dev/null +++ b/toolsrc/src/vcpkg/export.cpp @@ -0,0 +1,528 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace vcpkg::Export +{ + using Dependencies::ExportPlanAction; + using Dependencies::ExportPlanType; + using Dependencies::RequestType; + using Install::InstallDir; + + static std::string create_nuspec_file_contents(const std::string& raw_exported_dir, + const std::string& targets_redirect_path, + const std::string& nuget_id, + const std::string& nupkg_version) + { + static constexpr auto CONTENT_TEMPLATE = R"( + + + @NUGET_ID@ + @VERSION@ + vcpkg + + Vcpkg NuGet export + + + + + + + + + +)"; + + std::string nuspec_file_content = std::regex_replace(CONTENT_TEMPLATE, std::regex("@NUGET_ID@"), nuget_id); + nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VERSION@"), nupkg_version); + nuspec_file_content = + std::regex_replace(nuspec_file_content, std::regex("@RAW_EXPORTED_DIR@"), raw_exported_dir); + nuspec_file_content = + std::regex_replace(nuspec_file_content, std::regex("@TARGETS_REDIRECT_PATH@"), targets_redirect_path); + return nuspec_file_content; + } + + static std::string create_targets_redirect(const std::string& target_path) noexcept + { + return Strings::format(R"###( + + + +)###", + target_path, + target_path); + } + + static void print_plan(const std::map>& group_by_plan_type) + { + static constexpr std::array ORDER = {ExportPlanType::ALREADY_BUILT, + ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT}; + + for (const ExportPlanType plan_type : ORDER) + { + const auto it = group_by_plan_type.find(plan_type); + if (it == group_by_plan_type.cend()) + { + continue; + } + + std::vector cont = it->second; + std::sort(cont.begin(), cont.end(), &ExportPlanAction::compare_by_name); + const std::string as_string = Strings::join("\n", cont, [](const ExportPlanAction* p) { + return Dependencies::to_output_string(p->request_type, p->spec.to_string()); + }); + + switch (plan_type) + { + case ExportPlanType::ALREADY_BUILT: + System::println("The following packages are already built and will be exported:\n%s", as_string); + continue; + case ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT: + System::println("The following packages need to be built:\n%s", as_string); + continue; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + } + + static std::string create_export_id() + { + const tm date_time = System::get_current_date_time(); + + // Format is: YYYYmmdd-HHMMSS + // 15 characters + 1 null terminating character will be written for a total of 16 chars + char mbstr[16]; + const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y%m%d-%H%M%S", &date_time); + Checks::check_exit(VCPKG_LINE_INFO, + bytes_written == 15, + "Expected 15 bytes to be written, but %u were written", + bytes_written); + const std::string date_time_as_string(mbstr); + return ("vcpkg-export-" + date_time_as_string); + } + + static fs::path do_nuget_export(const VcpkgPaths& paths, + const std::string& nuget_id, + const std::string& nuget_version, + const fs::path& raw_exported_dir, + const fs::path& output_dir) + { + Files::Filesystem& fs = paths.get_filesystem(); + const fs::path& nuget_exe = paths.get_nuget_exe(); + + // This file will be placed in "build\native" in the nuget package. Therefore, go up two dirs. + const std::string targets_redirect_content = + create_targets_redirect("../../scripts/buildsystems/msbuild/vcpkg.targets"); + const fs::path targets_redirect = paths.buildsystems / "tmp" / "vcpkg.export.nuget.targets"; + + std::error_code ec; + fs.create_directories(paths.buildsystems / "tmp", ec); + + fs.write_contents(targets_redirect, targets_redirect_content); + + const std::string nuspec_file_content = + create_nuspec_file_contents(raw_exported_dir.string(), targets_redirect.string(), nuget_id, nuget_version); + const fs::path nuspec_file_path = paths.buildsystems / "tmp" / "vcpkg.export.nuspec"; + fs.write_contents(nuspec_file_path, nuspec_file_content); + + // -NoDefaultExcludes is needed for ".vcpkg-root" + const std::wstring cmd_line = + Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes > nul)", + nuget_exe.native(), + output_dir.native(), + nuspec_file_path.native()); + + const int exit_code = System::cmd_execute_clean(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed"); + + const fs::path output_path = output_dir / (nuget_id + ".nupkg"); + return output_path; + } + + struct ArchiveFormat final + { + enum class BackingEnum + { + ZIP = 1, + SEVEN_ZIP, + }; + + constexpr ArchiveFormat() = delete; + + constexpr ArchiveFormat(BackingEnum backing_enum, const wchar_t* extension, const wchar_t* cmake_option) + : backing_enum(backing_enum), m_extension(extension), m_cmake_option(cmake_option) + { + } + + constexpr operator BackingEnum() const { return backing_enum; } + constexpr CWStringView extension() const { return this->m_extension; } + constexpr CWStringView cmake_option() const { return this->m_cmake_option; } + + private: + BackingEnum backing_enum; + const wchar_t* m_extension; + const wchar_t* m_cmake_option; + }; + + namespace ArchiveFormatC + { + constexpr const ArchiveFormat ZIP(ArchiveFormat::BackingEnum::ZIP, L"zip", L"zip"); + constexpr const ArchiveFormat SEVEN_ZIP(ArchiveFormat::BackingEnum::SEVEN_ZIP, L"7z", L"7zip"); + } + + static fs::path do_archive_export(const VcpkgPaths& paths, + const fs::path& raw_exported_dir, + const fs::path& output_dir, + const ArchiveFormat& format) + { + const fs::path& cmake_exe = paths.get_cmake_exe(); + + const std::wstring exported_dir_filename = raw_exported_dir.filename().native(); + const std::wstring exported_archive_filename = + Strings::wformat(L"%s.%s", exported_dir_filename, format.extension()); + const fs::path exported_archive_path = (output_dir / exported_archive_filename); + + // -NoDefaultExcludes is needed for ".vcpkg-root" + const std::wstring cmd_line = Strings::wformat(LR"("%s" -E tar "cf" "%s" --format=%s -- "%s")", + cmake_exe.native(), + exported_archive_path.native(), + format.cmake_option(), + raw_exported_dir.native()); + + const int exit_code = System::cmd_execute_clean(cmd_line); + Checks::check_exit( + VCPKG_LINE_INFO, exit_code == 0, "Error: %s creation failed", exported_archive_path.generic_string()); + return exported_archive_path; + } + + static Optional maybe_lookup(std::unordered_map const& m, + std::string const& key) + { + const auto it = m.find(key); + if (it != m.end()) return it->second; + return nullopt; + } + + void export_integration_files(const fs::path& raw_exported_dir_path, const VcpkgPaths& paths) + { + const std::vector integration_files_relative_to_root = { + {".vcpkg-root"}, + {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"}, + {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, + {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"}, + {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, + {fs::path{"scripts"} / "getWindowsSDK.ps1"}, + {fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"}, + {fs::path{"scripts"} / "getProgramFiles32bit.ps1"}, + }; + + for (const fs::path& file : integration_files_relative_to_root) + { + const fs::path source = paths.root / file; + fs::path destination = raw_exported_dir_path / file; + Files::Filesystem& fs = paths.get_filesystem(); + std::error_code ec; + fs.create_directories(destination.parent_path(), ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec); + fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec); + } + } + + struct ExportArguments + { + bool dry_run; + bool raw; + bool nuget; + bool ifw; + bool zip; + bool seven_zip; + + Optional maybe_nuget_id; + Optional maybe_nuget_version; + + IFW::Options ifw_options; + std::vector specs; + }; + + static ExportArguments handle_export_command_arguments(const VcpkgCmdArguments& args, + const Triplet& default_triplet) + { + ExportArguments ret; + + static const std::string OPTION_DRY_RUN = "--dry-run"; + static const std::string OPTION_RAW = "--raw"; + static const std::string OPTION_NUGET = "--nuget"; + static const std::string OPTION_IFW = "--ifw"; + static const std::string OPTION_ZIP = "--zip"; + static const std::string OPTION_SEVEN_ZIP = "--7zip"; + static const std::string OPTION_NUGET_ID = "--nuget-id"; + static const std::string OPTION_NUGET_VERSION = "--nuget-version"; + static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url"; + static const std::string OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path"; + static const std::string OPTION_IFW_REPOSITORY_DIR_PATH = "--ifw-repository-directory-path"; + static const std::string OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path"; + static const std::string OPTION_IFW_INSTALLER_FILE_PATH = "--ifw-installer-file-path"; + + // input sanitization + static const std::string EXAMPLE = Help::create_example_string("export zlib zlib:x64-windows boost --nuget"); + args.check_min_arg_count(1, EXAMPLE); + + ret.specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE); + }); + + const auto options = args.check_and_get_optional_command_arguments( + { + OPTION_DRY_RUN, + OPTION_RAW, + OPTION_NUGET, + OPTION_IFW, + OPTION_ZIP, + OPTION_SEVEN_ZIP, + }, + { + OPTION_NUGET_ID, + OPTION_NUGET_VERSION, + OPTION_IFW_REPOSITORY_URL, + OPTION_IFW_PACKAGES_DIR_PATH, + OPTION_IFW_REPOSITORY_DIR_PATH, + OPTION_IFW_CONFIG_FILE_PATH, + OPTION_IFW_INSTALLER_FILE_PATH, + }); + ret.dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend(); + ret.raw = options.switches.find(OPTION_RAW) != options.switches.cend(); + ret.nuget = options.switches.find(OPTION_NUGET) != options.switches.cend(); + ret.ifw = options.switches.find(OPTION_IFW) != options.switches.cend(); + ret.zip = options.switches.find(OPTION_ZIP) != options.switches.cend(); + ret.seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend(); + + if (!ret.raw && !ret.nuget && !ret.ifw && !ret.zip && !ret.seven_zip && !ret.dry_run) + { + System::println(System::Color::error, + "Must provide at least one export type: --raw --nuget --ifw --zip --7zip"); + System::print(EXAMPLE); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + struct OptionPair + { + const std::string& name; + Optional& out_opt; + }; + const auto options_implies = + [&](const std::string& main_opt_name, bool main_opt, Span implying_opts) { + if (main_opt) + { + for (auto&& opt : implying_opts) + opt.out_opt = maybe_lookup(options.settings, opt.name); + } + else + { + for (auto&& opt : implying_opts) + Checks::check_exit(VCPKG_LINE_INFO, + !maybe_lookup(options.settings, opt.name), + "%s is only valid with %s", + opt.name, + main_opt_name); + } + }; + + options_implies(OPTION_NUGET, + ret.nuget, + { + {OPTION_NUGET_ID, ret.maybe_nuget_id}, + {OPTION_NUGET_VERSION, ret.maybe_nuget_version}, + }); + + options_implies(OPTION_IFW, + ret.ifw, + { + {OPTION_IFW_REPOSITORY_URL, ret.ifw_options.maybe_repository_url}, + {OPTION_IFW_PACKAGES_DIR_PATH, ret.ifw_options.maybe_packages_dir_path}, + {OPTION_IFW_REPOSITORY_DIR_PATH, ret.ifw_options.maybe_repository_dir_path}, + {OPTION_IFW_CONFIG_FILE_PATH, ret.ifw_options.maybe_config_file_path}, + {OPTION_IFW_INSTALLER_FILE_PATH, ret.ifw_options.maybe_installer_file_path}, + }); + return ret; + } + + static void print_next_step_info(const fs::path& prefix) + { + const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; + const CMakeVariable cmake_variable = CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); + System::println("\n" + "To use the exported libraries in CMake projects use:" + "\n" + " %s" + "\n", + Strings::to_utf8(cmake_variable.s)); + }; + + static void handle_raw_based_export(Span export_plan, + const ExportArguments& opts, + const std::string& export_id, + const VcpkgPaths& paths) + { + Files::Filesystem& fs = paths.get_filesystem(); + const fs::path export_to_path = paths.root; + const fs::path raw_exported_dir_path = export_to_path / export_id; + std::error_code ec; + fs.remove_all(raw_exported_dir_path, ec); + fs.create_directory(raw_exported_dir_path, ec); + + // execute the plan + for (const ExportPlanAction& action : export_plan) + { + if (action.plan_type != ExportPlanType::ALREADY_BUILT) + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + const std::string display_name = action.spec.to_string(); + System::println("Exporting package %s... ", display_name); + + const BinaryParagraph& binary_paragraph = + action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO).core_paragraph; + + const InstallDir dirs = InstallDir::from_destination_root( + raw_exported_dir_path / "installed", + action.spec.triplet().to_string(), + raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); + + Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs); + System::println(System::Color::success, "Exporting package %s... done", display_name); + } + + // Copy files needed for integration + export_integration_files(raw_exported_dir_path, paths); + + if (opts.raw) + { + System::println( + System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string()); + print_next_step_info(export_to_path); + } + + if (opts.nuget) + { + System::println("Creating nuget package... "); + + const std::string nuget_id = opts.maybe_nuget_id.value_or(raw_exported_dir_path.filename().string()); + const std::string nuget_version = opts.maybe_nuget_version.value_or("1.0.0"); + const fs::path output_path = + do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path); + System::println(System::Color::success, "Creating nuget package... done"); + System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string()); + + System::println(R"( +With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: + Install-Package %s -Source "%s" +)" + "\n", + nuget_id, + output_path.parent_path().u8string()); + } + + if (opts.zip) + { + System::println("Creating zip archive... "); + const fs::path output_path = + do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP); + System::println(System::Color::success, "Creating zip archive... done"); + System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string()); + print_next_step_info("[...]"); + } + + if (opts.seven_zip) + { + System::println("Creating 7zip archive... "); + const fs::path output_path = + do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP); + System::println(System::Color::success, "Creating 7zip archive... done"); + System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string()); + print_next_step_info("[...]"); + } + + if (!opts.raw) + { + fs.remove_all(raw_exported_dir_path, ec); + } + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + const auto opts = handle_export_command_arguments(args, default_triplet); + for (auto&& spec : opts.specs) + Input::check_triplet(spec.triplet(), paths); + + // create the plan + const StatusParagraphs status_db = database_load_check(paths); + std::vector export_plan = Dependencies::create_export_plan(paths, opts.specs, status_db); + Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty"); + + std::map> group_by_plan_type; + Util::group_by(export_plan, &group_by_plan_type, [](const ExportPlanAction& p) { return p.plan_type; }); + print_plan(group_by_plan_type); + + const bool has_non_user_requested_packages = + Util::find_if(export_plan, [](const ExportPlanAction& package) -> bool { + return package.request_type != RequestType::USER_REQUESTED; + }) != export_plan.cend(); + + if (has_non_user_requested_packages) + { + System::println(System::Color::warning, + "Additional packages (*) need to be exported to complete this operation."); + } + + const auto it = group_by_plan_type.find(ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT); + if (it != group_by_plan_type.cend() && !it->second.empty()) + { + System::println(System::Color::error, "There are packages that have not been built."); + + // No need to show all of them, just the user-requested ones. Dependency resolution will handle the rest. + std::vector unbuilt = it->second; + Util::erase_remove_if( + unbuilt, [](const ExportPlanAction* a) { return a->request_type != RequestType::USER_REQUESTED; }); + + const auto s = Strings::join(" ", unbuilt, [](const ExportPlanAction* a) { return a->spec.to_string(); }); + System::println("To build them, run:\n" + " vcpkg install %s", + s); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + if (opts.dry_run) + { + Checks::exit_success(VCPKG_LINE_INFO); + } + + std::string export_id = create_export_id(); + + if (opts.raw || opts.nuget || opts.zip || opts.seven_zip) + { + handle_raw_based_export(export_plan, opts, export_id, paths); + } + + if (opts.ifw) + { + IFW::do_export(export_plan, export_id, opts.ifw_options, paths); + + print_next_step_info("@RootDir@/src/vcpkg"); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/globalstate.cpp b/toolsrc/src/vcpkg/globalstate.cpp new file mode 100644 index 000000000..de564d357 --- /dev/null +++ b/toolsrc/src/vcpkg/globalstate.cpp @@ -0,0 +1,13 @@ +#include "pch.h" + +#include + +namespace vcpkg +{ + Util::LockGuarded GlobalState::timer; + std::atomic GlobalState::debugging = false; + std::atomic GlobalState::feature_packages = false; + + std::atomic GlobalState::g_init_console_cp = 0; + std::atomic GlobalState::g_init_console_output_cp = 0; +} diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp new file mode 100644 index 000000000..7867f71d2 --- /dev/null +++ b/toolsrc/src/vcpkg/help.cpp @@ -0,0 +1,131 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg::Help +{ + void help_topics() + { + System::println("Available help topics:\n" + " triplet\n" + " integrate\n" + " export"); + } + + void help_topic_valid_triplet(const VcpkgPaths& paths) + { + System::println("Available architecture triplets:"); + for (auto&& path : paths.get_filesystem().get_files_non_recursive(paths.triplets)) + { + System::println(" %s", path.stem().filename().string()); + } + } + + void help_topic_export() + { + System::println("Summary:\n" + " vcpkg export [options] ...\n" + "\n" + "Options:\n" + " --7zip Export to a 7zip (.7z) file\n" + " --dry-run Do not actually export\n" + " --nuget Export a NuGet package\n" + " --nuget-id= Specify the id for the exported NuGet package\n" + " --nuget-version= Specify the version for the exported NuGet package\n" + " --raw Export to an uncompressed directory\n" + " --zip Export to a zip file"); + } + + void print_usage() + { + System::println( + "Commands:\n" + " vcpkg search [pat] Search for packages available to be built\n" + " vcpkg install ... Install a package\n" + " vcpkg remove ... Uninstall a package\n" + " vcpkg remove --outdated Uninstall all out-of-date packages\n" + " vcpkg list List installed packages\n" + " vcpkg update Display list of packages for updating\n" + " vcpkg hash [alg] Hash a file by specific algorithm, default SHA512\n" + " vcpkg help topics Display the list of help topics\n" + " vcpkg help Display help for a specific topic\n" + "\n" + "%s" // Integration help + "\n" + " vcpkg export ... [opt]... Exports a package\n" + " vcpkg edit Open up a port for editing (uses %%EDITOR%%, default 'code')\n" + " vcpkg import Import a pre-built library\n" + " vcpkg create \n" + " [archivename] Create a new package\n" + " vcpkg owns Search for files in installed packages\n" + " vcpkg cache List cached compiled packages\n" + " vcpkg version Display version information\n" + " vcpkg contact Display contact information to send feedback\n" + "\n" + //"internal commands:\n" + //" --check-build-deps \n" + //" --create-binary-control \n" + //"\n" + "Options:\n" + " --triplet Specify the target architecture triplet.\n" + " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n" + "\n" + " --vcpkg-root Specify the vcpkg root directory\n" + " (default: %%VCPKG_ROOT%%)\n" + "\n" + "For more help (including examples) see the accompanying README.md.", + Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING); + } + + std::string create_example_string(const std::string& command_and_arguments) + { + std::string cs = Strings::format("Example:\n" + " vcpkg %s\n", + command_and_arguments); + return cs; + } + + void print_example(const std::string& command_and_arguments) + { + System::println(create_example_string(command_and_arguments)); + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + args.check_max_arg_count(1); + args.check_and_get_optional_command_arguments({}); + + if (args.command_arguments.empty()) + { + print_usage(); + Checks::exit_success(VCPKG_LINE_INFO); + } + const auto& topic = args.command_arguments[0]; + if (topic == "triplet" || topic == "triplets" || topic == "triple") + { + help_topic_valid_triplet(paths); + } + else if (topic == "export") + { + help_topic_export(); + } + else if (topic == "integrate") + { + System::print("Commands:\n" + "%s", + Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING); + } + else if (topic == "topics") + { + help_topics(); + } + else + { + System::println(System::Color::error, "Error: unknown topic %s", topic); + help_topics(); + Checks::exit_fail(VCPKG_LINE_INFO); + } + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/input.cpp b/toolsrc/src/vcpkg/input.cpp new file mode 100644 index 000000000..aee0fac7f --- /dev/null +++ b/toolsrc/src/vcpkg/input.cpp @@ -0,0 +1,55 @@ +#include "pch.h" + +#include +#include +#include +#include +#include + +namespace vcpkg::Input +{ + PackageSpec check_and_get_package_spec(const std::string& package_spec_as_string, + const Triplet& default_triplet, + CStringView example_text) + { + const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string); + auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet); + if (const auto spec = expected_spec.get()) + { + return PackageSpec{spec->package_spec}; + } + + // Intentionally show the lowercased string + System::println(System::Color::error, "Error: %s: %s", vcpkg::to_string(expected_spec.error()), as_lowercase); + System::print(example_text); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + void check_triplet(const Triplet& t, const VcpkgPaths& paths) + { + if (!paths.is_valid_triplet(t)) + { + System::println(System::Color::error, "Error: invalid triplet: %s", t); + Metrics::g_metrics.lock()->track_property("error", "invalid triplet: " + t.to_string()); + Help::help_topic_valid_triplet(paths); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } + + FullPackageSpec check_and_get_full_package_spec(const std::string& full_package_spec_as_string, + const Triplet& default_triplet, + CStringView example_text) + { + const std::string as_lowercase = Strings::ascii_to_lowercase(full_package_spec_as_string); + auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet); + if (const auto spec = expected_spec.get()) + { + return *spec; + } + + // Intentionally show the lowercased string + System::println(System::Color::error, "Error: %s: %s", vcpkg::to_string(expected_spec.error()), as_lowercase); + System::print(example_text); + Checks::exit_fail(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp new file mode 100644 index 000000000..74bea0d5f --- /dev/null +++ b/toolsrc/src/vcpkg/install.cpp @@ -0,0 +1,615 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Install +{ + using namespace Dependencies; + + InstallDir InstallDir::from_destination_root(const fs::path& destination_root, + const std::string& destination_subdirectory, + const fs::path& listfile) + { + InstallDir dirs; + dirs.m_destination = destination_root / destination_subdirectory; + dirs.m_destination_subdirectory = destination_subdirectory; + dirs.m_listfile = listfile; + return dirs; + } + + const fs::path& InstallDir::destination() const { return this->m_destination; } + + const std::string& InstallDir::destination_subdirectory() const { return this->m_destination_subdirectory; } + + const fs::path& InstallDir::listfile() const { return this->m_listfile; } + + void install_files_and_write_listfile(Files::Filesystem& fs, + const fs::path& source_dir, + const InstallDir& destination_dir) + { + std::vector output; + std::error_code ec; + + const size_t prefix_length = source_dir.native().size(); + const fs::path& destination = destination_dir.destination(); + const std::string& destination_subdirectory = destination_dir.destination_subdirectory(); + const fs::path& listfile = destination_dir.listfile(); + + Checks::check_exit( + VCPKG_LINE_INFO, fs.exists(source_dir), "Source directory %s does not exist", source_dir.generic_string()); + fs.create_directories(destination, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create destination directory %s", destination.generic_string()); + const fs::path listfile_parent = listfile.parent_path(); + fs.create_directories(listfile_parent, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !ec, "Could not create directory for listfile %s", listfile.generic_string()); + + output.push_back(Strings::format(R"(%s/)", destination_subdirectory)); + auto files = fs.get_files_recursive(source_dir); + for (auto&& file : files) + { + const auto status = fs.status(file, ec); + if (ec) + { + System::println(System::Color::error, "failed: %s: %s", file.u8string(), ec.message()); + continue; + } + + const std::string filename = file.filename().generic_string(); + if (fs::is_regular_file(status) && + (Strings::case_insensitive_ascii_compare(filename.c_str(), "CONTROL") || + Strings::case_insensitive_ascii_compare(filename.c_str(), "BUILD_INFO"))) + { + // Do not copy the control file + continue; + } + + const std::string suffix = file.generic_u8string().substr(prefix_length + 1); + const fs::path target = destination / suffix; + + if (fs::is_directory(status)) + { + fs.create_directory(target, ec); + if (ec) + { + System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message()); + } + + // Trailing backslash for directories + output.push_back(Strings::format(R"(%s/%s/)", destination_subdirectory, suffix)); + continue; + } + + if (fs::is_regular_file(status)) + { + if (fs.exists(target)) + { + System::println(System::Color::warning, + "File %s was already present and will be overwritten", + target.u8string(), + ec.message()); + } + fs.copy_file(file, target, fs::copy_options::overwrite_existing, ec); + if (ec) + { + System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message()); + } + output.push_back(Strings::format(R"(%s/%s)", destination_subdirectory, suffix)); + continue; + } + + if (!fs::status_known(status)) + { + System::println(System::Color::error, "failed: %s: unknown status", file.u8string()); + continue; + } + + System::println(System::Color::error, "failed: %s: cannot handle file type", file.u8string()); + } + + std::sort(output.begin(), output.end()); + + fs.write_lines(listfile, output); + } + + static void remove_first_n_chars(std::vector* strings, const size_t n) + { + for (std::string& s : *strings) + { + s.erase(0, n); + } + }; + + static std::vector extract_files_in_triplet( + const std::vector& pgh_and_files, const Triplet& triplet) + { + std::vector output; + for (const StatusParagraphAndAssociatedFiles& t : pgh_and_files) + { + if (t.pgh.package.spec.triplet() != triplet) + { + continue; + } + + output.insert(output.end(), t.files.begin(), t.files.end()); + } + + std::sort(output.begin(), output.end()); + return output; + } + + static SortedVector build_list_of_package_files(const Files::Filesystem& fs, + const fs::path& package_dir) + { + const std::vector package_file_paths = fs.get_files_recursive(package_dir); + const size_t package_remove_char_count = package_dir.generic_string().size() + 1; // +1 for the slash + auto package_files = Util::fmap(package_file_paths, [package_remove_char_count](const fs::path& path) { + std::string as_string = path.generic_string(); + as_string.erase(0, package_remove_char_count); + return std::move(as_string); + }); + + return SortedVector(std::move(package_files)); + } + + static SortedVector build_list_of_installed_files( + const std::vector& pgh_and_files, const Triplet& triplet) + { + std::vector installed_files = extract_files_in_triplet(pgh_and_files, triplet); + const size_t installed_remove_char_count = triplet.canonical_name().size() + 1; // +1 for the slash + remove_first_n_chars(&installed_files, installed_remove_char_count); + + return SortedVector(std::move(installed_files)); + } + + InstallResult install_package(const VcpkgPaths& paths, const BinaryControlFile& bcf, StatusParagraphs* status_db) + { + const fs::path package_dir = paths.package_dir(bcf.core_paragraph.spec); + const Triplet& triplet = bcf.core_paragraph.spec.triplet(); + const std::vector pgh_and_files = get_installed_files(paths, *status_db); + + const SortedVector package_files = + build_list_of_package_files(paths.get_filesystem(), package_dir); + const SortedVector installed_files = build_list_of_installed_files(pgh_and_files, triplet); + + std::vector intersection; + std::set_intersection(package_files.begin(), + package_files.end(), + installed_files.begin(), + installed_files.end(), + std::back_inserter(intersection)); + + if (!intersection.empty()) + { + const fs::path triplet_install_path = paths.installed / triplet.canonical_name(); + System::println(System::Color::error, + "The following files are already installed in %s and are in conflict with %s", + triplet_install_path.generic_string(), + bcf.core_paragraph.spec); + System::print("\n "); + System::println(Strings::join("\n ", intersection)); + System::println(); + return InstallResult::FILE_CONFLICTS; + } + + StatusParagraph source_paragraph; + source_paragraph.package = bcf.core_paragraph; + source_paragraph.want = Want::INSTALL; + source_paragraph.state = InstallState::HALF_INSTALLED; + + write_update(paths, source_paragraph); + status_db->insert(std::make_unique(source_paragraph)); + + std::vector features_spghs; + for (auto&& feature : bcf.features) + { + features_spghs.emplace_back(); + + StatusParagraph& feature_paragraph = features_spghs.back(); + feature_paragraph.package = feature; + feature_paragraph.want = Want::INSTALL; + feature_paragraph.state = InstallState::HALF_INSTALLED; + + write_update(paths, feature_paragraph); + status_db->insert(std::make_unique(feature_paragraph)); + } + + const InstallDir install_dir = InstallDir::from_destination_root( + paths.installed, triplet.to_string(), paths.listfile_path(bcf.core_paragraph)); + + install_files_and_write_listfile(paths.get_filesystem(), package_dir, install_dir); + + source_paragraph.state = InstallState::INSTALLED; + write_update(paths, source_paragraph); + status_db->insert(std::make_unique(source_paragraph)); + + for (auto&& feature_paragraph : features_spghs) + { + feature_paragraph.state = InstallState::INSTALLED; + write_update(paths, feature_paragraph); + status_db->insert(std::make_unique(feature_paragraph)); + } + + return InstallResult::SUCCESS; + } + + using Build::BuildResult; + + BuildResult perform_install_plan_action(const VcpkgPaths& paths, + const InstallPlanAction& action, + const Build::BuildPackageOptions& build_package_options, + StatusParagraphs& status_db) + { + const InstallPlanType& plan_type = action.plan_type; + const std::string display_name = action.spec.to_string(); + const std::string display_name_with_features = + GlobalState::feature_packages ? action.displayname() : display_name; + + const bool is_user_requested = action.request_type == RequestType::USER_REQUESTED; + const bool use_head_version = to_bool(build_package_options.use_head_version); + + if (plan_type == InstallPlanType::ALREADY_INSTALLED) + { + if (use_head_version && is_user_requested) + System::println( + System::Color::warning, "Package %s is already installed -- not building from HEAD", display_name); + else + System::println(System::Color::success, "Package %s is already installed", display_name); + return BuildResult::SUCCEEDED; + } + + if (plan_type == InstallPlanType::BUILD_AND_INSTALL) + { + if (use_head_version) + System::println("Building package %s from HEAD... ", display_name_with_features); + else + System::println("Building package %s... ", display_name_with_features); + + const auto result = [&]() -> Build::ExtendedBuildResult { + if (GlobalState::feature_packages) + { + const Build::BuildPackageConfig build_config{ + *action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO), + action.spec.triplet(), + paths.port_dir(action.spec), + build_package_options, + action.feature_list}; + return Build::build_package(paths, build_config, status_db); + } + else + { + const Build::BuildPackageConfig build_config{ + action.any_paragraph.source_paragraph.value_or_exit(VCPKG_LINE_INFO), + action.spec.triplet(), + paths.port_dir(action.spec), + build_package_options}; + return Build::build_package(paths, build_config, status_db); + } + }(); + + if (result.code != Build::BuildResult::SUCCEEDED) + { + System::println(System::Color::error, Build::create_error_message(result.code, action.spec)); + return result.code; + } + + System::println("Building package %s... done", display_name_with_features); + + const BinaryControlFile bcf = + Paragraphs::try_load_cached_control_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO); + System::println("Installing package %s... ", display_name_with_features); + const auto install_result = install_package(paths, bcf, &status_db); + switch (install_result) + { + case InstallResult::SUCCESS: + System::println(System::Color::success, "Installing package %s... done", display_name); + return BuildResult::SUCCEEDED; + case InstallResult::FILE_CONFLICTS: return BuildResult::FILE_CONFLICTS; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + if (plan_type == InstallPlanType::INSTALL) + { + if (use_head_version && is_user_requested) + { + System::println( + System::Color::warning, "Package %s is already built -- not building from HEAD", display_name); + } + System::println("Installing package %s... ", display_name); + const auto install_result = install_package( + paths, action.any_paragraph.binary_control_file.value_or_exit(VCPKG_LINE_INFO), &status_db); + switch (install_result) + { + case InstallResult::SUCCESS: + System::println(System::Color::success, "Installing package %s... done", display_name); + return BuildResult::SUCCEEDED; + case InstallResult::FILE_CONFLICTS: return BuildResult::FILE_CONFLICTS; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + Checks::unreachable(VCPKG_LINE_INFO); + } + + static void print_plan(const std::vector& action_plan, bool is_recursive) + { + std::vector remove_plans; + std::vector rebuilt_plans; + std::vector only_install_plans; + std::vector new_plans; + std::vector already_installed_plans; + + const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { + if (auto iplan = package.install_plan.get()) + return iplan->request_type != RequestType::USER_REQUESTED; + else + return false; + }) != action_plan.cend(); + + for (auto&& action : action_plan) + { + if (auto install_action = action.install_plan.get()) + { + // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at + // all. + auto it = Util::find_if( + remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; }); + if (it != remove_plans.end()) + { + rebuilt_plans.emplace_back(install_action); + } + else + { + switch (install_action->plan_type) + { + case InstallPlanType::INSTALL: only_install_plans.emplace_back(install_action); break; + case InstallPlanType::ALREADY_INSTALLED: + if (install_action->request_type == RequestType::USER_REQUESTED) + already_installed_plans.emplace_back(install_action); + break; + case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + } + else if (auto remove_action = action.remove_plan.get()) + { + remove_plans.emplace_back(remove_action); + } + } + + std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name); + std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); + + if (already_installed_plans.size() > 0) + { + const std::string already_string = + Strings::join("\n", already_installed_plans, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname()); + }); + System::println("The following packages are already installed:\n%s", already_string); + } + + if (rebuilt_plans.size() > 0) + { + const std::string rebuilt_string = Strings::join("\n", rebuilt_plans, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname()); + }); + System::println("The following packages will be rebuilt:\n%s", rebuilt_string); + } + + if (new_plans.size() > 0) + { + const std::string new_string = Strings::join("\n", new_plans, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname()); + }); + System::println("The following packages will be built and installed:\n%s", new_string); + } + + if (only_install_plans.size() > 0) + { + const std::string only_install_string = + Strings::join("\n", only_install_plans, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname()); + }); + System::println("The following packages will be directly installed:\n%s", only_install_string); + } + + if (has_non_user_requested_packages) + System::println("Additional packages (*) will be installed to complete this operation."); + + if (remove_plans.size() > 0 && !is_recursive) + { + System::println(System::Color::warning, + "If you are sure you want to rebuild the above packages, run the command with the " + "--recurse option"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } + + void perform_and_exit_ex(const std::vector& action_plan, + const Build::BuildPackageOptions& install_plan_options, + const KeepGoing keep_going, + const PrintSummary print_summary, + const VcpkgPaths& paths, + StatusParagraphs& status_db) + { + std::vector results; + std::vector timing; + const auto timer = Chrono::ElapsedTime::create_started(); + size_t counter = 0; + const size_t package_count = action_plan.size(); + + for (const auto& action : action_plan) + { + const auto build_timer = Chrono::ElapsedTime::create_started(); + counter++; + + const std::string display_name = action.spec().to_string(); + System::println("Starting package %d/%d: %s", counter, package_count, display_name); + + timing.push_back("0"); + results.push_back(BuildResult::NULLVALUE); + + if (const auto install_action = action.install_plan.get()) + { + const BuildResult result = + perform_install_plan_action(paths, *install_action, install_plan_options, status_db); + if (result != BuildResult::SUCCEEDED && keep_going == KeepGoing::NO) + { + System::println(Build::create_user_troubleshooting_message(install_action->spec)); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + results.back() = result; + } + else if (const auto remove_action = action.remove_plan.get()) + { + Checks::check_exit(VCPKG_LINE_INFO, GlobalState::feature_packages); + Remove::perform_remove_plan_action(paths, *remove_action, Remove::Purge::YES, status_db); + } + else + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + timing.back() = build_timer.to_string(); + System::println("Elapsed time for package %s: %s", display_name, build_timer.to_string()); + } + + System::println("Total time taken: %s", timer.to_string()); + + if (print_summary == PrintSummary::YES) + { + for (size_t i = 0; i < results.size(); i++) + { + System::println("%s: %s: %s", action_plan[i].spec(), Build::to_string(results[i]), timing[i]); + } + + std::map summary; + for (const BuildResult& v : Build::BUILD_RESULT_VALUES) + { + summary[v] = 0; + } + + for (const BuildResult& r : results) + { + summary[r]++; + } + + System::println("\n\nSUMMARY"); + for (const std::pair& entry : summary) + { + System::println(" %s: %d", Build::to_string(entry.first), entry.second); + } + } + + Checks::exit_success(VCPKG_LINE_INFO); + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + static const std::string OPTION_DRY_RUN = "--dry-run"; + static const std::string OPTION_USE_HEAD_VERSION = "--head"; + static const std::string OPTION_NO_DOWNLOADS = "--no-downloads"; + static const std::string OPTION_RECURSE = "--recurse"; + static const std::string OPTION_KEEP_GOING = "--keep-going"; + + // input sanitization + static const std::string EXAMPLE = Help::create_example_string("install zlib zlib:x64-windows curl boost"); + args.check_min_arg_count(1, EXAMPLE); + + const std::vector specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_full_package_spec(arg, default_triplet, EXAMPLE); + }); + + for (auto&& spec : specs) + { + Input::check_triplet(spec.package_spec.triplet(), paths); + if (!spec.features.empty() && !GlobalState::feature_packages) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag."); + } + } + + const std::unordered_set options = args.check_and_get_optional_command_arguments( + {OPTION_DRY_RUN, OPTION_USE_HEAD_VERSION, OPTION_NO_DOWNLOADS, OPTION_RECURSE, OPTION_KEEP_GOING}); + const bool dry_run = options.find(OPTION_DRY_RUN) != options.cend(); + const bool use_head_version = options.find(OPTION_USE_HEAD_VERSION) != options.cend(); + const bool no_downloads = options.find(OPTION_NO_DOWNLOADS) != options.cend(); + const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); + const KeepGoing keep_going = to_keep_going(options.find(OPTION_KEEP_GOING) != options.cend()); + + // create the plan + StatusParagraphs status_db = database_load_check(paths); + + const Build::BuildPackageOptions install_plan_options = {Build::to_use_head_version(use_head_version), + Build::to_allow_downloads(!no_downloads)}; + + std::vector action_plan; + + if (GlobalState::feature_packages) + { + std::unordered_map scf_map; + auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); + for (auto&& port : all_ports) + { + scf_map[port->core_paragraph->name] = std::move(*port); + } + action_plan = create_feature_install_plan(scf_map, FullPackageSpec::to_feature_specs(specs), status_db); + } + else + { + Dependencies::PathsPortFile paths_port_file(paths); + auto install_plan = Dependencies::create_install_plan( + paths_port_file, Util::fmap(specs, [](auto&& spec) { return spec.package_spec; }), status_db); + + action_plan = Util::fmap( + install_plan, [](InstallPlanAction& install_action) { return AnyAction(std::move(install_action)); }); + } + + // install plan will be empty if it is already installed - need to change this at status paragraph part + Checks::check_exit(VCPKG_LINE_INFO, !action_plan.empty(), "Install plan cannot be empty"); + + // log the plan + const std::string specs_string = Strings::join(",", action_plan, [](const AnyAction& action) { + if (auto iaction = action.install_plan.get()) + return iaction->spec.to_string(); + else if (auto raction = action.remove_plan.get()) + return "R$" + raction->spec.to_string(); + Checks::unreachable(VCPKG_LINE_INFO); + }); + + Metrics::g_metrics.lock()->track_property("installplan", specs_string); + + print_plan(action_plan, is_recursive); + + if (dry_run) + { + Checks::exit_success(VCPKG_LINE_INFO); + } + + perform_and_exit_ex(action_plan, install_plan_options, keep_going, PrintSummary::NO, paths, status_db); + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp new file mode 100644 index 000000000..84c1ee208 --- /dev/null +++ b/toolsrc/src/vcpkg/metrics.cpp @@ -0,0 +1,398 @@ +#include "pch.h" + +#include + +#include +#include +#include + +#pragma comment(lib, "version") +#pragma comment(lib, "winhttp") + +namespace vcpkg::Metrics +{ + Util::LockGuarded g_metrics; + + static std::string get_current_date_time() + { + struct tm newtime; + std::array date; + date.fill(0); + + struct _timeb timebuffer; + + _ftime_s(&timebuffer); + time_t now = timebuffer.time; + const int milli = timebuffer.millitm; + + const errno_t err = gmtime_s(&newtime, &now); + if (err) + { + return Strings::EMPTY; + } + + strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S", &newtime); + return std::string(&date[0]) + "." + std::to_string(milli) + "Z"; + } + + static std::string generate_random_UUID() + { + int part_sizes[] = {8, 4, 4, 4, 12}; + char uuid[37]; + memset(uuid, 0, sizeof(uuid)); + int num; + srand(static_cast(time(nullptr))); + int index = 0; + for (int part = 0; part < 5; part++) + { + if (part > 0) + { + uuid[index] = '-'; + index++; + } + + // Generating UUID format version 4 + // http://en.wikipedia.org/wiki/Universally_unique_identifier + for (int i = 0; i < part_sizes[part]; i++, index++) + { + if (part == 2 && i == 0) + { + num = 4; + } + else if (part == 4 && i == 0) + { + num = (rand() % 4) + 8; + } + else + { + num = rand() % 16; + } + + if (num < 10) + { + uuid[index] = static_cast('0' + num); + } + else + { + uuid[index] = static_cast('a' + (num - 10)); + } + } + } + + return uuid; + } + + static const std::string& get_session_id() + { + static const std::string ID = generate_random_UUID(); + return ID; + } + + static std::string to_json_string(const std::string& str) + { + std::string encoded = "\""; + for (auto&& ch : str) + { + if (ch == '\\') + { + encoded.append("\\\\"); + } + else if (ch == '"') + { + encoded.append("\\\""); + } + else if (ch < 0x20 || ch >= 0x80) + { + // Note: this treats incoming Strings as Latin-1 + static constexpr const char HEX[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + encoded.append("\\u00"); + encoded.push_back(HEX[ch / 16]); + encoded.push_back(HEX[ch % 16]); + } + else + { + encoded.push_back(ch); + } + } + encoded.push_back('"'); + return encoded; + } + + static std::string get_os_version_string() + { + std::wstring path; + path.resize(MAX_PATH); + const auto n = GetSystemDirectoryW(&path[0], static_cast(path.size())); + path.resize(n); + path += L"\\kernel32.dll"; + + const auto versz = GetFileVersionInfoSizeW(path.c_str(), nullptr); + if (versz == 0) return Strings::EMPTY; + + std::vector verbuf; + verbuf.resize(versz); + + if (!GetFileVersionInfoW(path.c_str(), 0, static_cast(verbuf.size()), &verbuf[0])) return Strings::EMPTY; + + void* rootblock; + UINT rootblocksize; + if (!VerQueryValueW(&verbuf[0], L"\\", &rootblock, &rootblocksize)) return Strings::EMPTY; + + auto rootblock_ffi = static_cast(rootblock); + + return Strings::format("%d.%d.%d", + static_cast(HIWORD(rootblock_ffi->dwProductVersionMS)), + static_cast(LOWORD(rootblock_ffi->dwProductVersionMS)), + static_cast(HIWORD(rootblock_ffi->dwProductVersionLS))); + } + + struct MetricMessage + { + std::string user_id = generate_random_UUID(); + std::string user_timestamp; + std::string timestamp = get_current_date_time(); + std::string properties; + std::string measurements; + + void track_property(const std::string& name, const std::string& value) + { + if (properties.size() != 0) properties.push_back(','); + properties.append(to_json_string(name)); + properties.push_back(':'); + properties.append(to_json_string(value)); + } + + void track_metric(const std::string& name, double value) + { + if (measurements.size() != 0) measurements.push_back(','); + measurements.append(to_json_string(name)); + measurements.push_back(':'); + measurements.append(std::to_string(value)); + } + + std::string format_event_data_template() const + { + const std::string& session_id = get_session_id(); + return Strings::format(R"([{ + "ver": 1, + "name": "Microsoft.ApplicationInsights.Event", + "time": "%s", + "sampleRate": 100.000000, + "seq": "0:0", + "iKey": "b4e88960-4393-4dd9-ab8e-97e8fe6d7603", + "flags": 0.000000, + "tags": { + "ai.device.os": "Windows", + "ai.device.osVersion": "%s", + "ai.session.id": "%s", + "ai.user.id": "%s", + "ai.user.accountAcquisitionDate": "%s" + }, + "data": { + "baseType": "EventData", + "baseData": { + "ver": 2, + "name": "commandline_test7", + "properties": { %s }, + "measurements": { %s } + } + } +}])", + timestamp, + get_os_version_string(), + session_id, + user_id, + user_timestamp, + properties, + measurements); + } + }; + + static MetricMessage g_metricmessage; + static bool g_should_send_metrics = +#if defined(NDEBUG) && (DISABLE_METRICS == 0) + true +#else + false +#endif + ; + static bool g_should_print_metrics = false; + + bool get_compiled_metrics_enabled() { return DISABLE_METRICS == 0; } + + std::wstring get_SQM_user() + { + auto hkcu_sqmclient = + System::get_registry_string(HKEY_CURRENT_USER, LR"(Software\Microsoft\SQMClient)", L"UserId"); + return hkcu_sqmclient.value_or(L"{}"); + } + + void Metrics::set_user_information(const std::string& user_id, const std::string& first_use_time) + { + g_metricmessage.user_id = user_id; + g_metricmessage.user_timestamp = first_use_time; + } + + void Metrics::init_user_information(std::string& user_id, std::string& first_use_time) + { + user_id = generate_random_UUID(); + first_use_time = get_current_date_time(); + } + + void Metrics::set_send_metrics(bool should_send_metrics) { g_should_send_metrics = should_send_metrics; } + + void Metrics::set_print_metrics(bool should_print_metrics) { g_should_print_metrics = should_print_metrics; } + + void Metrics::track_metric(const std::string& name, double value) { g_metricmessage.track_metric(name, value); } + + void Metrics::track_property(const std::string& name, const std::wstring& value) + { + // Note: this is not valid UTF-16 -> UTF-8, it just yields a close enough approximation for our purposes. + std::string converted_value; + converted_value.resize(value.size()); + std::transform( + value.begin(), value.end(), converted_value.begin(), [](wchar_t ch) { return static_cast(ch); }); + + g_metricmessage.track_property(name, converted_value); + } + + void Metrics::track_property(const std::string& name, const std::string& value) + { + g_metricmessage.track_property(name, value); + } + + void Metrics::upload(const std::string& payload) + { + HINTERNET connect = nullptr, request = nullptr; + BOOL results = FALSE; + + const HINTERNET session = WinHttpOpen( + L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + if (session) connect = WinHttpConnect(session, L"dc.services.visualstudio.com", INTERNET_DEFAULT_HTTPS_PORT, 0); + + if (connect) + request = WinHttpOpenRequest(connect, + L"POST", + L"/v2/track", + nullptr, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + WINHTTP_FLAG_SECURE); + + if (request) + { + if (MAXDWORD <= payload.size()) abort(); + std::wstring hdrs = L"Content-Type: application/json\r\n"; + std::string& p = const_cast(payload); + results = WinHttpSendRequest(request, + hdrs.c_str(), + static_cast(hdrs.size()), + static_cast(&p[0]), + static_cast(payload.size()), + static_cast(payload.size()), + 0); + } + + if (results) + { + results = WinHttpReceiveResponse(request, nullptr); + } + + DWORD http_code = 0, junk = sizeof(DWORD); + + if (results) + { + results = WinHttpQueryHeaders(request, + WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + nullptr, + &http_code, + &junk, + WINHTTP_NO_HEADER_INDEX); + } + + std::vector response_buffer; + if (results) + { + DWORD available_data = 0, read_data = 0, total_data = 0; + while ((results = WinHttpQueryDataAvailable(request, &available_data)) == TRUE && available_data > 0) + { + response_buffer.resize(response_buffer.size() + available_data); + + results = WinHttpReadData(request, &response_buffer.data()[total_data], available_data, &read_data); + + if (!results) + { + break; + } + + total_data += read_data; + + response_buffer.resize(total_data); + } + } + + if (!results) + { +#ifndef NDEBUG + __debugbreak(); + auto err = GetLastError(); + std::cerr << "[DEBUG] failed to connect to server: " << err << "\n"; +#endif + } + + if (request) WinHttpCloseHandle(request); + if (connect) WinHttpCloseHandle(connect); + if (session) WinHttpCloseHandle(session); + } + + static fs::path get_bindir() + { + wchar_t buf[_MAX_PATH]; + const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH); + if (bytes == 0) std::abort(); + return fs::path(buf, buf + bytes); + } + + void Metrics::flush() + { + const std::string payload = g_metricmessage.format_event_data_template(); + if (g_should_print_metrics) std::cerr << payload << "\n"; + if (!g_should_send_metrics) return; + + // upload(payload); + + wchar_t temp_folder[MAX_PATH]; + GetTempPathW(MAX_PATH, temp_folder); + + const fs::path temp_folder_path = temp_folder; + const fs::path temp_folder_path_exe = temp_folder_path / "vcpkgmetricsuploader.exe"; + + auto& fs = Files::get_real_filesystem(); + + if (true) + { + const fs::path exe_path = [&fs]() -> fs::path { + auto vcpkgdir = get_bindir().parent_path(); + auto path = vcpkgdir / "vcpkgmetricsuploader.exe"; + if (fs.exists(path)) return path; + + path = vcpkgdir / "scripts" / "vcpkgmetricsuploader.exe"; + if (fs.exists(path)) return path; + + return Strings::WEMPTY; + }(); + + std::error_code ec; + fs.copy_file(exe_path, temp_folder_path_exe, fs::copy_options::skip_existing, ec); + if (ec) return; + } + + const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + generate_random_UUID() + ".txt"); + fs.write_contents(vcpkg_metrics_txt_path, payload); + + const std::wstring cmd_line = + Strings::wformat(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native()); + System::cmd_execute_clean(cmd_line); + } +} diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp new file mode 100644 index 000000000..01a09618d --- /dev/null +++ b/toolsrc/src/vcpkg/packagespec.cpp @@ -0,0 +1,175 @@ +#include "pch.h" + +#include +#include +#include + +using vcpkg::Parse::parse_comma_list; + +namespace vcpkg +{ + static bool is_valid_package_spec_char(char c) + { + return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)) || (c == '[') || (c == ']'); + } + + std::string FeatureSpec::to_string() const + { + if (feature().empty()) return spec().to_string(); + return Strings::format("%s[%s]:%s", name(), feature(), triplet()); + } + + std::vector FeatureSpec::from_strings_and_triplet(const std::vector& depends, + const Triplet& triplet) + { + std::vector f_specs; + for (auto&& depend : depends) + { + auto maybe_spec = ParsedSpecifier::from_string(depend); + if (auto spec = maybe_spec.get()) + { + Checks::check_exit(VCPKG_LINE_INFO, + spec->triplet.empty(), + "error: triplets cannot currently be specified in this context: %s", + depend); + PackageSpec pspec = + PackageSpec::from_name_and_triplet(spec->name, triplet).value_or_exit(VCPKG_LINE_INFO); + + for (auto&& feature : spec->features) + f_specs.push_back(FeatureSpec{pspec, feature}); + + if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, Strings::EMPTY}); + } + else + { + Checks::exit_with_message(VCPKG_LINE_INFO, + "error while parsing feature list: %s: %s", + vcpkg::to_string(maybe_spec.error()), + depend); + } + } + return f_specs; + } + + std::vector FullPackageSpec::to_feature_specs(const std::vector& specs) + { + std::vector ret; + for (auto&& spec : specs) + { + ret.emplace_back(spec.package_spec, Strings::EMPTY); + for (auto&& feature : spec.features) + ret.emplace_back(spec.package_spec, feature); + } + return ret; + } + + ExpectedT FullPackageSpec::from_string(const std::string& spec_as_string, + const Triplet& default_triplet) + { + auto res = ParsedSpecifier::from_string(spec_as_string); + if (auto p = res.get()) + { + FullPackageSpec fspec; + Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(p->triplet); + fspec.package_spec = PackageSpec::from_name_and_triplet(p->name, t).value_or_exit(VCPKG_LINE_INFO); + fspec.features = std::move(p->features); + return fspec; + } + return res.error(); + } + + ExpectedT PackageSpec::from_name_and_triplet(const std::string& name, + const Triplet& triplet) + { + if (Util::find_if_not(name, is_valid_package_spec_char) != name.end()) + { + return PackageSpecParseResult::INVALID_CHARACTERS; + } + + PackageSpec p; + p.m_name = name; + p.m_triplet = triplet; + return p; + } + + const std::string& PackageSpec::name() const { return this->m_name; } + + const Triplet& PackageSpec::triplet() const { return this->m_triplet; } + + std::string PackageSpec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_triplet); } + + std::string PackageSpec::to_string() const { return Strings::format("%s:%s", this->name(), this->triplet()); } + + bool operator==(const PackageSpec& left, const PackageSpec& right) + { + return left.name() == right.name() && left.triplet() == right.triplet(); + } + + bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); } + + ExpectedT ParsedSpecifier::from_string(const std::string& input) + { + auto pos = input.find(':'); + auto pos_l_bracket = input.find('['); + auto pos_r_bracket = input.find(']'); + + ParsedSpecifier f; + if (pos == std::string::npos && pos_l_bracket == std::string::npos) + { + f.name = input; + return f; + } + else if (pos == std::string::npos) + { + if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket) + { + return PackageSpecParseResult::INVALID_CHARACTERS; + } + const std::string name = input.substr(0, pos_l_bracket); + f.name = name; + f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1)); + return f; + } + else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos) + { + const std::string name = input.substr(0, pos); + f.triplet = input.substr(pos + 1); + f.name = name; + } + else + { + if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket) + { + return PackageSpecParseResult::INVALID_CHARACTERS; + } + const std::string name = input.substr(0, pos_l_bracket); + f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1)); + f.triplet = input.substr(pos + 1); + f.name = name; + } + + auto pos2 = input.find(':', pos + 1); + if (pos2 != std::string::npos) + { + return PackageSpecParseResult::TOO_MANY_COLONS; + } + return f; + } + + ExpectedT Features::from_string(const std::string& name) + { + auto maybe_spec = ParsedSpecifier::from_string(name); + if (auto spec = maybe_spec.get()) + { + Checks::check_exit( + VCPKG_LINE_INFO, spec->triplet.empty(), "error: triplet not allowed in specifier: %s", name); + + Features f; + f.name = spec->name; + f.features = spec->features; + return f; + } + + return maybe_spec.error(); + } +} diff --git a/toolsrc/src/vcpkg/packagespecparseresult.cpp b/toolsrc/src/vcpkg/packagespecparseresult.cpp new file mode 100644 index 000000000..b12bd12d0 --- /dev/null +++ b/toolsrc/src/vcpkg/packagespecparseresult.cpp @@ -0,0 +1,21 @@ +#include "pch.h" + +#include + +#include + +namespace vcpkg +{ + CStringView to_string(PackageSpecParseResult ev) noexcept + { + switch (ev) + { + case PackageSpecParseResult::SUCCESS: return "OK"; + case PackageSpecParseResult::TOO_MANY_COLONS: return "Too many colons"; + case PackageSpecParseResult::INVALID_CHARACTERS: + return "Contains invalid characters. Only alphanumeric lowercase ASCII characters and dashes are " + "allowed"; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } +} diff --git a/toolsrc/src/vcpkg/paragraphparseresult.cpp b/toolsrc/src/vcpkg/paragraphparseresult.cpp new file mode 100644 index 000000000..920a4b16b --- /dev/null +++ b/toolsrc/src/vcpkg/paragraphparseresult.cpp @@ -0,0 +1,34 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg +{ + const char* ParagraphParseResultCategoryImpl::name() const noexcept { return "ParagraphParseResult"; } + + std::string ParagraphParseResultCategoryImpl::message(int ev) const noexcept + { + switch (static_cast(ev)) + { + case ParagraphParseResult::SUCCESS: return "OK"; + case ParagraphParseResult::EXPECTED_ONE_PARAGRAPH: return "There should be exactly one paragraph"; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + const std::error_category& paragraph_parse_result_category() + { + static ParagraphParseResultCategoryImpl instance; + return instance; + } + + std::error_code make_error_code(ParagraphParseResult e) + { + return std::error_code(static_cast(e), paragraph_parse_result_category()); + } + + ParagraphParseResult to_paragraph_parse_result(int i) { return static_cast(i); } + + ParagraphParseResult to_paragraph_parse_result(std::error_code ec) { return to_paragraph_parse_result(ec.value()); } +} diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp new file mode 100644 index 000000000..b93de190c --- /dev/null +++ b/toolsrc/src/vcpkg/paragraphs.cpp @@ -0,0 +1,304 @@ +#include "pch.h" + +#include +#include +#include +#include +#include + +using namespace vcpkg::Parse; + +namespace vcpkg::Paragraphs +{ + struct Parser + { + Parser(const char* c, const char* e) : cur(c), end(e) {} + + private: + const char* cur; + const char* const end; + + void peek(char& ch) const + { + if (cur == end) + ch = 0; + else + ch = *cur; + } + + void next(char& ch) + { + if (cur == end) + ch = 0; + else + { + ++cur; + peek(ch); + } + } + + void skip_comment(char& ch) + { + while (ch != '\r' && ch != '\n' && ch != '\0') + next(ch); + if (ch == '\r') next(ch); + if (ch == '\n') next(ch); + } + + void skip_spaces(char& ch) + { + while (ch == ' ' || ch == '\t') + next(ch); + } + + static bool is_alphanum(char ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); + } + + static bool is_comment(char ch) { return (ch == '#'); } + + static bool is_lineend(char ch) { return ch == '\r' || ch == '\n' || ch == 0; } + + void get_fieldvalue(char& ch, std::string& fieldvalue) + { + fieldvalue.clear(); + + auto beginning_of_line = cur; + do + { + // scan to end of current line (it is part of the field value) + while (!is_lineend(ch)) + next(ch); + + fieldvalue.append(beginning_of_line, cur); + + if (ch == '\r') next(ch); + if (ch == '\n') next(ch); + + if (is_alphanum(ch) || is_comment(ch)) + { + // Line begins a new field. + return; + } + + beginning_of_line = cur; + + // Line may continue the current field with data or terminate the paragraph, + // depending on first nonspace character. + skip_spaces(ch); + + if (is_lineend(ch)) + { + // Line was whitespace or empty. + // This terminates the field and the paragraph. + // We leave the blank line's whitespace consumed, because it doesn't matter. + return; + } + + // First nonspace is not a newline. This continues the current field value. + // We forcibly convert all newlines into single '\n' for ease of text handling later on. + fieldvalue.push_back('\n'); + } while (true); + } + + void get_fieldname(char& ch, std::string& fieldname) + { + auto begin_fieldname = cur; + while (is_alphanum(ch) || ch == '-') + next(ch); + Checks::check_exit(VCPKG_LINE_INFO, ch == ':', "Expected ':'"); + fieldname = std::string(begin_fieldname, cur); + + // skip ': ' + next(ch); + skip_spaces(ch); + } + + void get_paragraph(char& ch, std::unordered_map& fields) + { + fields.clear(); + std::string fieldname; + std::string fieldvalue; + do + { + if (is_comment(ch)) + { + skip_comment(ch); + continue; + } + + get_fieldname(ch, fieldname); + + auto it = fields.find(fieldname); + Checks::check_exit(VCPKG_LINE_INFO, it == fields.end(), "Duplicate field"); + + get_fieldvalue(ch, fieldvalue); + + fields.emplace(fieldname, fieldvalue); + } while (!is_lineend(ch)); + } + + public: + std::vector> get_paragraphs() + { + std::vector> paragraphs; + + char ch; + peek(ch); + + while (ch != 0) + { + if (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t') + { + next(ch); + continue; + } + + paragraphs.emplace_back(); + get_paragraph(ch, paragraphs.back()); + } + + return paragraphs; + } + }; + + Expected> get_single_paragraph(const Files::Filesystem& fs, + const fs::path& control_path) + { + const Expected contents = fs.read_contents(control_path); + if (auto spgh = contents.get()) + { + return parse_single_paragraph(*spgh); + } + + return contents.error(); + } + + Expected>> get_paragraphs(const Files::Filesystem& fs, + const fs::path& control_path) + { + const Expected contents = fs.read_contents(control_path); + if (auto spgh = contents.get()) + { + return parse_paragraphs(*spgh); + } + + return contents.error(); + } + + Expected> parse_single_paragraph(const std::string& str) + { + const std::vector> p = + Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs(); + + if (p.size() == 1) + { + return p.at(0); + } + + return std::error_code(ParagraphParseResult::EXPECTED_ONE_PARAGRAPH); + } + + Expected>> parse_paragraphs(const std::string& str) + { + return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs(); + } + + ParseExpected try_load_port(const Files::Filesystem& fs, const fs::path& path) + { + Expected>> pghs = get_paragraphs(fs, path / "CONTROL"); + if (auto vector_pghs = pghs.get()) + { + auto csf = SourceControlFile::parse_control_file(std::move(*vector_pghs)); + if (!GlobalState::feature_packages) + { + if (auto ptr = csf.get()) + { + Checks::check_exit(VCPKG_LINE_INFO, ptr->get() != nullptr); + ptr->get()->core_paragraph->default_features.clear(); + ptr->get()->feature_paragraphs.clear(); + } + } + return csf; + } + auto error_info = std::make_unique(); + error_info->name = path.filename().generic_u8string(); + error_info->error = pghs.error(); + return error_info; + } + + Expected try_load_cached_control_package(const VcpkgPaths& paths, const PackageSpec& spec) + { + Expected>> pghs = + get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL"); + + if (auto p = pghs.get()) + { + BinaryControlFile bcf; + bcf.core_paragraph = BinaryParagraph(p->front()); + p->erase(p->begin()); + + bcf.features = + Util::fmap(*p, [&](auto&& raw_feature) -> BinaryParagraph { return BinaryParagraph(raw_feature); }); + + return bcf; + } + + return pghs.error(); + } + + LoadResults try_load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir) + { + LoadResults ret; + for (auto&& path : fs.get_files_non_recursive(ports_dir)) + { + auto maybe_spgh = try_load_port(fs, path); + if (const auto spgh = maybe_spgh.get()) + { + ret.paragraphs.emplace_back(std::move(*spgh)); + } + else + { + ret.errors.emplace_back(std::move(maybe_spgh).error()); + } + } + return ret; + } + + std::vector> load_all_ports(const Files::Filesystem& fs, + const fs::path& ports_dir) + { + auto results = try_load_all_ports(fs, ports_dir); + if (!results.errors.empty()) + { + if (GlobalState::debugging) + { + print_error_message(results.errors); + } + else + { + for (auto&& error : results.errors) + { + System::println( + System::Color::warning, "Warning: an error occurred while parsing '%s'", error->name); + } + System::println(System::Color::warning, + "Use '--debug' to get more information about the parse failures.\n"); + } + } + return std::move(results.paragraphs); + } + + std::map load_all_port_names_and_versions(const Files::Filesystem& fs, + const fs::path& ports_dir) + { + auto all_ports = load_all_ports(fs, ports_dir); + + std::map names_and_versions; + for (auto&& port : all_ports) + names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); + + return names_and_versions; + } +} diff --git a/toolsrc/src/vcpkg/parse.cpp b/toolsrc/src/vcpkg/parse.cpp new file mode 100644 index 000000000..116a7f5c8 --- /dev/null +++ b/toolsrc/src/vcpkg/parse.cpp @@ -0,0 +1,81 @@ +#include "pch.h" + +#include + +#include +#include + +namespace vcpkg::Parse +{ + static Optional remove_field(std::unordered_map* fields, + const std::string& fieldname) + { + auto it = fields->find(fieldname); + if (it == fields->end()) + { + return nullopt; + } + + const std::string value = std::move(it->second); + fields->erase(it); + return value; + } + + void ParagraphParser::required_field(const std::string& fieldname, std::string& out) + { + auto maybe_field = remove_field(&fields, fieldname); + if (const auto field = maybe_field.get()) + out = std::move(*field); + else + missing_fields.push_back(fieldname); + } + std::string ParagraphParser::optional_field(const std::string& fieldname) const + { + return remove_field(&fields, fieldname).value_or(Strings::EMPTY); + } + std::unique_ptr ParagraphParser::error_info(const std::string& name) const + { + if (!fields.empty() || !missing_fields.empty()) + { + auto err = std::make_unique(); + err->name = name; + err->extra_fields = Util::extract_keys(fields); + err->missing_fields = std::move(missing_fields); + return err; + } + return nullptr; + } + + std::vector parse_comma_list(const std::string& str) + { + if (str.empty()) + { + return {}; + } + + std::vector out; + + size_t cur = 0; + do + { + auto pos = str.find(',', cur); + if (pos == std::string::npos) + { + out.push_back(str.substr(cur)); + break; + } + out.push_back(str.substr(cur, pos - cur)); + + // skip comma and space + ++pos; + if (str[pos] == ' ') + { + ++pos; + } + + cur = pos; + } while (cur != std::string::npos); + + return out; + } +} diff --git a/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp b/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp new file mode 100644 index 000000000..2baaddb5e --- /dev/null +++ b/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp @@ -0,0 +1,70 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg::PostBuildLint +{ + BuildType BuildType::value_of(const ConfigurationType& config, const Build::LinkageType& linkage) + { + if (config == ConfigurationType::DEBUG && linkage == Build::LinkageType::STATIC) + { + return BuildTypeC::DEBUG_STATIC; + } + + if (config == ConfigurationType::DEBUG && linkage == Build::LinkageType::DYNAMIC) + { + return BuildTypeC::DEBUG_DYNAMIC; + } + + if (config == ConfigurationType::RELEASE && linkage == Build::LinkageType::STATIC) + { + return BuildTypeC::RELEASE_STATIC; + } + + if (config == ConfigurationType::RELEASE && linkage == Build::LinkageType::DYNAMIC) + { + return BuildTypeC::RELEASE_DYNAMIC; + } + + Checks::unreachable(VCPKG_LINE_INFO); + } + + const ConfigurationType& BuildType::config() const { return this->m_config; } + + const Build::LinkageType& BuildType::linkage() const { return this->m_linkage; } + + const std::regex& BuildType::crt_regex() const + { + static const std::regex REGEX_DEBUG_STATIC(R"(/DEFAULTLIB:LIBCMTD)", std::regex_constants::icase); + static const std::regex REGEX_DEBUG_DYNAMIC(R"(/DEFAULTLIB:MSVCRTD)", std::regex_constants::icase); + static const std::regex REGEX_RELEASE_STATIC(R"(/DEFAULTLIB:LIBCMT[^D])", std::regex_constants::icase); + static const std::regex REGEX_RELEASE_DYNAMIC(R"(/DEFAULTLIB:MSVCRT[^D])", std::regex_constants::icase); + + switch (backing_enum) + { + case BuildTypeC::DEBUG_STATIC: return REGEX_DEBUG_STATIC; + case BuildTypeC::DEBUG_DYNAMIC: return REGEX_DEBUG_DYNAMIC; + case BuildTypeC::RELEASE_STATIC: return REGEX_RELEASE_STATIC; + case BuildTypeC::RELEASE_DYNAMIC: return REGEX_RELEASE_DYNAMIC; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + const std::string& BuildType::to_string() const + { + static const std::string NAME_DEBUG_STATIC("Debug,Static"); + static const std::string NAME_DEBUG_DYNAMIC("Debug,Dynamic"); + static const std::string NAME_RELEASE_STATIC("Release,Static"); + static const std::string NAME_RELEASE_DYNAMIC("Release,Dynamic"); + + switch (backing_enum) + { + case BuildTypeC::DEBUG_STATIC: return NAME_DEBUG_STATIC; + case BuildTypeC::DEBUG_DYNAMIC: return NAME_DEBUG_DYNAMIC; + case BuildTypeC::RELEASE_STATIC: return NAME_RELEASE_STATIC; + case BuildTypeC::RELEASE_DYNAMIC: return NAME_RELEASE_DYNAMIC; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } +} diff --git a/toolsrc/src/vcpkg/postbuildlint.cpp b/toolsrc/src/vcpkg/postbuildlint.cpp new file mode 100644 index 000000000..5f55249df --- /dev/null +++ b/toolsrc/src/vcpkg/postbuildlint.cpp @@ -0,0 +1,846 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using vcpkg::Build::BuildInfo; +using vcpkg::Build::BuildPolicy; +using vcpkg::Build::PreBuildInfo; + +namespace vcpkg::PostBuildLint +{ + static auto has_extension_pred(const Files::Filesystem& fs, const std::string& ext) + { + return [&fs, ext](const fs::path& path) { return !fs.is_directory(path) && path.extension() == ext; }; + } + + enum class LintStatus + { + SUCCESS = 0, + ERROR_DETECTED = 1 + }; + + struct OutdatedDynamicCrt + { + std::string name; + std::regex regex; + + OutdatedDynamicCrt(const std::string& name, const std::string& regex_as_string) + : name(name), regex(std::regex(regex_as_string, std::regex_constants::icase)) + { + } + }; + + Span get_outdated_dynamic_crts(CStringView toolset) + { + static const std::vector V_NO_MSVCRT = { + {"msvcp100.dll", R"(msvcp100\.dll)"}, + {"msvcp100d.dll", R"(msvcp100d\.dll)"}, + {"msvcp110.dll", R"(msvcp110\.dll)"}, + {"msvcp110_win.dll", R"(msvcp110_win\.dll)"}, + {"msvcp120.dll", R"(msvcp120\.dll)"}, + {"msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"}, + {"msvcp60.dll", R"(msvcp60\.dll)"}, + {"msvcp60.dll", R"(msvcp60\.dll)"}, + + {"msvcr100.dll", R"(msvcr100\.dll)"}, + {"msvcr100d.dll", R"(msvcr100d\.dll)"}, + {"msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)"}, + {"msvcr110.dll", R"(msvcr110\.dll)"}, + {"msvcr120.dll", R"(msvcr120\.dll)"}, + {"msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"}, + {"msvcrt20.dll", R"(msvcrt20\.dll)"}, + {"msvcrt40.dll", R"(msvcrt40\.dll)"}}; + + return V_NO_MSVCRT; + } + + static LintStatus check_for_files_in_include_directory(const Files::Filesystem& fs, + const Build::BuildPolicies& policies, + const fs::path& package_dir) + { + if (policies.is_enabled(BuildPolicy::EMPTY_INCLUDE_FOLDER)) + { + return LintStatus::SUCCESS; + } + + const fs::path include_dir = package_dir / "include"; + if (!fs.exists(include_dir) || fs.is_empty(include_dir)) + { + System::println(System::Color::warning, + "The folder /include is empty. This indicates the library was not correctly installed."); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_for_files_in_debug_include_directory(const Files::Filesystem& fs, + const fs::path& package_dir) + { + const fs::path debug_include_dir = package_dir / "debug" / "include"; + + std::vector files_found = fs.get_files_recursive(debug_include_dir); + + Util::unstable_keep_if( + files_found, [&fs](const fs::path& path) { return !fs.is_directory(path) && path.extension() != ".ifc"; }); + + if (!files_found.empty()) + { + System::println(System::Color::warning, + "Include files should not be duplicated into the /debug/include directory. If this cannot " + "be disabled in the project cmake, use\n" + " file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)"); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_for_files_in_debug_share_directory(const Files::Filesystem& fs, const fs::path& package_dir) + { + const fs::path debug_share = package_dir / "debug" / "share"; + + if (fs.exists(debug_share)) + { + System::println(System::Color::warning, + "/debug/share should not exist. Please reorganize any important files, then use\n" + " file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share)"); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_folder_lib_cmake(const Files::Filesystem& fs, + const fs::path& package_dir, + const PackageSpec& spec) + { + const fs::path lib_cmake = package_dir / "lib" / "cmake"; + if (fs.exists(lib_cmake)) + { + System::println( + System::Color::warning, + "The /lib/cmake folder should be merged with /debug/lib/cmake and moved to /share/%s/cmake.", + spec.name()); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_for_misplaced_cmake_files(const Files::Filesystem& fs, + const fs::path& package_dir, + const PackageSpec& spec) + { + std::vector dirs = { + package_dir / "cmake", + package_dir / "debug" / "cmake", + package_dir / "lib" / "cmake", + package_dir / "debug" / "lib" / "cmake", + }; + + std::vector misplaced_cmake_files; + for (auto&& dir : dirs) + { + auto files = fs.get_files_recursive(dir); + for (auto&& file : files) + { + if (!fs.is_directory(file) && file.extension() == ".cmake") + misplaced_cmake_files.push_back(std::move(file)); + } + } + + if (!misplaced_cmake_files.empty()) + { + System::println( + System::Color::warning, + "The following cmake files were found outside /share/%s. Please place cmake files in /share/%s.", + spec.name(), + spec.name()); + Files::print_paths(misplaced_cmake_files); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_folder_debug_lib_cmake(const Files::Filesystem& fs, + const fs::path& package_dir, + const PackageSpec& spec) + { + const fs::path lib_cmake_debug = package_dir / "debug" / "lib" / "cmake"; + if (fs.exists(lib_cmake_debug)) + { + System::println(System::Color::warning, + "The /debug/lib/cmake folder should be merged with /lib/cmake into /share/%s", + spec.name()); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_for_dlls_in_lib_dir(const Files::Filesystem& fs, const fs::path& package_dir) + { + std::vector dlls = fs.get_files_recursive(package_dir / "lib"); + Util::unstable_keep_if(dlls, has_extension_pred(fs, ".dll")); + + if (!dlls.empty()) + { + System::println(System::Color::warning, + "\nThe following dlls were found in /lib or /debug/lib. Please move them to /bin or " + "/debug/bin, respectively."); + Files::print_paths(dlls); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_for_copyright_file(const Files::Filesystem& fs, + const PackageSpec& spec, + const VcpkgPaths& paths) + { + const fs::path packages_dir = paths.packages / spec.dir(); + const fs::path copyright_file = packages_dir / "share" / spec.name() / "copyright"; + if (fs.exists(copyright_file)) + { + return LintStatus::SUCCESS; + } + const fs::path current_buildtrees_dir = paths.buildtrees / spec.name(); + const fs::path current_buildtrees_dir_src = current_buildtrees_dir / "src"; + + std::vector potential_copyright_files; + // We only search in the root of each unpacked source archive to reduce false positives + auto src_dirs = fs.get_files_non_recursive(current_buildtrees_dir_src); + for (auto&& src_dir : src_dirs) + { + if (!fs.is_directory(src_dir)) continue; + + for (auto&& src_file : fs.get_files_non_recursive(src_dir)) + { + const std::string filename = src_file.filename().string(); + + if (filename == "LICENSE" || filename == "LICENSE.txt" || filename == "COPYING") + { + potential_copyright_files.push_back(src_file); + } + } + } + + System::println(System::Color::warning, + "The software license must be available at ${CURRENT_PACKAGES_DIR}/share/%s/copyright", + spec.name()); + if (potential_copyright_files.size() == + 1) // if there is only one candidate, provide the cmake lines needed to place it in the proper location + { + const fs::path found_file = potential_copyright_files[0]; + const fs::path relative_path = found_file.string().erase( + 0, current_buildtrees_dir.string().size() + 1); // The +1 is needed to remove the "/" + System::println( + "\n file(COPY ${CURRENT_BUILDTREES_DIR}/%s DESTINATION ${CURRENT_PACKAGES_DIR}/share/%s)\n" + " file(RENAME ${CURRENT_PACKAGES_DIR}/share/%s/%s ${CURRENT_PACKAGES_DIR}/share/%s/copyright)", + relative_path.generic_string(), + spec.name(), + spec.name(), + found_file.filename().generic_string(), + spec.name()); + } + else if (potential_copyright_files.size() > 1) + { + System::println(System::Color::warning, "The following files are potential copyright files:"); + Files::print_paths(potential_copyright_files); + } + return LintStatus::ERROR_DETECTED; + } + + static LintStatus check_for_exes(const Files::Filesystem& fs, const fs::path& package_dir) + { + std::vector exes = fs.get_files_recursive(package_dir / "bin"); + Util::unstable_keep_if(exes, has_extension_pred(fs, ".exe")); + + if (!exes.empty()) + { + System::println( + System::Color::warning, + "The following EXEs were found in /bin or /debug/bin. EXEs are not valid distribution targets."); + Files::print_paths(exes); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_exports_of_dlls(const std::vector& dlls, const fs::path& dumpbin_exe) + { + std::vector dlls_with_no_exports; + for (const fs::path& dll : dlls) + { + const std::wstring cmd_line = + Strings::wformat(LR"("%s" /exports "%s")", dumpbin_exe.native(), dll.native()); + System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, + ec_data.exit_code == 0, + "Running command:\n %s\n failed", + Strings::to_utf8(cmd_line)); + + if (ec_data.output.find("ordinal hint RVA name") == std::string::npos) + { + dlls_with_no_exports.push_back(dll); + } + } + + if (!dlls_with_no_exports.empty()) + { + System::println(System::Color::warning, "The following DLLs have no exports:"); + Files::print_paths(dlls_with_no_exports); + System::println(System::Color::warning, "DLLs without any exports are likely a bug in the build script."); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_uwp_bit_of_dlls(const std::string& expected_system_name, + const std::vector& dlls, + const fs::path dumpbin_exe) + { + if (expected_system_name != "WindowsStore") + { + return LintStatus::SUCCESS; + } + + std::vector dlls_with_improper_uwp_bit; + for (const fs::path& dll : dlls) + { + const std::wstring cmd_line = + Strings::wformat(LR"("%s" /headers "%s")", dumpbin_exe.native(), dll.native()); + System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, + ec_data.exit_code == 0, + "Running command:\n %s\n failed", + Strings::to_utf8(cmd_line)); + + if (ec_data.output.find("App Container") == std::string::npos) + { + dlls_with_improper_uwp_bit.push_back(dll); + } + } + + if (!dlls_with_improper_uwp_bit.empty()) + { + System::println(System::Color::warning, "The following DLLs do not have the App Container bit set:"); + Files::print_paths(dlls_with_improper_uwp_bit); + System::println(System::Color::warning, "This bit is required for Windows Store apps."); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + struct FileAndArch + { + fs::path file; + std::string actual_arch; + }; + + static std::string get_actual_architecture(const MachineType& machine_type) + { + switch (machine_type) + { + case MachineType::AMD64: + case MachineType::IA64: return "x64"; + case MachineType::I386: return "x86"; + case MachineType::ARM: + case MachineType::ARMNT: return "arm"; + default: return "Machine Type Code = " + std::to_string(static_cast(machine_type)); + } + } + + static void print_invalid_architecture_files(const std::string& expected_architecture, + std::vector binaries_with_invalid_architecture) + { + System::println(System::Color::warning, "The following files were built for an incorrect architecture:"); + System::println(); + for (const FileAndArch& b : binaries_with_invalid_architecture) + { + System::println(" %s", b.file.generic_string()); + System::println("Expected %s, but was: %s", expected_architecture, b.actual_arch); + System::println(); + } + } + + static LintStatus check_dll_architecture(const std::string& expected_architecture, + const std::vector& files) + { + std::vector binaries_with_invalid_architecture; + + for (const fs::path& file : files) + { + Checks::check_exit(VCPKG_LINE_INFO, + file.extension() == ".dll", + "The file extension was not .dll: %s", + file.generic_string()); + const CoffFileReader::DllInfo info = CoffFileReader::read_dll(file); + const std::string actual_architecture = get_actual_architecture(info.machine_type); + + if (expected_architecture != actual_architecture) + { + binaries_with_invalid_architecture.push_back({file, actual_architecture}); + } + } + + if (!binaries_with_invalid_architecture.empty()) + { + print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_lib_architecture(const std::string& expected_architecture, + const std::vector& files) + { + std::vector binaries_with_invalid_architecture; + + for (const fs::path& file : files) + { + Checks::check_exit(VCPKG_LINE_INFO, + file.extension() == ".lib", + "The file extension was not .lib: %s", + file.generic_string()); + CoffFileReader::LibInfo info = CoffFileReader::read_lib(file); + + // This is zero for folly's debug library + // TODO: Why? + if (info.machine_types.size() == 0) return LintStatus::SUCCESS; + + Checks::check_exit(VCPKG_LINE_INFO, + info.machine_types.size() == 1, + "Found more than 1 architecture in file %s", + file.generic_string()); + + const std::string actual_architecture = get_actual_architecture(info.machine_types.at(0)); + if (expected_architecture != actual_architecture) + { + binaries_with_invalid_architecture.push_back({file, actual_architecture}); + } + } + + if (!binaries_with_invalid_architecture.empty()) + { + print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_no_dlls_present(const std::vector& dlls) + { + if (dlls.empty()) + { + return LintStatus::SUCCESS; + } + + System::println(System::Color::warning, + "DLLs should not be present in a static build, but the following DLLs were found:"); + Files::print_paths(dlls); + return LintStatus::ERROR_DETECTED; + } + + static LintStatus check_matching_debug_and_release_binaries(const std::vector& debug_binaries, + const std::vector& release_binaries) + { + const size_t debug_count = debug_binaries.size(); + const size_t release_count = release_binaries.size(); + if (debug_count == release_count) + { + return LintStatus::SUCCESS; + } + + System::println(System::Color::warning, + "Mismatching number of debug and release binaries. Found %d for debug but %d for release.", + debug_count, + release_count); + System::println("Debug binaries"); + Files::print_paths(debug_binaries); + + System::println("Release binaries"); + Files::print_paths(release_binaries); + + if (debug_count == 0) + { + System::println(System::Color::warning, "Debug binaries were not found"); + } + if (release_count == 0) + { + System::println(System::Color::warning, "Release binaries were not found"); + } + + System::println(); + + return LintStatus::ERROR_DETECTED; + } + + static LintStatus check_lib_files_are_available_if_dlls_are_available(const Build::BuildPolicies& policies, + const size_t lib_count, + const size_t dll_count, + const fs::path& lib_dir) + { + if (policies.is_enabled(BuildPolicy::DLLS_WITHOUT_LIBS)) return LintStatus::SUCCESS; + + if (lib_count == 0 && dll_count != 0) + { + System::println(System::Color::warning, "Import libs were not present in %s", lib_dir.u8string()); + System::println(System::Color::warning, + "If this is intended, add the following line in the portfile:\n" + " SET(%s enabled)", + to_cmake_variable(BuildPolicy::DLLS_WITHOUT_LIBS)); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_bin_folders_are_not_present_in_static_build(const Files::Filesystem& fs, + const fs::path& package_dir) + { + const fs::path bin = package_dir / "bin"; + const fs::path debug_bin = package_dir / "debug" / "bin"; + + if (!fs.exists(bin) && !fs.exists(debug_bin)) + { + return LintStatus::SUCCESS; + } + + if (fs.exists(bin)) + { + System::println(System::Color::warning, + R"(There should be no bin\ directory in a static build, but %s is present.)", + bin.u8string()); + } + + if (fs.exists(debug_bin)) + { + System::println(System::Color::warning, + R"(There should be no debug\bin\ directory in a static build, but %s is present.)", + debug_bin.u8string()); + } + + System::println( + System::Color::warning, + R"(If the creation of bin\ and/or debug\bin\ cannot be disabled, use this in the portfile to remove them)" + "\n" + "\n" + R"###( if(VCPKG_LIBRARY_LINKAGE STREQUAL static))###" + "\n" + R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin))###" + "\n" + R"###( endif())###" + "\n"); + + return LintStatus::ERROR_DETECTED; + } + + static LintStatus check_no_empty_folders(const Files::Filesystem& fs, const fs::path& dir) + { + std::vector empty_directories = fs.get_files_recursive(dir); + + Util::unstable_keep_if(empty_directories, [&fs](const fs::path& current) { + return fs.is_directory(current) && fs.is_empty(current); + }); + + if (!empty_directories.empty()) + { + System::println(System::Color::warning, "There should be no empty directories in %s", dir.generic_string()); + System::println("The following empty directories were found: "); + Files::print_paths(empty_directories); + System::println( + System::Color::warning, + "If a directory should be populated but is not, this might indicate an error in the portfile.\n" + "If the directories are not needed and their creation cannot be disabled, use something like this in " + "the portfile to remove them:\n" + "\n" + R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/a/dir ${CURRENT_PACKAGES_DIR}/some/other/dir))###" + "\n" + "\n"); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + struct BuildTypeAndFile + { + fs::path file; + BuildType build_type; + }; + + static LintStatus check_crt_linkage_of_libs(const BuildType& expected_build_type, + const std::vector& libs, + const fs::path dumpbin_exe) + { + std::vector bad_build_types(BuildTypeC::VALUES.cbegin(), BuildTypeC::VALUES.cend()); + bad_build_types.erase(std::remove(bad_build_types.begin(), bad_build_types.end(), expected_build_type), + bad_build_types.end()); + + std::vector libs_with_invalid_crt; + + for (const fs::path& lib : libs) + { + const std::wstring cmd_line = + Strings::wformat(LR"("%s" /directives "%s")", dumpbin_exe.native(), lib.native()); + System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, + ec_data.exit_code == 0, + "Running command:\n %s\n failed", + Strings::to_utf8(cmd_line)); + + for (const BuildType& bad_build_type : bad_build_types) + { + if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), bad_build_type.crt_regex())) + { + libs_with_invalid_crt.push_back({lib, bad_build_type}); + break; + } + } + } + + if (!libs_with_invalid_crt.empty()) + { + System::println(System::Color::warning, + "Expected %s crt linkage, but the following libs had invalid crt linkage:", + expected_build_type.to_string()); + System::println(); + for (const BuildTypeAndFile btf : libs_with_invalid_crt) + { + System::println(" %s: %s", btf.file.generic_string(), btf.build_type.to_string()); + } + System::println(); + + System::println(System::Color::warning, + "To inspect the lib files, use:\n dumpbin.exe /directives mylibfile.lib"); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + struct OutdatedDynamicCrtAndFile + { + fs::path file; + OutdatedDynamicCrt outdated_crt; + + OutdatedDynamicCrtAndFile() = delete; + }; + + static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector& dlls, + const fs::path dumpbin_exe, + const BuildInfo& build_info) + { + if (build_info.policies.is_enabled(BuildPolicy::ALLOW_OBSOLETE_MSVCRT)) return LintStatus::SUCCESS; + + std::vector dlls_with_outdated_crt; + + for (const fs::path& dll : dlls) + { + const std::wstring cmd_line = + Strings::wformat(LR"("%s" /dependents "%s")", dumpbin_exe.native(), dll.native()); + System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); + Checks::check_exit(VCPKG_LINE_INFO, + ec_data.exit_code == 0, + "Running command:\n %s\n failed", + Strings::to_utf8(cmd_line)); + + for (const OutdatedDynamicCrt& outdated_crt : get_outdated_dynamic_crts("v141")) + { + if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), outdated_crt.regex)) + { + dlls_with_outdated_crt.push_back({dll, outdated_crt}); + break; + } + } + } + + if (!dlls_with_outdated_crt.empty()) + { + System::println(System::Color::warning, "Detected outdated dynamic CRT in the following files:"); + System::println(); + for (const OutdatedDynamicCrtAndFile btf : dlls_with_outdated_crt) + { + System::println(" %s: %s", btf.file.generic_string(), btf.outdated_crt.name); + } + System::println(); + + System::println(System::Color::warning, + "To inspect the dll files, use:\n dumpbin.exe /dependents mydllfile.dll"); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static LintStatus check_no_files_in_dir(const Files::Filesystem& fs, const fs::path& dir) + { + std::vector misplaced_files = fs.get_files_non_recursive(dir); + Util::unstable_keep_if(misplaced_files, [&fs](const fs::path& path) { + const std::string filename = path.filename().generic_string(); + if (Strings::case_insensitive_ascii_compare(filename.c_str(), "CONTROL") || + Strings::case_insensitive_ascii_compare(filename.c_str(), "BUILD_INFO")) + return false; + return !fs.is_directory(path); + }); + + if (!misplaced_files.empty()) + { + System::println(System::Color::warning, "The following files are placed in\n%s: ", dir.u8string()); + Files::print_paths(misplaced_files); + System::println(System::Color::warning, "Files cannot be present in those directories.\n"); + return LintStatus::ERROR_DETECTED; + } + + return LintStatus::SUCCESS; + } + + static void operator+=(size_t& left, const LintStatus& right) { left += static_cast(right); } + + static size_t perform_all_checks_and_return_error_count(const PackageSpec& spec, + const VcpkgPaths& paths, + const PreBuildInfo& pre_build_info, + const BuildInfo& build_info) + { + const auto& fs = paths.get_filesystem(); + + // for dumpbin + const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); + const fs::path package_dir = paths.package_dir(spec); + + size_t error_count = 0; + + if (build_info.policies.is_enabled(BuildPolicy::EMPTY_PACKAGE)) + { + return error_count; + } + + error_count += check_for_files_in_include_directory(fs, build_info.policies, package_dir); + error_count += check_for_files_in_debug_include_directory(fs, package_dir); + error_count += check_for_files_in_debug_share_directory(fs, package_dir); + error_count += check_folder_lib_cmake(fs, package_dir, spec); + error_count += check_for_misplaced_cmake_files(fs, package_dir, spec); + error_count += check_folder_debug_lib_cmake(fs, package_dir, spec); + error_count += check_for_dlls_in_lib_dir(fs, package_dir); + error_count += check_for_dlls_in_lib_dir(fs, package_dir / "debug"); + error_count += check_for_copyright_file(fs, spec, paths); + error_count += check_for_exes(fs, package_dir); + error_count += check_for_exes(fs, package_dir / "debug"); + + const fs::path debug_lib_dir = package_dir / "debug" / "lib"; + const fs::path release_lib_dir = package_dir / "lib"; + const fs::path debug_bin_dir = package_dir / "debug" / "bin"; + const fs::path release_bin_dir = package_dir / "bin"; + + std::vector debug_libs = fs.get_files_recursive(debug_lib_dir); + Util::unstable_keep_if(debug_libs, has_extension_pred(fs, ".lib")); + std::vector release_libs = fs.get_files_recursive(release_lib_dir); + Util::unstable_keep_if(release_libs, has_extension_pred(fs, ".lib")); + + error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs); + + { + std::vector libs; + libs.insert(libs.cend(), debug_libs.cbegin(), debug_libs.cend()); + libs.insert(libs.cend(), release_libs.cbegin(), release_libs.cend()); + + error_count += check_lib_architecture(pre_build_info.target_architecture, libs); + } + + std::vector debug_dlls = fs.get_files_recursive(debug_bin_dir); + Util::unstable_keep_if(debug_dlls, has_extension_pred(fs, ".dll")); + std::vector release_dlls = fs.get_files_recursive(release_bin_dir); + Util::unstable_keep_if(release_dlls, has_extension_pred(fs, ".dll")); + + switch (build_info.library_linkage) + { + case Build::LinkageType::DYNAMIC: + { + error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); + + error_count += check_lib_files_are_available_if_dlls_are_available( + build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir); + error_count += check_lib_files_are_available_if_dlls_are_available( + build_info.policies, release_libs.size(), release_dlls.size(), release_lib_dir); + + std::vector dlls; + dlls.insert(dlls.cend(), debug_dlls.cbegin(), debug_dlls.cend()); + dlls.insert(dlls.cend(), release_dlls.cbegin(), release_dlls.cend()); + + error_count += check_exports_of_dlls(dlls, toolset.dumpbin); + error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls, toolset.dumpbin); + error_count += check_dll_architecture(pre_build_info.target_architecture, dlls); + + error_count += check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin, build_info); + break; + } + case Build::LinkageType::STATIC: + { + auto dlls = release_dlls; + dlls.insert(dlls.end(), debug_dlls.begin(), debug_dlls.end()); + error_count += check_no_dlls_present(dlls); + + error_count += check_bin_folders_are_not_present_in_static_build(fs, package_dir); + + if (!build_info.policies.is_enabled(BuildPolicy::ONLY_RELEASE_CRT)) + { + error_count += + check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::DEBUG, build_info.crt_linkage), + debug_libs, + toolset.dumpbin); + } + error_count += + check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::RELEASE, build_info.crt_linkage), + release_libs, + toolset.dumpbin); + break; + } + default: Checks::unreachable(VCPKG_LINE_INFO); + } + + error_count += check_no_empty_folders(fs, package_dir); + error_count += check_no_files_in_dir(fs, package_dir); + error_count += check_no_files_in_dir(fs, package_dir / "debug"); + + return error_count; + } + + size_t perform_all_checks(const PackageSpec& spec, + const VcpkgPaths& paths, + const PreBuildInfo& pre_build_info, + const BuildInfo& build_info) + { + System::println("-- Performing post-build validation"); + const size_t error_count = perform_all_checks_and_return_error_count(spec, paths, pre_build_info, build_info); + + if (error_count != 0) + { + const fs::path portfile = paths.ports / spec.name() / "portfile.cmake"; + System::println(System::Color::error, + "Found %u error(s). Please correct the portfile:\n %s", + error_count, + portfile.string()); + } + + System::println("-- Performing post-build validation done"); + + return error_count; + } +} diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp new file mode 100644 index 000000000..3751566f7 --- /dev/null +++ b/toolsrc/src/vcpkg/remove.cpp @@ -0,0 +1,251 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Remove +{ + using Dependencies::RemovePlanAction; + using Dependencies::RemovePlanType; + using Dependencies::RequestType; + using Update::OutdatedPackage; + + void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db) + { + auto& fs = paths.get_filesystem(); + auto spghs = status_db->find_all(spec.name(), spec.triplet()); + const auto core_pkg = **status_db->find(spec.name(), spec.triplet(), Strings::EMPTY); + + for (auto&& spgh : spghs) + { + StatusParagraph& pkg = **spgh; + if (pkg.state != InstallState::INSTALLED) continue; + pkg.want = Want::PURGE; + pkg.state = InstallState::HALF_INSTALLED; + write_update(paths, pkg); + } + + auto maybe_lines = fs.read_lines(paths.listfile_path(core_pkg.package)); + + if (const auto lines = maybe_lines.get()) + { + std::vector dirs_touched; + for (auto&& suffix : *lines) + { + if (!suffix.empty() && suffix.back() == '\r') suffix.pop_back(); + + std::error_code ec; + + auto target = paths.installed / suffix; + + const auto status = fs.status(target, ec); + if (ec) + { + System::println(System::Color::error, "failed: %s", ec.message()); + continue; + } + + if (fs::is_directory(status)) + { + dirs_touched.push_back(target); + } + else if (fs::is_regular_file(status)) + { + fs.remove(target, ec); + if (ec) + { + System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message()); + } + } + else if (!fs::status_known(status)) + { + System::println(System::Color::warning, "Warning: unknown status: %s", target.u8string()); + } + else + { + System::println(System::Color::warning, "Warning: %s: cannot handle file type", target.u8string()); + } + } + + auto b = dirs_touched.rbegin(); + const auto e = dirs_touched.rend(); + for (; b != e; ++b) + { + if (fs.is_empty(*b)) + { + std::error_code ec; + fs.remove(*b, ec); + if (ec) + { + System::println(System::Color::error, "failed: %s", ec.message()); + } + } + } + + fs.remove(paths.listfile_path(core_pkg.package)); + } + + for (auto&& spgh : spghs) + { + StatusParagraph& pkg = **spgh; + if (pkg.state != InstallState::HALF_INSTALLED) continue; + pkg.state = InstallState::NOT_INSTALLED; + write_update(paths, pkg); + } + } + + static void print_plan(const std::map>& group_by_plan_type) + { + static constexpr std::array ORDER = {RemovePlanType::NOT_INSTALLED, RemovePlanType::REMOVE}; + + for (const RemovePlanType plan_type : ORDER) + { + const auto it = group_by_plan_type.find(plan_type); + if (it == group_by_plan_type.cend()) + { + continue; + } + + std::vector cont = it->second; + std::sort(cont.begin(), cont.end(), &RemovePlanAction::compare_by_name); + const std::string as_string = Strings::join("\n", cont, [](const RemovePlanAction* p) { + return Dependencies::to_output_string(p->request_type, p->spec.to_string()); + }); + + switch (plan_type) + { + case RemovePlanType::NOT_INSTALLED: + System::println("The following packages are not installed, so not removed:\n%s", as_string); + continue; + case RemovePlanType::REMOVE: + System::println("The following packages will be removed:\n%s", as_string); + continue; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + } + + void perform_remove_plan_action(const VcpkgPaths& paths, + const RemovePlanAction& action, + const Purge purge, + StatusParagraphs& status_db) + { + const std::string display_name = action.spec.to_string(); + + switch (action.plan_type) + { + case RemovePlanType::NOT_INSTALLED: + System::println(System::Color::success, "Package %s is not installed", display_name); + break; + case RemovePlanType::REMOVE: + System::println("Removing package %s... ", display_name); + remove_package(paths, action.spec, &status_db); + System::println(System::Color::success, "Removing package %s... done", display_name); + break; + case RemovePlanType::UNKNOWN: + default: Checks::unreachable(VCPKG_LINE_INFO); + } + + if (purge == Purge::YES) + { + System::println("Purging package %s... ", display_name); + Files::Filesystem& fs = paths.get_filesystem(); + std::error_code ec; + fs.remove_all(paths.packages / action.spec.dir(), ec); + System::println(System::Color::success, "Purging package %s... done", display_name); + } + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + static const std::string OPTION_PURGE = "--purge"; + static const std::string OPTION_NO_PURGE = "--no-purge"; + static const std::string OPTION_RECURSE = "--recurse"; + static const std::string OPTION_DRY_RUN = "--dry-run"; + static const std::string OPTION_OUTDATED = "--outdated"; + static const std::string EXAMPLE = Help::create_example_string("remove zlib zlib:x64-windows curl boost"); + const std::unordered_set options = args.check_and_get_optional_command_arguments( + {OPTION_PURGE, OPTION_NO_PURGE, OPTION_RECURSE, OPTION_DRY_RUN, OPTION_OUTDATED}); + + StatusParagraphs status_db = database_load_check(paths); + std::vector specs; + if (options.find(OPTION_OUTDATED) != options.cend()) + { + args.check_exact_arg_count(0, EXAMPLE); + specs = Util::fmap(Update::find_outdated_packages(paths, status_db), + [](auto&& outdated) { return outdated.spec; }); + + if (specs.empty()) + { + System::println(System::Color::success, "There are no outdated packages."); + Checks::exit_success(VCPKG_LINE_INFO); + } + } + else + { + args.check_min_arg_count(1, EXAMPLE); + specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE); + }); + + for (auto&& spec : specs) + Input::check_triplet(spec.triplet(), paths); + } + + const bool no_purge_was_passed = options.find(OPTION_NO_PURGE) != options.end(); + const bool purge_was_passed = options.find(OPTION_PURGE) != options.end(); + if (purge_was_passed && no_purge_was_passed) + { + System::println(System::Color::error, "Error: cannot specify both --no-purge and --purge."); + System::print(EXAMPLE); + Checks::exit_fail(VCPKG_LINE_INFO); + } + const Purge purge = to_purge(purge_was_passed || !no_purge_was_passed); + const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); + const bool dry_run = options.find(OPTION_DRY_RUN) != options.cend(); + + const std::vector remove_plan = Dependencies::create_remove_plan(specs, status_db); + Checks::check_exit(VCPKG_LINE_INFO, !remove_plan.empty(), "Remove plan cannot be empty"); + + std::map> group_by_plan_type; + Util::group_by(remove_plan, &group_by_plan_type, [](const RemovePlanAction& p) { return p.plan_type; }); + print_plan(group_by_plan_type); + + const bool has_non_user_requested_packages = + Util::find_if(remove_plan, [](const RemovePlanAction& package) -> bool { + return package.request_type != RequestType::USER_REQUESTED; + }) != remove_plan.cend(); + + if (has_non_user_requested_packages) + { + System::println(System::Color::warning, + "Additional packages (*) need to be removed to complete this operation."); + + if (!is_recursive) + { + System::println(System::Color::warning, + "If you are sure you want to remove them, run the command with the --recurse option"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } + + if (dry_run) + { + Checks::exit_success(VCPKG_LINE_INFO); + } + + for (const RemovePlanAction& action : remove_plan) + { + perform_remove_plan_action(paths, action, purge, status_db); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp new file mode 100644 index 000000000..d766dcb72 --- /dev/null +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -0,0 +1,264 @@ +#include "pch.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace vcpkg +{ + using namespace vcpkg::Parse; + + namespace SourceParagraphFields + { + static const std::string BUILD_DEPENDS = "Build-Depends"; + static const std::string DEFAULTFEATURES = "Default-Features"; + static const std::string DESCRIPTION = "Description"; + static const std::string FEATURE = "Feature"; + static const std::string MAINTAINER = "Maintainer"; + static const std::string SOURCE = "Source"; + static const std::string SUPPORTS = "Supports"; + static const std::string VERSION = "Version"; + } + + static Span get_list_of_valid_fields() + { + static const std::string valid_fields[] = { + SourceParagraphFields::SOURCE, + SourceParagraphFields::VERSION, + SourceParagraphFields::DESCRIPTION, + SourceParagraphFields::MAINTAINER, + SourceParagraphFields::BUILD_DEPENDS, + }; + + return valid_fields; + } + + void print_error_message(Span> error_info_list) + { + Checks::check_exit(VCPKG_LINE_INFO, error_info_list.size() > 0); + + for (auto&& error_info : error_info_list) + { + Checks::check_exit(VCPKG_LINE_INFO, error_info != nullptr); + if (error_info->error) + { + System::println( + System::Color::error, "Error: while loading %s: %s", error_info->name, error_info->error.message()); + } + } + + bool have_remaining_fields = false; + for (auto&& error_info : error_info_list) + { + if (!error_info->extra_fields.empty()) + { + System::println(System::Color::error, + "Error: There are invalid fields in the control file of %s", + error_info->name); + System::println("The following fields were not expected:\n\n %s\n", + Strings::join("\n ", error_info->extra_fields)); + have_remaining_fields = true; + } + } + + if (have_remaining_fields) + { + System::println("This is the list of valid fields (case-sensitive): \n\n %s\n", + Strings::join("\n ", get_list_of_valid_fields())); + System::println("Different source may be available for vcpkg. Use .\\bootstrap-vcpkg.bat to update.\n"); + } + + for (auto&& error_info : error_info_list) + { + if (!error_info->missing_fields.empty()) + { + System::println(System::Color::error, + "Error: There are missing fields in the control file of %s", + error_info->name); + System::println("The following fields were missing:\n\n %s\n", + Strings::join("\n ", error_info->missing_fields)); + } + } + } + + static ParseExpected parse_source_paragraph(RawParagraph&& fields) + { + ParagraphParser parser(std::move(fields)); + + auto spgh = std::make_unique(); + + parser.required_field(SourceParagraphFields::SOURCE, spgh->name); + parser.required_field(SourceParagraphFields::VERSION, spgh->version); + + spgh->description = parser.optional_field(SourceParagraphFields::DESCRIPTION); + spgh->maintainer = parser.optional_field(SourceParagraphFields::MAINTAINER); + spgh->depends = expand_qualified_dependencies( + parse_comma_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS))); + spgh->supports = parse_comma_list(parser.optional_field(SourceParagraphFields::SUPPORTS)); + spgh->default_features = parse_comma_list(parser.optional_field(SourceParagraphFields::DEFAULTFEATURES)); + + auto err = parser.error_info(spgh->name); + if (err) + return std::move(err); + else + return std::move(spgh); + } + + static ParseExpected parse_feature_paragraph(RawParagraph&& fields) + { + ParagraphParser parser(std::move(fields)); + + auto fpgh = std::make_unique(); + + parser.required_field(SourceParagraphFields::FEATURE, fpgh->name); + parser.required_field(SourceParagraphFields::DESCRIPTION, fpgh->description); + + fpgh->depends = expand_qualified_dependencies( + parse_comma_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS))); + + auto err = parser.error_info(fpgh->name); + if (err) + return std::move(err); + else + return std::move(fpgh); + } + + ParseExpected SourceControlFile::parse_control_file( + std::vector>&& control_paragraphs) + { + if (control_paragraphs.size() == 0) + { + return std::make_unique(); + } + + auto control_file = std::make_unique(); + + auto maybe_source = parse_source_paragraph(std::move(control_paragraphs.front())); + if (const auto source = maybe_source.get()) + control_file->core_paragraph = std::move(*source); + else + return std::move(maybe_source).error(); + + control_paragraphs.erase(control_paragraphs.begin()); + + for (auto&& feature_pgh : control_paragraphs) + { + auto maybe_feature = parse_feature_paragraph(std::move(feature_pgh)); + if (const auto feature = maybe_feature.get()) + control_file->feature_paragraphs.emplace_back(std::move(*feature)); + else + return std::move(maybe_feature).error(); + } + + return std::move(control_file); + } + + Dependency Dependency::parse_dependency(std::string name, std::string qualifier) + { + Dependency dep; + dep.qualifier = qualifier; + if (auto maybe_features = Features::from_string(name)) + dep.depend = *maybe_features.get(); + else + Checks::exit_with_message( + VCPKG_LINE_INFO, "error while parsing dependency: %s: %s", to_string(maybe_features.error()), name); + return dep; + } + + std::string Dependency::name() const + { + if (this->depend.features.empty()) return this->depend.name; + + const std::string features = Strings::join(",", this->depend.features); + return Strings::format("%s[%s]", this->depend.name, features); + } + + std::vector vcpkg::expand_qualified_dependencies(const std::vector& depends) + { + return Util::fmap(depends, [&](const std::string& depend_string) -> Dependency { + auto pos = depend_string.find(' '); + if (pos == std::string::npos) return Dependency::parse_dependency(depend_string, Strings::EMPTY); + // expect of the form "\w+ \[\w+\]" + Dependency dep; + + dep.depend.name = depend_string.substr(0, pos); + if (depend_string.c_str()[pos + 1] != '(' || depend_string[depend_string.size() - 1] != ')') + { + // Error, but for now just slurp the entire string. + return Dependency::parse_dependency(depend_string, Strings::EMPTY); + } + dep.qualifier = depend_string.substr(pos + 2, depend_string.size() - pos - 3); + return dep; + }); + } + + std::vector filter_dependencies(const std::vector& deps, const Triplet& t) + { + std::vector ret; + for (auto&& dep : deps) + { + if (dep.qualifier.empty() || t.canonical_name().find(dep.qualifier) != std::string::npos) + { + ret.emplace_back(dep.name()); + } + } + return ret; + } + + std::vector filter_dependencies_to_specs(const std::vector& deps, const Triplet& t) + { + return FeatureSpec::from_strings_and_triplet(filter_dependencies(deps, t), t); + } + + std::string to_string(const Dependency& dep) { return dep.name(); } + + ExpectedT> Supports::parse(const std::vector& strs) + { + Supports ret; + std::vector unrecognized; + + for (auto&& str : strs) + { + if (str == "x64") + ret.architectures.push_back(Architecture::X64); + else if (str == "x86") + ret.architectures.push_back(Architecture::X86); + else if (str == "arm") + ret.architectures.push_back(Architecture::ARM); + else if (str == "windows") + ret.platforms.push_back(Platform::WINDOWS); + else if (str == "uwp") + ret.platforms.push_back(Platform::UWP); + else if (str == "v140") + ret.toolsets.push_back(ToolsetVersion::V140); + else if (str == "v141") + ret.toolsets.push_back(ToolsetVersion::V141); + else if (str == "crt-static") + ret.crt_linkages.push_back(Linkage::STATIC); + else if (str == "crt-dynamic") + ret.crt_linkages.push_back(Linkage::DYNAMIC); + else + unrecognized.push_back(str); + } + + if (unrecognized.empty()) + return std::move(ret); + else + return std::move(unrecognized); + } + + bool Supports::is_supported(Architecture arch, Platform plat, Linkage crt, ToolsetVersion tools) + { + const auto is_in_or_empty = [](auto v, auto&& c) -> bool { return c.empty() || c.end() != Util::find(c, v); }; + if (!is_in_or_empty(arch, architectures)) return false; + if (!is_in_or_empty(plat, platforms)) return false; + if (!is_in_or_empty(crt, crt_linkages)) return false; + if (!is_in_or_empty(tools, toolsets)) return false; + return true; + } +} diff --git a/toolsrc/src/vcpkg/statusparagraph.cpp b/toolsrc/src/vcpkg/statusparagraph.cpp new file mode 100644 index 000000000..5f00825e1 --- /dev/null +++ b/toolsrc/src/vcpkg/statusparagraph.cpp @@ -0,0 +1,86 @@ +#include "pch.h" + +#include + +using namespace vcpkg::Parse; + +namespace vcpkg +{ + namespace BinaryParagraphRequiredField + { + static const std::string STATUS = "Status"; + } + + StatusParagraph::StatusParagraph() : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) {} + + void serialize(const StatusParagraph& pgh, std::string& out_str) + { + serialize(pgh.package, out_str); + out_str.append("Status: ") + .append(to_string(pgh.want)) + .append(" ok ") + .append(to_string(pgh.state)) + .push_back('\n'); + } + + StatusParagraph::StatusParagraph(std::unordered_map&& fields) + { + auto status_it = fields.find(BinaryParagraphRequiredField::STATUS); + Checks::check_exit(VCPKG_LINE_INFO, status_it != fields.end(), "Expected 'Status' field in status paragraph"); + std::string status_field = std::move(status_it->second); + fields.erase(status_it); + + this->package = BinaryParagraph(std::move(fields)); + + auto b = status_field.begin(); + const auto mark = b; + const auto e = status_field.end(); + + // Todo: improve error handling + while (b != e && *b != ' ') + ++b; + + want = [](const std::string& text) { + if (text == "unknown") return Want::UNKNOWN; + if (text == "install") return Want::INSTALL; + if (text == "hold") return Want::HOLD; + if (text == "deinstall") return Want::DEINSTALL; + if (text == "purge") return Want::PURGE; + return Want::ERROR_STATE; + }(std::string(mark, b)); + + if (std::distance(b, e) < 4) return; + b += 4; + + state = [](const std::string& text) { + if (text == "not-installed") return InstallState::NOT_INSTALLED; + if (text == "installed") return InstallState::INSTALLED; + if (text == "half-installed") return InstallState::HALF_INSTALLED; + return InstallState::ERROR_STATE; + }(std::string(b, e)); + } + + std::string to_string(InstallState f) + { + switch (f) + { + case InstallState::HALF_INSTALLED: return "half-installed"; + case InstallState::INSTALLED: return "installed"; + case InstallState::NOT_INSTALLED: return "not-installed"; + default: return "error"; + } + } + + std::string to_string(Want f) + { + switch (f) + { + case Want::DEINSTALL: return "deinstall"; + case Want::HOLD: return "hold"; + case Want::INSTALL: return "install"; + case Want::PURGE: return "purge"; + case Want::UNKNOWN: return "unknown"; + default: return "error"; + } + } +} diff --git a/toolsrc/src/vcpkg/statusparagraphs.cpp b/toolsrc/src/vcpkg/statusparagraphs.cpp new file mode 100644 index 000000000..647ed6d3f --- /dev/null +++ b/toolsrc/src/vcpkg/statusparagraphs.cpp @@ -0,0 +1,89 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg +{ + StatusParagraphs::StatusParagraphs() = default; + + StatusParagraphs::StatusParagraphs(std::vector>&& ps) + : paragraphs(std::move(ps)){}; + + StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) const + { + return std::find_if(begin(), end(), [&](const std::unique_ptr& pgh) { + const PackageSpec& spec = pgh->package.spec; + return spec.name() == name && spec.triplet() == triplet; + }); + } + + StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) + { + return std::find_if(begin(), end(), [&](const std::unique_ptr& pgh) { + const PackageSpec& spec = pgh->package.spec; + return spec.name() == name && spec.triplet() == triplet; + }); + } + + std::vector*> StatusParagraphs::find_all(const std::string& name, + const Triplet& triplet) + { + std::vector*> spghs; + for (auto&& p : *this) + { + if (p->package.spec.name() == name && p->package.spec.triplet() == triplet) + { + spghs.emplace_back(&p); + } + } + return spghs; + } + + StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, + const Triplet& triplet, + const std::string& feature) + { + return std::find_if(begin(), end(), [&](const std::unique_ptr& pgh) { + const PackageSpec& spec = pgh->package.spec; + return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature; + }); + } + + StatusParagraphs::const_iterator StatusParagraphs::find_installed(const std::string& name, + const Triplet& triplet) const + { + const const_iterator it = find(name, triplet); + if (it != end() && (*it)->want == Want::INSTALL && (*it)->state == InstallState::INSTALLED) + { + return it; + } + + return end(); + } + + StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr pgh) + { + Checks::check_exit(VCPKG_LINE_INFO, pgh != nullptr, "Inserted null paragraph"); + const PackageSpec& spec = pgh->package.spec; + const auto ptr = find(spec.name(), spec.triplet(), pgh->package.feature); + if (ptr == end()) + { + paragraphs.push_back(std::move(pgh)); + return paragraphs.rbegin(); + } + + // consume data from provided pgh. + **ptr = std::move(*pgh); + return ptr; + } + + void serialize(const StatusParagraphs& pghs, std::string& out_str) + { + for (auto& pgh : pghs.paragraphs) + { + serialize(*pgh, out_str); + out_str.push_back('\n'); + } + } +} diff --git a/toolsrc/src/vcpkg/triplet.cpp b/toolsrc/src/vcpkg/triplet.cpp new file mode 100644 index 000000000..8a84ee2b2 --- /dev/null +++ b/toolsrc/src/vcpkg/triplet.cpp @@ -0,0 +1,55 @@ +#include "pch.h" + +#include +#include +#include + +namespace vcpkg +{ + struct TripletInstance + { + TripletInstance(std::string&& s) : value(std::move(s)), hash(std::hash()(value)) {} + + const std::string value; + const size_t hash = 0; + + bool operator==(const TripletInstance& o) const { return o.value == value; } + }; + const TripletInstance Triplet::DEFAULT_INSTANCE({}); +} + +template<> +struct std::hash +{ + size_t operator()(const vcpkg::TripletInstance& t) const { return t.hash; } +}; + +namespace vcpkg +{ + static std::unordered_set g_triplet_instances; + + const Triplet Triplet::X86_WINDOWS = from_canonical_name("x86-windows"); + const Triplet Triplet::X64_WINDOWS = from_canonical_name("x64-windows"); + const Triplet Triplet::X86_UWP = from_canonical_name("x86-uwp"); + const Triplet Triplet::X64_UWP = from_canonical_name("x64-uwp"); + const Triplet Triplet::ARM_UWP = from_canonical_name("arm-uwp"); + + bool Triplet::operator==(const Triplet& other) const { return this->m_instance == other.m_instance; } + + bool operator!=(const Triplet& left, const Triplet& right) { return !(left == right); } + + Triplet Triplet::from_canonical_name(const std::string& triplet_as_string) + { + std::string s(Strings::ascii_to_lowercase(triplet_as_string)); + const auto it = std::find(s.cbegin(), s.cend(), '-'); + Checks::check_exit(VCPKG_LINE_INFO, it != s.cend(), "Invalid triplet: %s", triplet_as_string); + + const auto p = g_triplet_instances.emplace(std::move(s)); + return &*p.first; + } + + const std::string& Triplet::canonical_name() const { return this->m_instance->value; } + + const std::string& Triplet::to_string() const { return this->canonical_name(); } + size_t Triplet::hash_code() const { return m_instance->hash; } +} diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp new file mode 100644 index 000000000..168949bc1 --- /dev/null +++ b/toolsrc/src/vcpkg/update.cpp @@ -0,0 +1,75 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Update +{ + bool OutdatedPackage::compare_by_name(const OutdatedPackage& left, const OutdatedPackage& right) + { + return left.spec.name() < right.spec.name(); + } + + std::vector find_outdated_packages(const VcpkgPaths& paths, const StatusParagraphs& status_db) + { + const std::map src_names_to_versions = + Paragraphs::load_all_port_names_and_versions(paths.get_filesystem(), paths.ports); + const std::vector installed_packages = get_installed_ports(status_db); + + std::vector output; + for (const StatusParagraph* pgh : installed_packages) + { + const auto it = src_names_to_versions.find(pgh->package.spec.name()); + if (it == src_names_to_versions.end()) + { + // Package was not installed from portfile + continue; + } + if (it->second != pgh->package.version) + { + output.push_back({pgh->package.spec, VersionDiff(pgh->package.version, it->second)}); + } + } + + return output; + } + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + args.check_exact_arg_count(0); + args.check_and_get_optional_command_arguments({}); + System::println("Using local portfile versions. To update the local portfiles, use `git pull`."); + + const StatusParagraphs status_db = database_load_check(paths); + + const auto outdated_packages = + SortedVector(find_outdated_packages(paths, status_db), &OutdatedPackage::compare_by_name); + + if (outdated_packages.empty()) + { + System::println("No packages need updating."); + } + else + { + std::string install_line; + System::println("The following packages differ from their port versions:"); + for (auto&& package : outdated_packages) + { + install_line += package.spec.to_string(); + install_line += " "; + System::println(" %-32s %s", package.spec, package.version_diff.to_string()); + } + System::println("\n" + "To update these packages, run\n" + " .\\vcpkg remove --outdated\n" + " .\\vcpkg install " + + install_line); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp new file mode 100644 index 000000000..522961693 --- /dev/null +++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp @@ -0,0 +1,284 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +namespace vcpkg +{ + static void parse_value(const std::string* arg_begin, + const std::string* arg_end, + const std::string& option_name, + std::unique_ptr& option_field) + { + if (arg_begin == arg_end) + { + System::println(System::Color::error, "Error: expected value after %s", option_name); + Metrics::g_metrics.lock()->track_property("error", "error option name"); + Help::print_usage(); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + if (option_field != nullptr) + { + System::println(System::Color::error, "Error: %s specified multiple times", option_name); + Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times"); + Help::print_usage(); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + option_field = std::make_unique(*arg_begin); + } + + static void parse_switch(bool new_setting, const std::string& option_name, Optional& option_field) + { + if (option_field && option_field != new_setting) + { + System::println(System::Color::error, "Error: conflicting values specified for --%s", option_name); + Metrics::g_metrics.lock()->track_property("error", "error conflicting switches"); + Help::print_usage(); + Checks::exit_fail(VCPKG_LINE_INFO); + } + option_field = new_setting; + } + + VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc, const wchar_t* const* const argv) + { + std::vector v; + for (int i = 1; i < argc; ++i) + { + v.push_back(Strings::to_utf8(argv[i])); + } + + return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size()); + } + + VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin, + const std::string* arg_end) + { + VcpkgCmdArguments args; + + for (; arg_begin != arg_end; ++arg_begin) + { + std::string arg = *arg_begin; + + if (arg.empty()) + { + continue; + } + + if (arg[0] == '-' && arg[1] != '-') + { + Metrics::g_metrics.lock()->track_property("error", "error short options are not supported"); + Checks::exit_with_message(VCPKG_LINE_INFO, "Error: short options are not supported: %s", arg); + } + + if (arg[0] == '-' && arg[1] == '-') + { + // make argument case insensitive + auto& f = std::use_facet>(std::locale()); + f.tolower(&arg[0], &arg[0] + arg.size()); + // command switch + if (arg == "--vcpkg-root") + { + ++arg_begin; + parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir); + continue; + } + if (arg == "--triplet") + { + ++arg_begin; + parse_value(arg_begin, arg_end, "--triplet", args.triplet); + continue; + } + if (arg == "--debug") + { + parse_switch(true, "debug", args.debug); + continue; + } + if (arg == "--sendmetrics") + { + parse_switch(true, "sendmetrics", args.sendmetrics); + continue; + } + if (arg == "--printmetrics") + { + parse_switch(true, "printmetrics", args.printmetrics); + continue; + } + if (arg == "--no-sendmetrics") + { + parse_switch(false, "sendmetrics", args.sendmetrics); + continue; + } + if (arg == "--no-printmetrics") + { + parse_switch(false, "printmetrics", args.printmetrics); + continue; + } + if (arg == "--featurepackages") + { + GlobalState::feature_packages = true; + continue; + } + + const auto eq_pos = arg.find('='); + if (eq_pos != std::string::npos) + { + args.optional_command_arguments.emplace(arg.substr(0, eq_pos), arg.substr(eq_pos + 1)); + } + else + { + args.optional_command_arguments.emplace(arg, nullopt); + } + continue; + } + + if (args.command.empty()) + { + args.command = arg; + } + else + { + args.command_arguments.push_back(arg); + } + } + + return args; + } + + ParsedArguments VcpkgCmdArguments::check_and_get_optional_command_arguments( + const std::vector& valid_switches, const std::vector& valid_settings) const + { + bool failed = false; + ParsedArguments output; + + auto options_copy = this->optional_command_arguments; + for (const std::string& option : valid_switches) + { + const auto it = options_copy.find(option); + if (it != options_copy.end()) + { + if (it->second.has_value()) + { + // Having a string value indicates it was passed like '--a=xyz' + System::println(System::Color::error, "The option '%s' does not accept an argument.", option); + failed = true; + } + else + { + output.switches.insert(option); + options_copy.erase(it); + } + } + } + + for (const std::string& option : valid_settings) + { + const auto it = options_copy.find(option); + if (it != options_copy.end()) + { + if (!it->second.has_value()) + { + // Not having a string value indicates it was passed like '--a' + System::println(System::Color::error, "The option '%s' must be passed an argument.", option); + failed = true; + } + else + { + output.settings.emplace(option, it->second.value_or_exit(VCPKG_LINE_INFO)); + options_copy.erase(it); + } + } + } + + if (!options_copy.empty()) + { + System::println(System::Color::error, "Unknown option(s) for command '%s':", this->command); + for (auto&& option : options_copy) + { + System::println(" %s", option.first); + } + System::println("\nValid options are:", this->command); + for (auto&& option : valid_switches) + { + System::println(" %s", option); + } + for (auto&& option : valid_settings) + { + System::println(" %s=...", option); + } + System::println(" --triplet "); + System::println(" --vcpkg-root "); + + Checks::exit_fail(VCPKG_LINE_INFO); + } + if (failed) Checks::exit_fail(VCPKG_LINE_INFO); + + return output; + } + + void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count) const + { + return check_max_arg_count(expected_arg_count, Strings::EMPTY); + } + + void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count) const + { + return check_min_arg_count(expected_arg_count, Strings::EMPTY); + } + + void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count) const + { + return check_exact_arg_count(expected_arg_count, Strings::EMPTY); + } + + void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count, const std::string& example_text) const + { + const size_t actual_arg_count = command_arguments.size(); + if (actual_arg_count > expected_arg_count) + { + System::println(System::Color::error, + "Error: `%s` requires at most %u arguments, but %u were provided", + this->command, + expected_arg_count, + actual_arg_count); + System::print(example_text); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } + + void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count, const std::string& example_text) const + { + const size_t actual_arg_count = command_arguments.size(); + if (actual_arg_count < expected_arg_count) + { + System::println(System::Color::error, + "Error: `%s` requires at least %u arguments, but %u were provided", + this->command, + expected_arg_count, + actual_arg_count); + System::print(example_text); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } + + void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count, + const std::string& example_text) const + { + const size_t actual_arg_count = command_arguments.size(); + if (actual_arg_count != expected_arg_count) + { + System::println(System::Color::error, + "Error: `%s` requires %u arguments, but %u were provided", + this->command, + expected_arg_count, + actual_arg_count); + System::print(example_text); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } +} diff --git a/toolsrc/src/vcpkg/vcpkglib.cpp b/toolsrc/src/vcpkg/vcpkglib.cpp new file mode 100644 index 000000000..23c774210 --- /dev/null +++ b/toolsrc/src/vcpkg/vcpkglib.cpp @@ -0,0 +1,247 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +namespace vcpkg +{ + static StatusParagraphs load_current_database(Files::Filesystem& fs, + const fs::path& vcpkg_dir_status_file, + const fs::path& vcpkg_dir_status_file_old) + { + if (!fs.exists(vcpkg_dir_status_file)) + { + if (!fs.exists(vcpkg_dir_status_file_old)) + { + // no status file, use empty db + return StatusParagraphs(); + } + + fs.rename(vcpkg_dir_status_file_old, vcpkg_dir_status_file); + } + + auto pghs = Paragraphs::get_paragraphs(fs, vcpkg_dir_status_file).value_or_exit(VCPKG_LINE_INFO); + + std::vector> status_pghs; + for (auto&& p : pghs) + { + status_pghs.push_back(std::make_unique(std::move(p))); + } + + return StatusParagraphs(std::move(status_pghs)); + } + + StatusParagraphs database_load_check(const VcpkgPaths& paths) + { + auto& fs = paths.get_filesystem(); + + const auto updates_dir = paths.vcpkg_dir_updates; + + std::error_code ec; + fs.create_directory(paths.installed, ec); + fs.create_directory(paths.vcpkg_dir, ec); + fs.create_directory(paths.vcpkg_dir_info, ec); + fs.create_directory(updates_dir, ec); + + const fs::path& status_file = paths.vcpkg_dir_status_file; + const fs::path status_file_old = status_file.parent_path() / "status-old"; + const fs::path status_file_new = status_file.parent_path() / "status-new"; + + StatusParagraphs current_status_db = load_current_database(fs, status_file, status_file_old); + + auto update_files = fs.get_files_non_recursive(updates_dir); + if (update_files.empty()) + { + // updates directory is empty, control file is up-to-date. + return current_status_db; + } + for (auto&& file : update_files) + { + if (!fs.is_regular_file(file)) continue; + if (file.filename() == "incomplete") continue; + + auto pghs = Paragraphs::get_paragraphs(fs, file).value_or_exit(VCPKG_LINE_INFO); + for (auto&& p : pghs) + { + current_status_db.insert(std::make_unique(std::move(p))); + } + } + + fs.write_contents(status_file_new, Strings::serialize(current_status_db)); + + fs.rename(status_file_new, status_file); + + for (auto&& file : update_files) + { + if (!fs.is_regular_file(file)) continue; + + fs.remove(file); + } + + return current_status_db; + } + + void write_update(const VcpkgPaths& paths, const StatusParagraph& p) + { + static int update_id = 0; + auto& fs = paths.get_filesystem(); + + const auto my_update_id = update_id++; + const auto tmp_update_filename = paths.vcpkg_dir_updates / "incomplete"; + const auto update_filename = paths.vcpkg_dir_updates / std::to_string(my_update_id); + + fs.write_contents(tmp_update_filename, Strings::serialize(p)); + fs.rename(tmp_update_filename, update_filename); + } + + static void upgrade_to_slash_terminated_sorted_format(Files::Filesystem& fs, + std::vector* lines, + const fs::path& listfile_path) + { + static bool was_tracked = false; + + if (lines->empty()) + { + return; + } + + if (lines->at(0).back() == '/') + { + return; // File already in the new format + } + + if (!was_tracked) + { + was_tracked = true; + Metrics::g_metrics.lock()->track_property("listfile", "update to new format"); + } + + // The files are sorted such that directories are placed just before the files they contain + // (They are not necessarily sorted alphabetically, e.g. libflac) + // Therefore we can detect the entries that represent directories by comparing every element with the next one + // and checking if the next has a slash immediately after the current one's length + for (size_t i = 0; i < lines->size() - 1; i++) + { + std::string& current_string = lines->at(i); + const std::string& next_string = lines->at(i + 1); + + const size_t potential_slash_char_index = current_string.length(); + // Make sure the index exists first + if (next_string.size() > potential_slash_char_index && next_string.at(potential_slash_char_index) == '/') + { + current_string += '/'; // Mark as a directory + } + } + + // After suffixing the directories with a slash, we can now sort. + // We cannot sort before adding the suffixes because the following (actual example): + /* + x86-windows/include/FLAC <<<<<< This would be separated from its group due to sorting + x86-windows/include/FLAC/all.h + x86-windows/include/FLAC/assert.h + x86-windows/include/FLAC/callback.h + x86-windows/include/FLAC++ + x86-windows/include/FLAC++/all.h + x86-windows/include/FLAC++/decoder.h + x86-windows/include/FLAC++/encoder.h + * + x86-windows/include/FLAC/ <<<<<< This will now be kept with its group when sorting + x86-windows/include/FLAC/all.h + x86-windows/include/FLAC/assert.h + x86-windows/include/FLAC/callback.h + x86-windows/include/FLAC++/ + x86-windows/include/FLAC++/all.h + x86-windows/include/FLAC++/decoder.h + x86-windows/include/FLAC++/encoder.h + */ + // Note that after sorting, the FLAC++/ group will be placed before the FLAC/ group + // The new format is lexicographically sorted + std::sort(lines->begin(), lines->end()); + + // Replace the listfile on disk + const fs::path updated_listfile_path = listfile_path.generic_string() + "_updated"; + fs.write_lines(updated_listfile_path, *lines); + fs.rename(updated_listfile_path, listfile_path); + } + + std::vector get_installed_ports(const StatusParagraphs& status_db) + { + std::vector installed_packages; + for (auto&& pgh : status_db) + { + if (pgh->state != InstallState::INSTALLED || pgh->want != Want::INSTALL) continue; + installed_packages.push_back(pgh.get()); + } + + return installed_packages; + } + + std::vector get_installed_files(const VcpkgPaths& paths, + const StatusParagraphs& status_db) + { + auto& fs = paths.get_filesystem(); + + std::vector installed_files; + + for (const std::unique_ptr& pgh : status_db) + { + if (pgh->state != InstallState::INSTALLED || !pgh->package.feature.empty()) + { + continue; + } + + const fs::path listfile_path = paths.listfile_path(pgh->package); + std::vector installed_files_of_current_pgh = + fs.read_lines(listfile_path).value_or_exit(VCPKG_LINE_INFO); + Strings::trim_all_and_remove_whitespace_strings(&installed_files_of_current_pgh); + upgrade_to_slash_terminated_sorted_format(fs, &installed_files_of_current_pgh, listfile_path); + + // Remove the directories + Util::erase_remove_if(installed_files_of_current_pgh, + [](const std::string& file) { return file.back() == '/'; }); + + StatusParagraphAndAssociatedFiles pgh_and_files = { + *pgh, SortedVector(std::move(installed_files_of_current_pgh))}; + installed_files.push_back(std::move(pgh_and_files)); + } + + return installed_files; + } + + CMakeVariable::CMakeVariable(const CWStringView varname, const wchar_t* varvalue) + : s(Strings::wformat(LR"("-D%s=%s")", varname, varvalue)) + { + } + CMakeVariable::CMakeVariable(const CWStringView varname, const std::string& varvalue) + : CMakeVariable(varname, Strings::to_utf16(varvalue).c_str()) + { + } + CMakeVariable::CMakeVariable(const CWStringView varname, const std::wstring& varvalue) + : CMakeVariable(varname, varvalue.c_str()) + { + } + CMakeVariable::CMakeVariable(const CWStringView varname, const fs::path& path) + : CMakeVariable(varname, path.generic_wstring()) + { + } + + std::wstring make_cmake_cmd(const fs::path& cmake_exe, + const fs::path& cmake_script, + const std::vector& pass_variables) + { + const std::wstring cmd_cmake_pass_variables = Strings::join(L" ", pass_variables, [](auto&& v) { return v.s; }); + return Strings::wformat( + LR"("%s" %s -P "%s")", cmake_exe.native(), cmd_cmake_pass_variables, cmake_script.generic_wstring()); + } + + std::string shorten_text(const std::string& desc, size_t length) + { + Checks::check_exit(VCPKG_LINE_INFO, length >= 3); + auto simple_desc = std::regex_replace(desc, std::regex("\\s+"), " "); + return simple_desc.size() <= length ? simple_desc : simple_desc.substr(0, length - 3) + "..."; + } +} diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp new file mode 100644 index 000000000..e1a4acd69 --- /dev/null +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -0,0 +1,519 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg +{ + static constexpr CWStringView V_120 = L"v120"; + static constexpr CWStringView V_140 = L"v140"; + static constexpr CWStringView V_141 = L"v141"; + + static bool exists_and_has_equal_or_greater_version(const std::wstring& version_cmd, + const std::array& expected_version) + { + static const std::regex RE(R"###((\d+)\.(\d+)\.(\d+))###"); + + const auto rc = System::cmd_execute_and_capture_output(Strings::wformat(LR"(%s)", version_cmd)); + if (rc.exit_code != 0) + { + return false; + } + + std::match_results match; + const auto found = std::regex_search(rc.output, match, RE); + if (!found) + { + return false; + } + + const int d1 = atoi(match[1].str().c_str()); + const int d2 = atoi(match[2].str().c_str()); + const int d3 = atoi(match[3].str().c_str()); + if (d1 > expected_version[0] || (d1 == expected_version[0] && d2 > expected_version[1]) || + (d1 == expected_version[0] && d2 == expected_version[1] && d3 >= expected_version[2])) + { + // satisfactory version found + return true; + } + + return false; + } + + static Optional find_if_has_equal_or_greater_version(const std::vector& candidate_paths, + const std::wstring& version_check_arguments, + const std::array& expected_version) + { + auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { + const std::wstring cmd = Strings::wformat(LR"("%s" %s)", p.native(), version_check_arguments); + return exists_and_has_equal_or_greater_version(cmd, expected_version); + }); + + if (it != candidate_paths.cend()) + { + return std::move(*it); + } + + return nullopt; + } + + static fs::path fetch_dependency(const fs::path& scripts_folder, + const std::wstring& tool_name, + const fs::path& expected_downloaded_path, + const std::array& version) + { + const std::string tool_name_utf8 = Strings::to_utf8(tool_name); + const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]); + System::println("A suitable version of %s was not found (required v%s). Downloading portable %s v%s...", + tool_name_utf8, + version_as_string, + tool_name_utf8, + version_as_string); + const fs::path script = scripts_folder / "fetchDependency.ps1"; + const auto install_cmd = + System::create_powershell_script_cmd(script, Strings::wformat(L"-Dependency %s", tool_name)); + const System::ExitCodeAndOutput rc = System::cmd_execute_and_capture_output(install_cmd); + if (rc.exit_code) + { + System::println(System::Color::error, + "Launching powershell failed or was denied when trying to fetch %s version %s.\n" + "(No sufficient installed version was found)", + tool_name_utf8, + version_as_string); + { + auto locked_metrics = Metrics::g_metrics.lock(); + locked_metrics->track_property("error", "powershell install failed"); + locked_metrics->track_property("dependency", tool_name); + } + Checks::exit_with_code(VCPKG_LINE_INFO, rc.exit_code); + } + + const fs::path actual_downloaded_path = Strings::trimmed(rc.output); + std::error_code ec; + const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + eq && !ec, + "Expected dependency downloaded path to be %s, but was %s", + expected_downloaded_path.u8string(), + actual_downloaded_path.u8string()); + return actual_downloaded_path; + } + + static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path& scripts_folder) + { + static constexpr std::array EXPECTED_VERSION = {3, 9, 3}; + static const std::wstring VERSION_CHECK_ARGUMENTS = L"--version"; + + const fs::path downloaded_copy = downloads_folder / "cmake-3.9.3-win32-x86" / "bin" / "cmake.exe"; + const std::vector from_path = Files::find_from_PATH(L"cmake"); + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(System::get_program_files_platform_bitness() / "CMake" / "bin" / "cmake.exe"); + candidate_paths.push_back(System::get_program_files_32_bit() / "CMake" / "bin"); + + const Optional path = + find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); + if (const auto p = path.get()) + { + return *p; + } + + return fetch_dependency(scripts_folder, L"cmake", downloaded_copy, EXPECTED_VERSION); + } + + fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path& scripts_folder) + { + static constexpr std::array EXPECTED_VERSION = {4, 3, 0}; + static const std::wstring VERSION_CHECK_ARGUMENTS = Strings::WEMPTY; + + const fs::path downloaded_copy = downloads_folder / "nuget-4.3.0" / "nuget.exe"; + const std::vector from_path = Files::find_from_PATH(L"nuget"); + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + + auto path = find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); + if (const auto p = path.get()) + { + return *p; + } + + return fetch_dependency(scripts_folder, L"nuget", downloaded_copy, EXPECTED_VERSION); + } + + fs::path get_git_path(const fs::path& downloads_folder, const fs::path& scripts_folder) + { + static constexpr std::array EXPECTED_VERSION = {2, 14, 1}; + static const std::wstring VERSION_CHECK_ARGUMENTS = L"--version"; + + const fs::path downloaded_copy = downloads_folder / "MinGit-2.14.1-32-bit" / "cmd" / "git.exe"; + const std::vector from_path = Files::find_from_PATH(L"git"); + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(System::get_program_files_platform_bitness() / "git" / "cmd" / "git.exe"); + candidate_paths.push_back(System::get_program_files_32_bit() / "git" / "cmd" / "git.exe"); + + const Optional path = + find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); + if (const auto p = path.get()) + { + return *p; + } + + return fetch_dependency(scripts_folder, L"git", downloaded_copy, EXPECTED_VERSION); + } + + static fs::path get_ifw_installerbase_path(const fs::path& downloads_folder, const fs::path& scripts_folder) + { + static constexpr std::array EXPECTED_VERSION = {3, 1, 81}; + static const std::wstring VERSION_CHECK_ARGUMENTS = L"--framework-version"; + + const fs::path downloaded_copy = + downloads_folder / "QtInstallerFramework-win-x86" / "bin" / "installerbase.exe"; + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + // TODO: Uncomment later + // const std::vector from_path = Files::find_from_PATH(L"installerbase"); + // candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + // candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / + // "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe"); + // candidate_paths.push_back(fs::path(System::get_environment_variable(L"HOMEDRIVE").value_or("C:")) / "Qt" / + // "QtIFW-3.1.0" / "bin" / "installerbase.exe"); + + const Optional path = + find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, EXPECTED_VERSION); + if (const auto p = path.get()) + { + return *p; + } + + return fetch_dependency(scripts_folder, L"installerbase", downloaded_copy, EXPECTED_VERSION); + } + + Expected VcpkgPaths::create(const fs::path& vcpkg_root_dir) + { + std::error_code ec; + const fs::path canonical_vcpkg_root_dir = fs::stdfs::canonical(vcpkg_root_dir, ec); + if (ec) + { + return ec; + } + + VcpkgPaths paths; + paths.root = canonical_vcpkg_root_dir; + + if (paths.root.empty()) + { + Metrics::g_metrics.lock()->track_property("error", "Invalid vcpkg root directory"); + Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid vcpkg root directory: %s", paths.root.string()); + } + + paths.packages = paths.root / "packages"; + paths.buildtrees = paths.root / "buildtrees"; + paths.downloads = paths.root / "downloads"; + paths.ports = paths.root / "ports"; + paths.installed = paths.root / "installed"; + paths.triplets = paths.root / "triplets"; + paths.scripts = paths.root / "scripts"; + + paths.buildsystems = paths.scripts / "buildsystems"; + paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets"; + + paths.vcpkg_dir = paths.installed / "vcpkg"; + paths.vcpkg_dir_status_file = paths.vcpkg_dir / "status"; + paths.vcpkg_dir_info = paths.vcpkg_dir / "info"; + paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates"; + + paths.ports_cmake = paths.scripts / "ports.cmake"; + + return paths; + } + + fs::path VcpkgPaths::package_dir(const PackageSpec& spec) const { return this->packages / spec.dir(); } + + fs::path VcpkgPaths::port_dir(const PackageSpec& spec) const { return this->ports / spec.name(); } + fs::path VcpkgPaths::port_dir(const std::string& name) const { return this->ports / name; } + + fs::path VcpkgPaths::build_info_file_path(const PackageSpec& spec) const + { + return this->package_dir(spec) / "BUILD_INFO"; + } + + fs::path VcpkgPaths::listfile_path(const BinaryParagraph& pgh) const + { + return this->vcpkg_dir_info / (pgh.fullstem() + ".list"); + } + + bool VcpkgPaths::is_valid_triplet(const Triplet& t) const + { + for (auto&& path : get_filesystem().get_files_non_recursive(this->triplets)) + { + const std::string triplet_file_name = path.stem().generic_u8string(); + if (t.canonical_name() == triplet_file_name) // TODO: fuzzy compare + { + // t.value = triplet_file_name; // NOTE: uncomment when implementing fuzzy compare + return true; + } + } + return false; + } + + const fs::path& VcpkgPaths::get_cmake_exe() const + { + return this->cmake_exe.get_lazy([this]() { return get_cmake_path(this->downloads, this->scripts); }); + } + + const fs::path& VcpkgPaths::get_git_exe() const + { + return this->git_exe.get_lazy([this]() { return get_git_path(this->downloads, this->scripts); }); + } + + const fs::path& VcpkgPaths::get_nuget_exe() const + { + return this->nuget_exe.get_lazy([this]() { return get_nuget_path(this->downloads, this->scripts); }); + } + + const fs::path& VcpkgPaths::get_ifw_installerbase_exe() const + { + return this->ifw_installerbase_exe.get_lazy( + [this]() { return get_ifw_installerbase_path(this->downloads, this->scripts); }); + } + + const fs::path& VcpkgPaths::get_ifw_binarycreator_exe() const + { + return this->ifw_binarycreator_exe.get_lazy( + [this]() { return get_ifw_installerbase_exe().parent_path() / "binarycreator.exe"; }); + } + + const fs::path& VcpkgPaths::get_ifw_repogen_exe() const + { + return this->ifw_repogen_exe.get_lazy( + [this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; }); + } + + struct VisualStudioInstance + { + fs::path root_path; + std::string version; + std::string release_type; + std::string preference_weight; // Mostly unused, just for verification that order is as intended + + std::string major_version() const { return version.substr(0, 2); } + }; + + static std::vector get_visual_studio_instances(const VcpkgPaths& paths) + { + const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; + const std::wstring cmd = System::create_powershell_script_cmd(script); + const System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd); + Checks::check_exit( + VCPKG_LINE_INFO, ec_data.exit_code == 0, "Could not run script to detect Visual Studio instances"); + + const std::vector instances_as_strings = Strings::split(ec_data.output, "\n"); + std::vector output; + for (const std::string& instance_as_string : instances_as_strings) + { + const std::vector split = Strings::split(instance_as_string, "::"); + output.push_back({split.at(3), split.at(2), split.at(1), split.at(0)}); + } + + return output; + } + + static std::vector find_toolset_instances(const VcpkgPaths& paths) + { + using CPU = System::CPUArchitecture; + + const auto& fs = paths.get_filesystem(); + + // Note: this will contain a mix of vcvarsall.bat locations and dumpbin.exe locations. + std::vector paths_examined; + + std::vector found_toolsets; + + const std::vector vs_instances = get_visual_studio_instances(paths); + const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) { + return vs_instance.major_version() == "14"; + }) != vs_instances.cend(); + + for (const VisualStudioInstance& vs_instance : vs_instances) + { + const std::string major_version = vs_instance.major_version(); + if (major_version == "15") + { + const fs::path vc_dir = vs_instance.root_path / "VC"; + + // Skip any instances that do not have vcvarsall. + const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build"; + const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat"; + paths_examined.push_back(vcvarsall_bat); + if (!fs.exists(vcvarsall_bat)) continue; + + // Get all supported architectures + std::vector supported_architectures; + if (fs.exists(vcvarsall_dir / "vcvars32.bat")) + supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); + if (fs.exists(vcvarsall_dir / "vcvars64.bat")) + supported_architectures.push_back({L"amd64", CPU::X64, CPU::X64}); + if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat")) + supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); + if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat")) + supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); + if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat")) + supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); + if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat")) + supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); + + // Locate the "best" MSVC toolchain version + const fs::path msvc_path = vc_dir / "Tools" / "MSVC"; + std::vector msvc_subdirectories = fs.get_files_non_recursive(msvc_path); + Util::unstable_keep_if(msvc_subdirectories, + [&fs](const fs::path& path) { return fs.is_directory(path); }); + + // Sort them so that latest comes first + std::sort( + msvc_subdirectories.begin(), + msvc_subdirectories.end(), + [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); }); + + for (const fs::path& subdir : msvc_subdirectories) + { + const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe"; + paths_examined.push_back(dumpbin_path); + if (fs.exists(dumpbin_path)) + { + found_toolsets.push_back(Toolset{ + vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}); + + if (v140_is_available) + { + found_toolsets.push_back(Toolset{vs_instance.root_path, + dumpbin_path, + vcvarsall_bat, + {L"-vcvars_ver=14.0"}, + V_140, + supported_architectures}); + } + + break; + } + } + + continue; + } + + if (major_version == "14") + { + const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat"; + + paths_examined.push_back(vcvarsall_bat); + if (fs.exists(vcvarsall_bat)) + { + const fs::path vs2015_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe"; + paths_examined.push_back(vs2015_dumpbin_exe); + + const fs::path vs2015_bin_dir = vcvarsall_bat.parent_path() / "bin"; + std::vector supported_architectures; + if (fs.exists(vs2015_bin_dir / "vcvars32.bat")) + supported_architectures.push_back({L"x86", CPU::X86, CPU::X86}); + if (fs.exists(vs2015_bin_dir / "amd64\\vcvars64.bat")) + supported_architectures.push_back({L"x64", CPU::X64, CPU::X64}); + if (fs.exists(vs2015_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat")) + supported_architectures.push_back({L"x86_amd64", CPU::X86, CPU::X64}); + if (fs.exists(vs2015_bin_dir / "x86_arm\\vcvarsx86_arm.bat")) + supported_architectures.push_back({L"x86_arm", CPU::X86, CPU::ARM}); + if (fs.exists(vs2015_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat")) + supported_architectures.push_back({L"amd64_x86", CPU::X64, CPU::X86}); + if (fs.exists(vs2015_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat")) + supported_architectures.push_back({L"amd64_arm", CPU::X64, CPU::ARM}); + + if (fs.exists(vs2015_dumpbin_exe)) + { + found_toolsets.push_back({vs_instance.root_path, + vs2015_dumpbin_exe, + vcvarsall_bat, + {}, + V_140, + supported_architectures}); + } + } + } + } + + if (found_toolsets.empty()) + { + System::println(System::Color::error, "Could not locate a complete toolset."); + System::println("The following paths were examined:"); + for (const fs::path& path : paths_examined) + { + System::println(" %s", path.u8string()); + } + Checks::exit_fail(VCPKG_LINE_INFO); + } + + return found_toolsets; + } + + const Toolset& VcpkgPaths::get_toolset(const Optional& toolset_version, + const Optional& visual_studio_path) const + { + // Invariant: toolsets are non-empty and sorted with newest at back() + const std::vector& vs_toolsets = + this->toolsets.get_lazy([this]() { return find_toolset_instances(*this); }); + + std::vector candidates = Util::element_pointers(vs_toolsets); + const auto tsv = toolset_version.get(); + const auto vsp = visual_studio_path.get(); + + if (tsv && vsp) + { + const std::wstring w_toolset_version = Strings::to_utf16(*tsv); + const fs::path vs_root_path = *vsp; + Util::stable_keep_if(candidates, [&](const Toolset* t) { + return w_toolset_version == t->version && vs_root_path == t->visual_studio_root_path; + }); + Checks::check_exit(VCPKG_LINE_INFO, + !candidates.empty(), + "Could not find Visual Studio instace at %s with %s toolset.", + vs_root_path.generic_string(), + *tsv); + + Checks::check_exit(VCPKG_LINE_INFO, candidates.size() == 1); + return *candidates.back(); + } + + if (tsv) + { + const std::wstring w_toolset_version = Strings::to_utf16(*tsv); + Util::stable_keep_if(candidates, [&](const Toolset* t) { return w_toolset_version == t->version; }); + Checks::check_exit( + VCPKG_LINE_INFO, !candidates.empty(), "Could not find Visual Studio instace with %s toolset.", *tsv); + } + + if (vsp) + { + const fs::path vs_root_path = *vsp; + Util::stable_keep_if(candidates, + [&](const Toolset* t) { return vs_root_path == t->visual_studio_root_path; }); + Checks::check_exit(VCPKG_LINE_INFO, + !candidates.empty(), + "Could not find Visual Studio instace at %s.", + vs_root_path.generic_string()); + } + + return *candidates.front(); + } + + Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); } +} diff --git a/toolsrc/src/vcpkg/versiont.cpp b/toolsrc/src/vcpkg/versiont.cpp new file mode 100644 index 000000000..1c52d674a --- /dev/null +++ b/toolsrc/src/vcpkg/versiont.cpp @@ -0,0 +1,19 @@ +#include "pch.h" + +#include +#include + +namespace vcpkg +{ + VersionT::VersionT() : value("0.0.0") {} + VersionT::VersionT(const std::string& value) : value(value) {} + std::string VersionT::to_string() const { return value; } + bool operator==(const VersionT& left, const VersionT& right) { return left.value == right.value; } + bool operator!=(const VersionT& left, const VersionT& right) { return left.value != right.value; } + std::string to_printf_arg(const VersionT& version) { return version.value; } + + VersionDiff::VersionDiff() : left(), right() {} + VersionDiff::VersionDiff(const VersionT& left, const VersionT& right) : left(left), right(right) {} + + std::string VersionDiff::to_string() const { return Strings::format("%s -> %s", left.value, right.value); } +} diff --git a/toolsrc/src/vcpkg_Build.cpp b/toolsrc/src/vcpkg_Build.cpp deleted file mode 100644 index 9be20629d..000000000 --- a/toolsrc/src/vcpkg_Build.cpp +++ /dev/null @@ -1,406 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "PostBuildLint.h" -#include "metrics.h" -#include "vcpkg_Build.h" -#include "vcpkg_Checks.h" -#include "vcpkg_Chrono.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Enums.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_System.h" -#include "vcpkg_optional.h" -#include "vcpkglib.h" - -namespace vcpkg::Build -{ - Optional to_linkage_type(const std::string& str) - { - if (str == "dynamic") return LinkageType::DYNAMIC; - if (str == "static") return LinkageType::STATIC; - return nullopt; - } - - namespace BuildInfoRequiredField - { - static const std::string CRT_LINKAGE = "CRTLinkage"; - static const std::string LIBRARY_LINKAGE = "LibraryLinkage"; - } - - CWStringView to_vcvarsall_target(const std::string& cmake_system_name) - { - if (cmake_system_name == Strings::EMPTY) return Strings::WEMPTY; - if (cmake_system_name == "Windows") return Strings::WEMPTY; - if (cmake_system_name == "WindowsStore") return L"store"; - - Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported vcvarsall target %s", cmake_system_name); - } - - CWStringView to_vcvarsall_toolchain(const std::string& target_architecture, const Toolset& toolset) - { - auto maybe_target_arch = System::to_cpu_architecture(target_architecture); - Checks::check_exit( - VCPKG_LINE_INFO, maybe_target_arch.has_value(), "Invalid architecture string: %s", target_architecture); - auto target_arch = maybe_target_arch.value_or_exit(VCPKG_LINE_INFO); - auto host_architectures = System::get_supported_host_architectures(); - - for (auto&& host : host_architectures) - { - auto it = Util::find_if(toolset.supported_architectures, [&](const ToolsetArchOption& opt) { - return host == opt.host_arch && target_arch == opt.target_arch; - }); - if (it != toolset.supported_architectures.end()) return it->name; - } - - Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported toolchain combination %s", target_architecture); - } - - std::wstring make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) - { - const wchar_t* tonull = L" >nul"; - if (GlobalState::debugging) - { - tonull = Strings::WEMPTY; - } - - const auto arch = to_vcvarsall_toolchain(pre_build_info.target_architecture, toolset); - const auto target = to_vcvarsall_target(pre_build_info.cmake_system_name); - - return Strings::wformat(LR"("%s" %s %s %s %s 2>&1)", - toolset.vcvarsall.native(), - Strings::join(L" ", toolset.vcvarsall_options), - arch, - target, - tonull); - } - - static void create_binary_feature_control_file(const SourceParagraph& source_paragraph, - const FeatureParagraph& feature_paragraph, - const Triplet& triplet, - BinaryControlFile& bcf) - { - BinaryParagraph bpgh(source_paragraph, feature_paragraph, triplet); - bcf.features.emplace_back(std::move(bpgh)); - } - - static void create_binary_control_file(const SourceParagraph& source_paragraph, - const Triplet& triplet, - const BuildInfo& build_info, - BinaryControlFile& bcf) - { - BinaryParagraph bpgh(source_paragraph, triplet); - if (const auto p_ver = build_info.version.get()) - { - bpgh.version = *p_ver; - } - bcf.core_paragraph = std::move(bpgh); - } - - static void write_binary_control_file(const VcpkgPaths& paths, BinaryControlFile bcf) - { - std::string start = Strings::serialize(bcf.core_paragraph); - for (auto&& feature : bcf.features) - { - start += "\n" + Strings::serialize(feature); - } - const fs::path binary_control_file = paths.packages / bcf.core_paragraph.dir() / "CONTROL"; - paths.get_filesystem().write_contents(binary_control_file, start); - } - - ExtendedBuildResult build_package(const VcpkgPaths& paths, - const BuildPackageConfig& config, - const StatusParagraphs& status_db) - { - const PackageSpec spec = - PackageSpec::from_name_and_triplet(config.src.name, config.triplet).value_or_exit(VCPKG_LINE_INFO); - - const Triplet& triplet = config.triplet; - { - std::vector missing_specs; - for (auto&& dep : filter_dependencies(config.src.depends, triplet)) - { - if (status_db.find_installed(dep, triplet) == status_db.end()) - { - missing_specs.push_back( - PackageSpec::from_name_and_triplet(dep, triplet).value_or_exit(VCPKG_LINE_INFO)); - } - } - // Fail the build if any dependencies were missing - if (!missing_specs.empty()) - { - return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_specs)}; - } - } - - const fs::path& cmake_exe_path = paths.get_cmake_exe(); - const fs::path& git_exe_path = paths.get_git_exe(); - - const fs::path ports_cmake_script_path = paths.ports_cmake; - const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet); - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); - const auto cmd_set_environment = make_build_env_cmd(pre_build_info, toolset); - - std::string features; - if (GlobalState::feature_packages) - { - if (config.feature_list) - { - for (auto&& feature : *config.feature_list) - { - features.append(feature + ";"); - } - if (features.size() > 0) - { - features.pop_back(); - } - } - } - - const std::wstring cmd_launch_cmake = make_cmake_cmd( - cmake_exe_path, - ports_cmake_script_path, - { - {L"CMD", L"BUILD"}, - {L"PORT", config.src.name}, - {L"CURRENT_PORT_DIR", config.port_dir / "/."}, - {L"TARGET_TRIPLET", triplet.canonical_name()}, - {L"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()}, - {L"VCPKG_USE_HEAD_VERSION", to_bool(config.build_package_options.use_head_version) ? L"1" : L"0"}, - {L"_VCPKG_NO_DOWNLOADS", !to_bool(config.build_package_options.allow_downloads) ? L"1" : L"0"}, - {L"GIT", git_exe_path}, - {L"FEATURES", features}, - }); - - const std::wstring command = Strings::wformat(LR"(%s && %s)", cmd_set_environment, cmd_launch_cmake); - - const ElapsedTime timer = ElapsedTime::create_started(); - - const int return_code = System::cmd_execute_clean(command); - const auto buildtimeus = timer.microseconds(); - const auto spec_string = spec.to_string(); - - { - auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->track_metric("buildtimeus-" + spec_string, buildtimeus); - if (return_code != 0) - { - locked_metrics->track_property("error", "build failed"); - locked_metrics->track_property("build_error", spec_string); - return {BuildResult::BUILD_FAILED, {}}; - } - } - - const BuildInfo build_info = read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec)); - const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info); - - BinaryControlFile bcf; - - create_binary_control_file(config.src, triplet, build_info, bcf); - - if (error_count != 0) - { - return {BuildResult::POST_BUILD_CHECKS_FAILED, {}}; - } - if (GlobalState::feature_packages) - { - if (config.feature_list) - { - for (auto&& feature : *config.feature_list) - { - for (auto&& f_pgh : config.scf->feature_paragraphs) - { - if (f_pgh->name == feature) - create_binary_feature_control_file(*config.scf->core_paragraph, *f_pgh, triplet, bcf); - } - } - } - } - - write_binary_control_file(paths, bcf); - - // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name; - // delete_directory(port_buildtrees_dir); - - return {BuildResult::SUCCEEDED, {}}; - } - - const std::string& to_string(const BuildResult build_result) - { - static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string("vcpkg::Commands::Build::BuildResult"); - static const std::string SUCCEEDED_STRING = "SUCCEEDED"; - static const std::string BUILD_FAILED_STRING = "BUILD_FAILED"; - static const std::string FILE_CONFLICTS_STRING = "FILE_CONFLICTS"; - static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED"; - static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES"; - - switch (build_result) - { - case BuildResult::NULLVALUE: return NULLVALUE_STRING; - case BuildResult::SUCCEEDED: return SUCCEEDED_STRING; - case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING; - case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING; - case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; - case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - std::string create_error_message(const BuildResult build_result, const PackageSpec& spec) - { - return Strings::format("Error: Building package %s failed with: %s", spec, Build::to_string(build_result)); - } - - std::string create_user_troubleshooting_message(const PackageSpec& spec) - { - return Strings::format("Please ensure you're using the latest portfiles with `.\\vcpkg update`, then\n" - "submit an issue at https://github.com/Microsoft/vcpkg/issues including:\n" - " Package: %s\n" - " Vcpkg version: %s\n" - "\n" - "Additionally, attach any relevant sections from the log files above.", - spec, - Commands::Version::version()); - } - - static BuildInfo inner_create_buildinfo(std::unordered_map pgh) - { - Parse::ParagraphParser parser(std::move(pgh)); - - BuildInfo build_info; - - { - std::string crt_linkage_as_string; - parser.required_field(BuildInfoRequiredField::CRT_LINKAGE, crt_linkage_as_string); - - auto crtlinkage = to_linkage_type(crt_linkage_as_string); - if (const auto p = crtlinkage.get()) - build_info.crt_linkage = *p; - else - Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid crt linkage type: [%s]", crt_linkage_as_string); - } - - { - std::string library_linkage_as_string; - parser.required_field(BuildInfoRequiredField::LIBRARY_LINKAGE, library_linkage_as_string); - auto liblinkage = to_linkage_type(library_linkage_as_string); - if (const auto p = liblinkage.get()) - build_info.library_linkage = *p; - else - Checks::exit_with_message( - VCPKG_LINE_INFO, "Invalid library linkage type: [%s]", library_linkage_as_string); - } - std::string version = parser.optional_field("Version"); - if (!version.empty()) build_info.version = std::move(version); - - std::map policies; - for (auto policy : G_ALL_POLICIES) - { - const auto setting = parser.optional_field(to_string(policy)); - if (setting.empty()) continue; - if (setting == "enabled") - policies.emplace(policy, true); - else if (setting == "disabled") - policies.emplace(policy, false); - else - Checks::exit_with_message( - VCPKG_LINE_INFO, "Unknown setting for policy '%s': %s", to_string(policy), setting); - } - - if (const auto err = parser.error_info("PostBuildInformation")) - { - print_error_message(err); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - build_info.policies = BuildPolicies(std::move(policies)); - - return build_info; - } - - BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath) - { - const Expected> pghs = - Paragraphs::get_single_paragraph(fs, filepath); - Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package"); - return inner_create_buildinfo(*pghs.get()); - } - - PreBuildInfo PreBuildInfo::from_triplet_file(const VcpkgPaths& paths, const Triplet& triplet) - { - static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb"; - - const fs::path& cmake_exe_path = paths.get_cmake_exe(); - const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake"; - const fs::path triplet_file_path = paths.triplets / (triplet.canonical_name() + ".cmake"); - - const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe_path, - ports_cmake_script_path, - { - {L"CMAKE_TRIPLET_FILE", triplet_file_path}, - }); - - const std::wstring command = Strings::wformat(LR"(%s)", cmd_launch_cmake); - const auto ec_data = System::cmd_execute_and_capture_output(command); - Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0); - - const std::vector lines = Strings::split(ec_data.output, "\n"); - - PreBuildInfo pre_build_info; - - const auto e = lines.cend(); - auto cur = std::find(lines.cbegin(), e, FLAG_GUID); - if (cur != e) ++cur; - - for (; cur != e; ++cur) - { - auto&& line = *cur; - - const std::vector s = Strings::split(line, "="); - Checks::check_exit(VCPKG_LINE_INFO, - s.size() == 1 || s.size() == 2, - "Expected format is [VARIABLE_NAME=VARIABLE_VALUE], but was [%s]", - line); - - const bool variable_with_no_value = s.size() == 1; - const std::string variable_name = s.at(0); - const std::string variable_value = variable_with_no_value ? Strings::EMPTY : s.at(1); - - if (variable_name == "VCPKG_TARGET_ARCHITECTURE") - { - pre_build_info.target_architecture = variable_value; - continue; - } - - if (variable_name == "VCPKG_CMAKE_SYSTEM_NAME") - { - pre_build_info.cmake_system_name = variable_value; - continue; - } - - if (variable_name == "VCPKG_CMAKE_SYSTEM_VERSION") - { - pre_build_info.cmake_system_version = variable_value; - continue; - } - - if (variable_name == "VCPKG_PLATFORM_TOOLSET") - { - pre_build_info.platform_toolset = - variable_value.empty() ? nullopt : Optional{variable_value}; - continue; - } - - if (variable_name == "VCPKG_VISUAL_STUDIO_PATH") - { - pre_build_info.visual_studio_path = - variable_value.empty() ? nullopt : Optional{variable_value}; - continue; - } - - Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line); - } - - return pre_build_info; - } -} diff --git a/toolsrc/src/vcpkg_Build_BuildPolicy.cpp b/toolsrc/src/vcpkg_Build_BuildPolicy.cpp deleted file mode 100644 index b3bb778dc..000000000 --- a/toolsrc/src/vcpkg_Build_BuildPolicy.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Build.h" -#include "vcpkg_Checks.h" -#include "vcpkg_Enums.h" - -namespace vcpkg::Build -{ - static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage"; - static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs"; - static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT"; - static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder"; - static const std::string NAME_ALLOW_OBSOLETE_MSVCRT = "PolicyAllowObsoleteMsvcrt"; - - const std::string& to_string(BuildPolicy policy) - { - switch (policy) - { - case BuildPolicy::EMPTY_PACKAGE: return NAME_EMPTY_PACKAGE; - case BuildPolicy::DLLS_WITHOUT_LIBS: return NAME_DLLS_WITHOUT_LIBS; - case BuildPolicy::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT; - case BuildPolicy::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER; - case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return NAME_ALLOW_OBSOLETE_MSVCRT; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - CStringView to_cmake_variable(BuildPolicy policy) - { - switch (policy) - { - case BuildPolicy::EMPTY_PACKAGE: return "VCPKG_POLICY_EMPTY_PACKAGE"; - case BuildPolicy::DLLS_WITHOUT_LIBS: return "VCPKG_POLICY_DLLS_WITHOUT_LIBS"; - case BuildPolicy::ONLY_RELEASE_CRT: return "VCPKG_POLICY_ONLY_RELEASE_CRT"; - case BuildPolicy::EMPTY_INCLUDE_FOLDER: return "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER"; - case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return "VCPKG_POLICY_ALLOW_OBSOLETE_MSVCRT"; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } -} diff --git a/toolsrc/src/vcpkg_Checks.cpp b/toolsrc/src/vcpkg_Checks.cpp deleted file mode 100644 index e7c9046a4..000000000 --- a/toolsrc/src/vcpkg_Checks.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "pch.h" - -#include "metrics.h" -#include "vcpkg_Checks.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::Checks -{ - [[noreturn]] static void cleanup_and_exit(const int exit_code) - { - const auto elapsed_us = GlobalState::timer.lock()->microseconds(); - - auto metrics = Metrics::g_metrics.lock(); - metrics->track_metric("elapsed_us", elapsed_us); - GlobalState::debugging = false; - metrics->flush(); - - SetConsoleCP(GlobalState::g_init_console_cp); - SetConsoleOutputCP(GlobalState::g_init_console_output_cp); - - fflush(nullptr); - - ::TerminateProcess(::GetCurrentProcess(), exit_code); - } - - static BOOL ctrl_handler(DWORD fdw_ctrl_type) - { - { - auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->track_property("CtrlHandler", std::to_string(fdw_ctrl_type)); - locked_metrics->track_property("error", "CtrlHandler was fired."); - } - cleanup_and_exit(EXIT_FAILURE); - } - - void register_console_ctrl_handler() - { - SetConsoleCtrlHandler(reinterpret_cast(ctrl_handler), TRUE); - } - - [[noreturn]] void unreachable(const LineInfo& line_info) - { - System::println(System::Color::error, "Error: Unreachable code was reached"); - System::println(System::Color::error, line_info.to_string()); // Always print line_info here -#ifndef NDEBUG - std::abort(); -#else - cleanup_and_exit(EXIT_FAILURE); -#endif - } - - [[noreturn]] void exit_with_code(const LineInfo& line_info, const int exit_code) - { - Debug::println(System::Color::error, line_info.to_string()); - cleanup_and_exit(exit_code); - } - - [[noreturn]] void exit_with_message(const LineInfo& line_info, const CStringView error_message) - { - System::println(System::Color::error, error_message); - exit_fail(line_info); - } - - void check_exit(const LineInfo& line_info, bool expression) - { - if (!expression) - { - exit_with_message(line_info, Strings::EMPTY); - } - } - - void check_exit(const LineInfo& line_info, bool expression, const CStringView error_message) - { - if (!expression) - { - exit_with_message(line_info, error_message); - } - } -} diff --git a/toolsrc/src/vcpkg_Chrono.cpp b/toolsrc/src/vcpkg_Chrono.cpp deleted file mode 100644 index d96f30987..000000000 --- a/toolsrc/src/vcpkg_Chrono.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Checks.h" -#include "vcpkg_Chrono.h" - -namespace vcpkg -{ - static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos) - { - using std::chrono::duration_cast; - using std::chrono::hours; - using std::chrono::microseconds; - using std::chrono::milliseconds; - using std::chrono::minutes; - using std::chrono::nanoseconds; - using std::chrono::seconds; - - const double nanos_as_double = static_cast(nanos.count()); - - if (duration_cast(nanos) > hours()) - { - const auto t = nanos_as_double / duration_cast(hours(1)).count(); - return Strings::format("%.4g h", t); - } - - if (duration_cast(nanos) > minutes()) - { - const auto t = nanos_as_double / duration_cast(minutes(1)).count(); - return Strings::format("%.4g min", t); - } - - if (duration_cast(nanos) > seconds()) - { - const auto t = nanos_as_double / duration_cast(seconds(1)).count(); - return Strings::format("%.4g s", t); - } - - if (duration_cast(nanos) > milliseconds()) - { - const auto t = nanos_as_double / duration_cast(milliseconds(1)).count(); - return Strings::format("%.4g ms", t); - } - - if (duration_cast(nanos) > microseconds()) - { - const auto t = nanos_as_double / duration_cast(microseconds(1)).count(); - return Strings::format("%.4g us", t); - } - - return Strings::format("%.4g ns", nanos_as_double); - } - - ElapsedTime ElapsedTime::create_started() - { - ElapsedTime t; - t.m_start_tick = std::chrono::high_resolution_clock::now(); - return t; - } - - std::string ElapsedTime::to_string() const { return format_time_userfriendly(elapsed()); } -} diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp deleted file mode 100644 index 8dd60a2eb..000000000 --- a/toolsrc/src/vcpkg_Dependencies.cpp +++ /dev/null @@ -1,669 +0,0 @@ -#include "pch.h" - -#include "PackageSpec.h" -#include "Paragraphs.h" -#include "StatusParagraphs.h" -#include "VcpkgPaths.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Files.h" -#include "vcpkg_Graphs.h" -#include "vcpkg_Strings.h" -#include "vcpkg_Util.h" -#include "vcpkglib.h" - -namespace vcpkg::Dependencies -{ - struct FeatureNodeEdges - { - std::vector remove_edges; - std::vector build_edges; - bool plus = false; - }; - - struct Cluster : Util::MoveOnlyBase - { - std::vector status_paragraphs; - Optional source_control_file; - PackageSpec spec; - std::unordered_map edges; - std::unordered_set to_install_features; - std::unordered_set original_features; - bool will_remove = false; - bool transient_uninstalled = true; - RequestType request_type = RequestType::AUTO_SELECTED; - }; - - struct ClusterPtr - { - Cluster* ptr; - - Cluster* operator->() const { return ptr; } - }; - - bool operator==(const ClusterPtr& l, const ClusterPtr& r) { return l.ptr == r.ptr; } -} - -template<> -struct std::hash -{ - size_t operator()(const vcpkg::Dependencies::ClusterPtr& value) const - { - return std::hash()(value.ptr->spec); - } -}; - -namespace vcpkg::Dependencies -{ - struct GraphPlan - { - Graphs::Graph remove_graph; - Graphs::Graph install_graph; - }; - - struct ClusterGraph : Util::MoveOnlyBase - { - explicit ClusterGraph(std::unordered_map&& ports) - : m_ports(std::move(ports)) - { - } - - Cluster& get(const PackageSpec& spec) - { - auto it = m_graph.find(spec); - if (it == m_graph.end()) - { - // Load on-demand from m_ports - auto it_ports = m_ports.find(spec.name()); - if (it_ports != m_ports.end()) - { - auto& clust = m_graph[spec]; - clust.spec = spec; - cluster_from_scf(*it_ports->second, clust); - return clust; - } - return m_graph[spec]; - } - return it->second; - } - - private: - void cluster_from_scf(const SourceControlFile& scf, Cluster& out_cluster) const - { - FeatureNodeEdges core_dependencies; - core_dependencies.build_edges = - filter_dependencies_to_specs(scf.core_paragraph->depends, out_cluster.spec.triplet()); - out_cluster.edges.emplace("core", std::move(core_dependencies)); - - for (const auto& feature : scf.feature_paragraphs) - { - FeatureNodeEdges added_edges; - added_edges.build_edges = filter_dependencies_to_specs(feature->depends, out_cluster.spec.triplet()); - out_cluster.edges.emplace(feature->name, std::move(added_edges)); - } - out_cluster.source_control_file = &scf; - } - - std::unordered_map m_graph; - std::unordered_map m_ports; - }; - - std::vector AnyParagraph::dependencies(const Triplet& triplet) const - { - auto to_package_specs = [&](const std::vector& dependencies_as_string) { - return Util::fmap(dependencies_as_string, [&](const std::string s) { - return PackageSpec::from_name_and_triplet(s, triplet).value_or_exit(VCPKG_LINE_INFO); - }); - }; - - if (auto p = this->status_paragraph.get()) - { - return to_package_specs(p->package.depends); - } - - if (auto p = this->binary_control_file.get()) - { - auto deps = Util::fmap_flatten(p->features, [](const BinaryParagraph& pgh) { return pgh.depends; }); - deps.insert(deps.end(), p->core_paragraph.depends.begin(), p->core_paragraph.depends.end()); - return to_package_specs(deps); - } - - if (auto p = this->source_paragraph.get()) - { - return to_package_specs(filter_dependencies(p->depends, triplet)); - } - - Checks::exit_with_message(VCPKG_LINE_INFO, - "Cannot get dependencies because there was none of: source/binary/status paragraphs"); - } - - std::string to_output_string(RequestType request_type, const CStringView s) - { - switch (request_type) - { - case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s); - case RequestType::USER_REQUESTED: return Strings::format(" %s", s); - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - InstallPlanAction::InstallPlanAction() : plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {} - - InstallPlanAction::InstallPlanAction(const PackageSpec& spec, - const SourceControlFile& any_paragraph, - const std::unordered_set& features, - const RequestType& request_type) - : spec(spec), plan_type(InstallPlanType::BUILD_AND_INSTALL), request_type(request_type), feature_list(features) - { - this->any_paragraph.source_control_file = &any_paragraph; - } - - InstallPlanAction::InstallPlanAction(const PackageSpec& spec, - const std::unordered_set& features, - const RequestType& request_type) - : spec(spec), plan_type(InstallPlanType::ALREADY_INSTALLED), request_type(request_type), feature_list(features) - { - } - - InstallPlanAction::InstallPlanAction(const PackageSpec& spec, - const AnyParagraph& any_paragraph, - const RequestType& request_type) - : spec(spec), any_paragraph(any_paragraph), plan_type(InstallPlanType::UNKNOWN), request_type(request_type) - { - if (auto p = any_paragraph.status_paragraph.get()) - { - this->plan_type = InstallPlanType::ALREADY_INSTALLED; - return; - } - - if (auto p = any_paragraph.binary_control_file.get()) - { - this->plan_type = InstallPlanType::INSTALL; - return; - } - - if (auto p = any_paragraph.source_paragraph.get()) - { - this->plan_type = InstallPlanType::BUILD_AND_INSTALL; - return; - } - } - - std::string InstallPlanAction::displayname() const - { - if (this->feature_list.empty()) - { - return this->spec.to_string(); - } - - const std::string features = Strings::join(",", this->feature_list); - return Strings::format("%s[%s]:%s", this->spec.name(), features, this->spec.triplet()); - } - - bool InstallPlanAction::compare_by_name(const InstallPlanAction* left, const InstallPlanAction* right) - { - return left->spec.name() < right->spec.name(); - } - - RemovePlanAction::RemovePlanAction() : plan_type(RemovePlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {} - - RemovePlanAction::RemovePlanAction(const PackageSpec& spec, - const RemovePlanType& plan_type, - const RequestType& request_type) - : spec(spec), plan_type(plan_type), request_type(request_type) - { - } - - const PackageSpec& AnyAction::spec() const - { - if (const auto p = install_plan.get()) - { - return p->spec; - } - - if (const auto p = remove_plan.get()) - { - return p->spec; - } - - Checks::exit_with_message(VCPKG_LINE_INFO, "Null action"); - } - - bool ExportPlanAction::compare_by_name(const ExportPlanAction* left, const ExportPlanAction* right) - { - return left->spec.name() < right->spec.name(); - } - - ExportPlanAction::ExportPlanAction() : plan_type(ExportPlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {} - - ExportPlanAction::ExportPlanAction(const PackageSpec& spec, - const AnyParagraph& any_paragraph, - const RequestType& request_type) - : spec(spec), any_paragraph(any_paragraph), plan_type(ExportPlanType::UNKNOWN), request_type(request_type) - { - if (auto p = any_paragraph.binary_control_file.get()) - { - this->plan_type = ExportPlanType::ALREADY_BUILT; - return; - } - - if (auto p = any_paragraph.source_paragraph.get()) - { - this->plan_type = ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT; - return; - } - } - - bool RemovePlanAction::compare_by_name(const RemovePlanAction* left, const RemovePlanAction* right) - { - return left->spec.name() < right->spec.name(); - } - - MapPortFile::MapPortFile(const std::unordered_map& map) : ports(map) {} - - const SourceControlFile& MapPortFile::get_control_file(const std::string& spec) const - { - auto scf = ports.find(spec); - if (scf == ports.end()) - { - Checks::exit_fail(VCPKG_LINE_INFO); - } - return scf->second; - } - - PathsPortFile::PathsPortFile(const VcpkgPaths& paths) : ports(paths) {} - - const SourceControlFile& PathsPortFile::get_control_file(const std::string& spec) const - { - auto cache_it = cache.find(spec); - if (cache_it != cache.end()) - { - return cache_it->second; - } - Parse::ParseExpected source_control_file = - Paragraphs::try_load_port(ports.get_filesystem(), ports.port_dir(spec)); - - if (auto scf = source_control_file.get()) - { - auto it = cache.emplace(spec, std::move(*scf->get())); - return it.first->second; - } - print_error_message(source_control_file.error()); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - std::vector create_install_plan(const PortFileProvider& port_file_provider, - const std::vector& specs, - const StatusParagraphs& status_db) - { - struct InstallAdjacencyProvider final : Graphs::AdjacencyProvider - { - const PortFileProvider& port_file_provider; - const StatusParagraphs& status_db; - const std::unordered_set& specs_as_set; - - InstallAdjacencyProvider(const PortFileProvider& port_file_provider, - const StatusParagraphs& s, - const std::unordered_set& specs_as_set) - : port_file_provider(port_file_provider), status_db(s), specs_as_set(specs_as_set) - { - } - - std::vector adjacency_list(const InstallPlanAction& plan) const override - { - if (plan.any_paragraph.status_paragraph.get()) return std::vector{}; - return plan.any_paragraph.dependencies(plan.spec.triplet()); - } - - InstallPlanAction load_vertex_data(const PackageSpec& spec) const override - { - const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() - ? RequestType::USER_REQUESTED - : RequestType::AUTO_SELECTED; - auto it = status_db.find_installed(spec); - if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type}; - return InstallPlanAction{ - spec, - {nullopt, nullopt, *port_file_provider.get_control_file(spec.name()).core_paragraph}, - request_type}; - } - }; - - const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); - std::vector toposort = - Graphs::topological_sort(specs, InstallAdjacencyProvider{port_file_provider, status_db, specs_as_set}); - Util::erase_remove_if(toposort, [](const InstallPlanAction& plan) { - return plan.request_type == RequestType::AUTO_SELECTED && - plan.plan_type == InstallPlanType::ALREADY_INSTALLED; - }); - - return toposort; - } - - std::vector create_remove_plan(const std::vector& specs, - const StatusParagraphs& status_db) - { - struct RemoveAdjacencyProvider final : Graphs::AdjacencyProvider - { - const StatusParagraphs& status_db; - const std::vector& installed_ports; - const std::unordered_set& specs_as_set; - - RemoveAdjacencyProvider(const StatusParagraphs& status_db, - const std::vector& installed_ports, - const std::unordered_set& specs_as_set) - : status_db(status_db), installed_ports(installed_ports), specs_as_set(specs_as_set) - { - } - - std::vector adjacency_list(const RemovePlanAction& plan) const override - { - if (plan.plan_type == RemovePlanType::NOT_INSTALLED) - { - return {}; - } - - const PackageSpec& spec = plan.spec; - std::vector dependents; - for (const StatusParagraph* an_installed_package : installed_ports) - { - if (an_installed_package->package.spec.triplet() != spec.triplet()) continue; - - const std::vector& deps = an_installed_package->package.depends; - if (std::find(deps.begin(), deps.end(), spec.name()) == deps.end()) continue; - - dependents.push_back(an_installed_package->package.spec); - } - - return dependents; - } - - RemovePlanAction load_vertex_data(const PackageSpec& spec) const override - { - const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() - ? RequestType::USER_REQUESTED - : RequestType::AUTO_SELECTED; - const StatusParagraphs::const_iterator it = status_db.find_installed(spec); - if (it == status_db.end()) - { - return RemovePlanAction{spec, RemovePlanType::NOT_INSTALLED, request_type}; - } - return RemovePlanAction{spec, RemovePlanType::REMOVE, request_type}; - } - }; - - const std::vector& installed_ports = get_installed_ports(status_db); - const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); - return Graphs::topological_sort(specs, RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set}); - } - - std::vector create_export_plan(const VcpkgPaths& paths, - const std::vector& specs, - const StatusParagraphs& status_db) - { - struct ExportAdjacencyProvider final : Graphs::AdjacencyProvider - { - const VcpkgPaths& paths; - const StatusParagraphs& status_db; - const std::unordered_set& specs_as_set; - - ExportAdjacencyProvider(const VcpkgPaths& p, - const StatusParagraphs& s, - const std::unordered_set& specs_as_set) - : paths(p), status_db(s), specs_as_set(specs_as_set) - { - } - - std::vector adjacency_list(const ExportPlanAction& plan) const override - { - return plan.any_paragraph.dependencies(plan.spec.triplet()); - } - - ExportPlanAction load_vertex_data(const PackageSpec& spec) const override - { - const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() - ? RequestType::USER_REQUESTED - : RequestType::AUTO_SELECTED; - - Expected maybe_bpgh = Paragraphs::try_load_cached_control_package(paths, spec); - if (auto bcf = maybe_bpgh.get()) - return ExportPlanAction{spec, AnyParagraph{nullopt, std::move(*bcf), nullopt}, request_type}; - - auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec)); - if (auto scf = maybe_scf.get()) - return ExportPlanAction{spec, {nullopt, nullopt, *scf->get()->core_paragraph}, request_type}; - else - print_error_message(maybe_scf.error()); - - Checks::exit_with_message(VCPKG_LINE_INFO, "Could not find package %s", spec); - } - }; - - const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); - std::vector toposort = - Graphs::topological_sort(specs, ExportAdjacencyProvider{paths, status_db, specs_as_set}); - return toposort; - } - - enum class MarkPlusResult - { - FEATURE_NOT_FOUND, - SUCCESS, - }; - - MarkPlusResult mark_plus(const std::string& feature, - Cluster& cluster, - ClusterGraph& pkg_to_cluster, - GraphPlan& graph_plan); - void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan); - - MarkPlusResult mark_plus(const std::string& feature, Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan) - { - if (feature.empty()) - { - // Indicates that core was not specified in the reference - return mark_plus("core", cluster, graph, graph_plan); - } - - auto it = cluster.edges.find(feature); - if (it == cluster.edges.end()) return MarkPlusResult::FEATURE_NOT_FOUND; - - if (cluster.edges[feature].plus) return MarkPlusResult::SUCCESS; - - if (cluster.original_features.find(feature) == cluster.original_features.end()) - { - cluster.transient_uninstalled = true; - } - - if (!cluster.transient_uninstalled) - { - return MarkPlusResult::SUCCESS; - } - cluster.edges[feature].plus = true; - - if (!cluster.original_features.empty()) - { - mark_minus(cluster, graph, graph_plan); - } - - graph_plan.install_graph.add_vertex({&cluster}); - auto& tracked = cluster.to_install_features; - tracked.insert(feature); - - if (feature != "core") - { - // All features implicitly depend on core - auto res = mark_plus("core", cluster, graph, graph_plan); - - // Should be impossible for "core" to not exist - Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS); - } - - for (auto&& depend : cluster.edges[feature].build_edges) - { - auto& depend_cluster = graph.get(depend.spec()); - auto res = mark_plus(depend.feature(), depend_cluster, graph, graph_plan); - - Checks::check_exit(VCPKG_LINE_INFO, - res == MarkPlusResult::SUCCESS, - "Error: Unable to satisfy dependency %s of %s", - depend, - FeatureSpec(cluster.spec, feature)); - - if (&depend_cluster == &cluster) continue; - graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster}); - } - - return MarkPlusResult::SUCCESS; - } - - void mark_minus(Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan) - { - if (cluster.will_remove) return; - cluster.will_remove = true; - - graph_plan.remove_graph.add_vertex({&cluster}); - for (auto&& pair : cluster.edges) - { - auto& remove_edges_edges = pair.second.remove_edges; - for (auto&& depend : remove_edges_edges) - { - auto& depend_cluster = graph.get(depend.spec()); - graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster}); - mark_minus(depend_cluster, graph, graph_plan); - } - } - - cluster.transient_uninstalled = true; - for (auto&& original_feature : cluster.original_features) - { - auto res = mark_plus(original_feature, cluster, graph, graph_plan); - if (res != MarkPlusResult::SUCCESS) - { - System::println(System::Color::warning, - "Warning: could not reinstall feature %s", - FeatureSpec{cluster.spec, original_feature}); - } - } - } - - static ClusterGraph create_feature_install_graph(const std::unordered_map& map, - const StatusParagraphs& status_db) - { - std::unordered_map ptr_map; - for (auto&& p : map) - ptr_map.emplace(p.first, &p.second); - ClusterGraph graph(std::move(ptr_map)); - - auto installed_ports = get_installed_ports(status_db); - - for (auto&& status_paragraph : installed_ports) - { - Cluster& cluster = graph.get(status_paragraph->package.spec); - - cluster.transient_uninstalled = false; - - cluster.status_paragraphs.emplace_back(status_paragraph); - - auto& status_paragraph_feature = status_paragraph->package.feature; - // In this case, empty string indicates the "core" paragraph for a package. - if (status_paragraph_feature.empty()) - { - cluster.original_features.insert("core"); - } - else - { - cluster.original_features.insert(status_paragraph_feature); - } - } - - for (auto&& status_paragraph : installed_ports) - { - auto& spec = status_paragraph->package.spec; - auto& status_paragraph_feature = status_paragraph->package.feature; - auto reverse_edges = FeatureSpec::from_strings_and_triplet(status_paragraph->package.depends, - status_paragraph->package.spec.triplet()); - - for (auto&& dependency : reverse_edges) - { - auto& dep_cluster = graph.get(dependency.spec()); - - auto depends_name = dependency.feature(); - if (depends_name.empty()) depends_name = "core"; - - auto& target_node = dep_cluster.edges[depends_name]; - target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature}); - } - } - return graph; - } - - std::vector create_feature_install_plan(const std::unordered_map& map, - const std::vector& specs, - const StatusParagraphs& status_db) - { - ClusterGraph graph = create_feature_install_graph(map, status_db); - - GraphPlan graph_plan; - for (auto&& spec : specs) - { - Cluster& spec_cluster = graph.get(spec.spec()); - spec_cluster.request_type = RequestType::USER_REQUESTED; - auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan); - - Checks::check_exit( - VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); - - graph_plan.install_graph.add_vertex(ClusterPtr{&spec_cluster}); - } - - Graphs::GraphAdjacencyProvider adjacency_remove_graph(graph_plan.remove_graph.adjacency_list()); - auto remove_vertex_list = graph_plan.remove_graph.vertex_list(); - auto remove_toposort = Graphs::topological_sort(remove_vertex_list, adjacency_remove_graph); - - Graphs::GraphAdjacencyProvider adjacency_install_graph(graph_plan.install_graph.adjacency_list()); - auto insert_vertex_list = graph_plan.install_graph.vertex_list(); - auto insert_toposort = Graphs::topological_sort(insert_vertex_list, adjacency_install_graph); - - std::vector plan; - - for (auto&& p_cluster : remove_toposort) - { - auto scf = *p_cluster->source_control_file.get(); - auto spec = PackageSpec::from_name_and_triplet(scf->core_paragraph->name, p_cluster->spec.triplet()) - .value_or_exit(VCPKG_LINE_INFO); - plan.emplace_back(RemovePlanAction{ - std::move(spec), - RemovePlanType::REMOVE, - p_cluster->request_type, - }); - } - - for (auto&& p_cluster : insert_toposort) - { - if (p_cluster->transient_uninstalled) - { - // If it will be transiently uninstalled, we need to issue a full installation command - auto pscf = p_cluster->source_control_file.value_or_exit(VCPKG_LINE_INFO); - Checks::check_exit(VCPKG_LINE_INFO, pscf != nullptr); - plan.emplace_back(InstallPlanAction{ - p_cluster->spec, - *pscf, - p_cluster->to_install_features, - p_cluster->request_type, - }); - } - else - { - // If the package isn't transitively installed, still include it if the user explicitly requested it - if (p_cluster->request_type != RequestType::USER_REQUESTED) continue; - plan.emplace_back(InstallPlanAction{ - p_cluster->spec, - p_cluster->original_features, - p_cluster->request_type, - }); - } - } - - return plan; - } -} diff --git a/toolsrc/src/vcpkg_Enums.cpp b/toolsrc/src/vcpkg_Enums.cpp deleted file mode 100644 index 51ba9d5dc..000000000 --- a/toolsrc/src/vcpkg_Enums.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Checks.h" -#include "vcpkg_Enums.h" - -namespace vcpkg::Enums -{ - std::string nullvalue_to_string(const CStringView enum_name) { return Strings::format("%s_NULLVALUE", enum_name); } - - [[noreturn]] void nullvalue_used(const LineInfo& line_info, const CStringView enum_name) - { - Checks::exit_with_message(line_info, "NULLVALUE of enum %s was used", enum_name); - } -} diff --git a/toolsrc/src/vcpkg_Files.cpp b/toolsrc/src/vcpkg_Files.cpp deleted file mode 100644 index ad1fcebc6..000000000 --- a/toolsrc/src/vcpkg_Files.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Files.h" -#include "vcpkg_System.h" -#include "vcpkg_Util.h" -#include - -namespace vcpkg::Files -{ - static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])"); - - struct RealFilesystem final : Filesystem - { - virtual Expected read_contents(const fs::path& file_path) const override - { - std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary); - if (file_stream.fail()) - { - return std::make_error_code(std::errc::no_such_file_or_directory); - } - - file_stream.seekg(0, file_stream.end); - auto length = file_stream.tellg(); - file_stream.seekg(0, file_stream.beg); - - if (length > SIZE_MAX) - { - return std::make_error_code(std::errc::file_too_large); - } - - std::string output; - output.resize(static_cast(length)); - file_stream.read(&output[0], length); - file_stream.close(); - - return std::move(output); - } - virtual Expected> read_lines(const fs::path& file_path) const override - { - std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary); - if (file_stream.fail()) - { - return std::make_error_code(std::errc::no_such_file_or_directory); - } - - std::vector output; - std::string line; - while (std::getline(file_stream, line)) - { - output.push_back(line); - } - file_stream.close(); - - return std::move(output); - } - virtual fs::path find_file_recursively_up(const fs::path& starting_dir, - const std::string& filename) const override - { - fs::path current_dir = starting_dir; - for (; !current_dir.empty(); current_dir = current_dir.parent_path()) - { - const fs::path candidate = current_dir / filename; - if (exists(candidate)) - { - break; - } - } - - return current_dir; - } - - virtual std::vector get_files_recursive(const fs::path& dir) const override - { - std::vector ret; - - fs::stdfs::recursive_directory_iterator b(dir), e{}; - for (; b != e; ++b) - { - ret.push_back(b->path()); - } - - return ret; - } - - virtual std::vector get_files_non_recursive(const fs::path& dir) const override - { - std::vector ret; - - fs::stdfs::directory_iterator b(dir), e{}; - for (; b != e; ++b) - { - ret.push_back(b->path()); - } - - return ret; - } - - virtual void write_lines(const fs::path& file_path, const std::vector& lines) override - { - std::fstream output(file_path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); - for (const std::string& line : lines) - { - output << line << "\n"; - } - output.close(); - } - - virtual void rename(const fs::path& oldpath, const fs::path& newpath) override - { - fs::stdfs::rename(oldpath, newpath); - } - virtual bool remove(const fs::path& path) override { return fs::stdfs::remove(path); } - virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); } - virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec) override - { - // Working around the currently buggy remove_all() - std::uintmax_t out = fs::stdfs::remove_all(path, ec); - - for (int i = 0; i < 5 && this->exists(path); i++) - { - using namespace std::chrono_literals; - std::this_thread::sleep_for(i * 100ms); - out += fs::stdfs::remove_all(path, ec); - } - - if (this->exists(path)) - { - System::println(System::Color::warning, - "Some files in %s were unable to be removed. Close any editors operating in this " - "directory and retry.", - path.string()); - } - - return out; - } - virtual bool exists(const fs::path& path) const override { return fs::stdfs::exists(path); } - virtual bool is_directory(const fs::path& path) const override { return fs::stdfs::is_directory(path); } - virtual bool is_regular_file(const fs::path& path) const override { return fs::stdfs::is_regular_file(path); } - virtual bool is_empty(const fs::path& path) const override { return fs::stdfs::is_empty(path); } - virtual bool create_directory(const fs::path& path, std::error_code& ec) override - { - return fs::stdfs::create_directory(path, ec); - } - virtual bool create_directories(const fs::path& path, std::error_code& ec) override - { - return fs::stdfs::create_directories(path, ec); - } - virtual void copy(const fs::path& oldpath, const fs::path& newpath, fs::copy_options opts) override - { - fs::stdfs::copy(oldpath, newpath, opts); - } - virtual bool copy_file(const fs::path& oldpath, - const fs::path& newpath, - fs::copy_options opts, - std::error_code& ec) override - { - return fs::stdfs::copy_file(oldpath, newpath, opts, ec); - } - - virtual fs::file_status status(const fs::path& path, std::error_code& ec) const override - { - return fs::stdfs::status(path, ec); - } - virtual void write_contents(const fs::path& file_path, const std::string& data) override - { - FILE* f = nullptr; - auto ec = _wfopen_s(&f, file_path.native().c_str(), L"wb"); - Checks::check_exit( - VCPKG_LINE_INFO, ec == 0, "Error: Could not open file for writing: %s", file_path.u8string().c_str()); - auto count = fwrite(data.data(), sizeof(data[0]), data.size(), f); - fclose(f); - - Checks::check_exit(VCPKG_LINE_INFO, count == data.size()); - } - }; - - Filesystem& get_real_filesystem() - { - static RealFilesystem real_fs; - return real_fs; - } - - bool has_invalid_chars_for_filesystem(const std::string& s) - { - return std::regex_search(s, FILESYSTEM_INVALID_CHARACTERS_REGEX); - } - - void print_paths(const std::vector& paths) - { - System::println(); - for (const fs::path& p : paths) - { - System::println(" %s", p.generic_string()); - } - System::println(); - } - - std::vector find_from_PATH(const std::wstring& name) - { - const std::wstring cmd = Strings::wformat(L"where.exe %s", name); - auto out = System::cmd_execute_and_capture_output(cmd); - if (out.exit_code != 0) - { - return {}; - } - - return Util::fmap(Strings::split(out.output, "\n"), [](auto&& s) { return fs::path(s); }); - } -} diff --git a/toolsrc/src/vcpkg_GlobalState.cpp b/toolsrc/src/vcpkg_GlobalState.cpp deleted file mode 100644 index 2221222c0..000000000 --- a/toolsrc/src/vcpkg_GlobalState.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "pch.h" - -#include "vcpkg_GlobalState.h" - -namespace vcpkg -{ - Util::LockGuarded GlobalState::timer; - std::atomic GlobalState::debugging = false; - std::atomic GlobalState::feature_packages = false; - - std::atomic GlobalState::g_init_console_cp = 0; - std::atomic GlobalState::g_init_console_output_cp = 0; -} diff --git a/toolsrc/src/vcpkg_Input.cpp b/toolsrc/src/vcpkg_Input.cpp deleted file mode 100644 index f1cac9632..000000000 --- a/toolsrc/src/vcpkg_Input.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "pch.h" - -#include "metrics.h" -#include "vcpkg_Commands.h" -#include "vcpkg_Input.h" -#include "vcpkg_System.h" - -namespace vcpkg::Input -{ - PackageSpec check_and_get_package_spec(const std::string& package_spec_as_string, - const Triplet& default_triplet, - CStringView example_text) - { - const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string); - auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet); - if (const auto spec = expected_spec.get()) - { - return PackageSpec{spec->package_spec}; - } - - // Intentionally show the lowercased string - System::println(System::Color::error, "Error: %s: %s", vcpkg::to_string(expected_spec.error()), as_lowercase); - System::print(example_text); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - void check_triplet(const Triplet& t, const VcpkgPaths& paths) - { - if (!paths.is_valid_triplet(t)) - { - System::println(System::Color::error, "Error: invalid triplet: %s", t); - Metrics::g_metrics.lock()->track_property("error", "invalid triplet: " + t.to_string()); - Commands::Help::help_topic_valid_triplet(paths); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - FullPackageSpec check_and_get_full_package_spec(const std::string& full_package_spec_as_string, - const Triplet& default_triplet, - CStringView example_text) - { - const std::string as_lowercase = Strings::ascii_to_lowercase(full_package_spec_as_string); - auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet); - if (const auto spec = expected_spec.get()) - { - return *spec; - } - - // Intentionally show the lowercased string - System::println(System::Color::error, "Error: %s: %s", vcpkg::to_string(expected_spec.error()), as_lowercase); - System::print(example_text); - Checks::exit_fail(VCPKG_LINE_INFO); - } -} diff --git a/toolsrc/src/vcpkg_Parse.cpp b/toolsrc/src/vcpkg_Parse.cpp deleted file mode 100644 index e671a1a05..000000000 --- a/toolsrc/src/vcpkg_Parse.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Checks.h" -#include "vcpkg_Maps.h" -#include "vcpkg_Parse.h" - -namespace vcpkg::Parse -{ - static Optional remove_field(std::unordered_map* fields, - const std::string& fieldname) - { - auto it = fields->find(fieldname); - if (it == fields->end()) - { - return nullopt; - } - - const std::string value = std::move(it->second); - fields->erase(it); - return value; - } - - void ParagraphParser::required_field(const std::string& fieldname, std::string& out) - { - auto maybe_field = remove_field(&fields, fieldname); - if (const auto field = maybe_field.get()) - out = std::move(*field); - else - missing_fields.push_back(fieldname); - } - std::string ParagraphParser::optional_field(const std::string& fieldname) const - { - return remove_field(&fields, fieldname).value_or(Strings::EMPTY); - } - std::unique_ptr ParagraphParser::error_info(const std::string& name) const - { - if (!fields.empty() || !missing_fields.empty()) - { - auto err = std::make_unique(); - err->name = name; - err->extra_fields = Maps::extract_keys(fields); - err->missing_fields = std::move(missing_fields); - return err; - } - return nullptr; - } - - std::vector parse_comma_list(const std::string& str) - { - if (str.empty()) - { - return {}; - } - - std::vector out; - - size_t cur = 0; - do - { - auto pos = str.find(',', cur); - if (pos == std::string::npos) - { - out.push_back(str.substr(cur)); - break; - } - out.push_back(str.substr(cur, pos - cur)); - - // skip comma and space - ++pos; - if (str[pos] == ' ') - { - ++pos; - } - - cur = pos; - } while (cur != std::string::npos); - - return out; - } -} diff --git a/toolsrc/src/vcpkg_Strings.cpp b/toolsrc/src/vcpkg_Strings.cpp deleted file mode 100644 index bbe6b29cd..000000000 --- a/toolsrc/src/vcpkg_Strings.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Checks.h" -#include "vcpkg_Strings.h" -#include "vcpkg_Util.h" - -namespace vcpkg::Strings::details -{ - // To disambiguate between two overloads - static const auto isspace = [](const char c) { return std::isspace(c); }; - - // Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower() - static char tolower_char(const char c) { return static_cast(std::tolower(c)); } - -#if defined(_WIN32) - static _locale_t& c_locale() - { - static _locale_t c_locale_impl = _create_locale(LC_ALL, "C"); - return c_locale_impl; - } -#endif - - std::string format_internal(const char* fmtstr, ...) - { - va_list args; - va_start(args, fmtstr); - -#if defined(_WIN32) - const int sz = _vscprintf_l(fmtstr, c_locale(), args); -#else - const int sz = vsnprintf(nullptr, 0, fmtstr, args); -#endif - Checks::check_exit(VCPKG_LINE_INFO, sz > 0); - - std::string output(sz, '\0'); - -#if defined(_WIN32) - _vsnprintf_s_l(&output.at(0), output.size() + 1, output.size(), fmtstr, c_locale(), args); -#else - vsnprintf(&output.at(0), output.size() + 1, fmtstr, args); -#endif - va_end(args); - - return output; - } - - std::wstring wformat_internal(const wchar_t* fmtstr, ...) - { - va_list args; - va_start(args, fmtstr); - -#if defined(_WIN32) - const int sz = _vscwprintf_l(fmtstr, c_locale(), args); -#else - const int sz = vswprintf(nullptr, 0, fmtstr, args); -#endif - Checks::check_exit(VCPKG_LINE_INFO, sz > 0); - - std::wstring output(sz, L'\0'); - -#if defined(_WIN32) - _vsnwprintf_s_l(&output.at(0), output.size() + 1, output.size(), fmtstr, c_locale(), args); -#else - vswprintf(&output.at(0), output.size() + 1, fmtstr, args); -#endif - va_end(args); - - return output; - } -} - -namespace vcpkg::Strings -{ - std::wstring to_utf16(const CStringView s) - { - const int size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, nullptr, 0); - std::wstring output; - output.resize(size - 1); - MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, output.data(), size - 1); - return output; - } - - std::string to_utf8(const CWStringView w) - { - const int size = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, nullptr, 0, nullptr, nullptr); - std::string output; - output.resize(size - 1); - WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, output.data(), size - 1, nullptr, nullptr); - return output; - } - - std::string::const_iterator case_insensitive_ascii_find(const std::string& s, const std::string& pattern) - { - const std::string pattern_as_lower_case(ascii_to_lowercase(pattern)); - return search(s.begin(), - s.end(), - pattern_as_lower_case.begin(), - pattern_as_lower_case.end(), - [](const char a, const char b) { return details::tolower_char(a) == b; }); - } - - bool case_insensitive_ascii_contains(const std::string& s, const std::string& pattern) - { - return case_insensitive_ascii_find(s, pattern) != s.end(); - } - - int case_insensitive_ascii_compare(const CStringView left, const CStringView right) - { - return _stricmp(left.c_str(), right.c_str()); - } - - std::string ascii_to_lowercase(const std::string& input) - { - std::string output(input); - std::transform(output.begin(), output.end(), output.begin(), &details::tolower_char); - return output; - } - - bool case_insensitive_ascii_starts_with(const std::string& s, const std::string& pattern) - { - return _strnicmp(s.c_str(), pattern.c_str(), pattern.size()) == 0; - } - - void trim(std::string* s) - { - s->erase(std::find_if_not(s->rbegin(), s->rend(), details::isspace).base(), s->end()); - s->erase(s->begin(), std::find_if_not(s->begin(), s->end(), details::isspace)); - } - - std::string trimmed(const std::string& s) - { - auto whitespace_back = std::find_if_not(s.rbegin(), s.rend(), details::isspace).base(); - auto whitespace_front = std::find_if_not(s.begin(), whitespace_back, details::isspace); - return std::string(whitespace_front, whitespace_back); - } - - void trim_all_and_remove_whitespace_strings(std::vector* strings) - { - for (std::string& s : *strings) - { - trim(&s); - } - - Util::erase_remove_if(*strings, [](const std::string& s) { return s.empty(); }); - } - - std::vector split(const std::string& s, const std::string& delimiter) - { - std::vector output; - - if (delimiter.empty()) - { - output.push_back(s); - return output; - } - - const size_t delimiter_length = delimiter.length(); - size_t i = 0; - for (size_t pos = s.find(delimiter); pos != std::string::npos; pos = s.find(delimiter, pos)) - { - output.push_back(s.substr(i, pos - i)); - pos += delimiter_length; - i = pos; - } - - // Add the rest of the string after the last delimiter, unless there is nothing after it - if (i != s.length()) - { - output.push_back(s.substr(i, s.length())); - } - - return output; - } -} diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp deleted file mode 100644 index 3b4c440d8..000000000 --- a/toolsrc/src/vcpkg_System.cpp +++ /dev/null @@ -1,349 +0,0 @@ -#include "pch.h" - -#include "vcpkg_Checks.h" -#include "vcpkg_GlobalState.h" -#include "vcpkg_System.h" -#include "vcpkglib.h" - -namespace vcpkg::System -{ - tm get_current_date_time() - { - using std::chrono::system_clock; - std::time_t now_time = system_clock::to_time_t(system_clock::now()); - tm parts; - localtime_s(&parts, &now_time); - return parts; - } - - fs::path get_exe_path_of_current_process() - { - wchar_t buf[_MAX_PATH]; - const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH); - if (bytes == 0) std::abort(); - return fs::path(buf, buf + bytes); - } - - Optional to_cpu_architecture(CStringView arch) - { - if (Strings::case_insensitive_ascii_compare(arch, "x86") == 0) return CPUArchitecture::X86; - if (Strings::case_insensitive_ascii_compare(arch, "x64") == 0) return CPUArchitecture::X64; - if (Strings::case_insensitive_ascii_compare(arch, "amd64") == 0) return CPUArchitecture::X64; - if (Strings::case_insensitive_ascii_compare(arch, "arm") == 0) return CPUArchitecture::ARM; - if (Strings::case_insensitive_ascii_compare(arch, "arm64") == 0) return CPUArchitecture::ARM64; - return nullopt; - } - - CPUArchitecture get_host_processor() - { - auto w6432 = get_environment_variable(L"PROCESSOR_ARCHITEW6432"); - if (const auto p = w6432.get()) return to_cpu_architecture(Strings::to_utf8(*p)).value_or_exit(VCPKG_LINE_INFO); - - const auto procarch = get_environment_variable(L"PROCESSOR_ARCHITECTURE").value_or_exit(VCPKG_LINE_INFO); - return to_cpu_architecture(Strings::to_utf8(procarch)).value_or_exit(VCPKG_LINE_INFO); - } - - std::vector get_supported_host_architectures() - { - std::vector supported_architectures; - supported_architectures.push_back(get_host_processor()); - - // AMD64 machines support to run x86 applications - if (supported_architectures.back() == CPUArchitecture::X64) - { - supported_architectures.push_back(CPUArchitecture::X86); - } - - return supported_architectures; - } - - int cmd_execute_clean(const CWStringView cmd_line) - { - static const std::wstring SYSTEM_ROOT = get_environment_variable(L"SystemRoot").value_or_exit(VCPKG_LINE_INFO); - static const std::wstring SYSTEM_32 = SYSTEM_ROOT + LR"(\system32)"; - static const std::wstring NEW_PATH = Strings::wformat( - LR"(Path=%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)", SYSTEM_32, SYSTEM_ROOT, SYSTEM_32, SYSTEM_32); - - std::vector env_wstrings = { - L"ALLUSERSPROFILE", - L"APPDATA", - L"CommonProgramFiles", - L"CommonProgramFiles(x86)", - L"CommonProgramW6432", - L"COMPUTERNAME", - L"ComSpec", - L"HOMEDRIVE", - L"HOMEPATH", - L"LOCALAPPDATA", - L"LOGONSERVER", - L"NUMBER_OF_PROCESSORS", - L"OS", - L"PATHEXT", - L"PROCESSOR_ARCHITECTURE", - L"PROCESSOR_ARCHITEW6432", - L"PROCESSOR_IDENTIFIER", - L"PROCESSOR_LEVEL", - L"PROCESSOR_REVISION", - L"ProgramData", - L"ProgramFiles", - L"ProgramFiles(x86)", - L"ProgramW6432", - L"PROMPT", - L"PSModulePath", - L"PUBLIC", - L"SystemDrive", - L"SystemRoot", - L"TEMP", - L"TMP", - L"USERDNSDOMAIN", - L"USERDOMAIN", - L"USERDOMAIN_ROAMINGPROFILE", - L"USERNAME", - L"USERPROFILE", - L"windir", - // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe - L"HTTP_PROXY", - L"HTTPS_PROXY", - // Enables find_package(CUDA) in CMake - L"CUDA_PATH", - }; - - // Flush stdout before launching external process - fflush(nullptr); - - std::wstring env_cstr; - - for (auto&& env_wstring : env_wstrings) - { - const Optional value = System::get_environment_variable(env_wstring); - const auto v = value.get(); - if (!v || v->empty()) continue; - - env_cstr.append(env_wstring); - env_cstr.push_back(L'='); - env_cstr.append(*v); - env_cstr.push_back(L'\0'); - } - - env_cstr.append(NEW_PATH); - env_cstr.push_back(L'\0'); - - STARTUPINFOW startup_info; - memset(&startup_info, 0, sizeof(STARTUPINFOW)); - startup_info.cb = sizeof(STARTUPINFOW); - - PROCESS_INFORMATION process_info; - memset(&process_info, 0, sizeof(PROCESS_INFORMATION)); - - // Basically we are wrapping it in quotes - std::wstring actual_cmd_line = Strings::wformat(LR"###(cmd.exe /c "%s")###", cmd_line); - Debug::println("CreateProcessW(%s)", Strings::to_utf8(actual_cmd_line)); - bool succeeded = TRUE == CreateProcessW(nullptr, - actual_cmd_line.data(), - nullptr, - nullptr, - FALSE, - BELOW_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, - env_cstr.data(), - nullptr, - &startup_info, - &process_info); - - Checks::check_exit(VCPKG_LINE_INFO, succeeded, "Process creation failed with error code: %lu", GetLastError()); - - CloseHandle(process_info.hThread); - - const DWORD result = WaitForSingleObject(process_info.hProcess, INFINITE); - Checks::check_exit(VCPKG_LINE_INFO, result != WAIT_FAILED, "WaitForSingleObject failed"); - - DWORD exit_code = 0; - GetExitCodeProcess(process_info.hProcess, &exit_code); - - Debug::println("CreateProcessW() returned %lu", exit_code); - return static_cast(exit_code); - } - - int cmd_execute(const CWStringView cmd_line) - { - // Flush stdout before launching external process - fflush(nullptr); - - // Basically we are wrapping it in quotes - const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); - Debug::println("_wsystem(%s)", Strings::to_utf8(actual_cmd_line)); - const int exit_code = _wsystem(actual_cmd_line.c_str()); - Debug::println("_wsystem() returned %d", exit_code); - return exit_code; - } - - // On Win7, output from powershell calls contain a byte order mark, so we strip it out if it is present - static void remove_byte_order_marks(std::wstring* s) - { - const wchar_t* a = s->c_str(); - // This is the UTF-8 byte-order mark - while (s->size() >= 3 && a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF) - { - s->erase(0, 3); - } - } - - ExitCodeAndOutput cmd_execute_and_capture_output(const CWStringView cmd_line) - { - // Flush stdout before launching external process - fflush(stdout); - - const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s 2>&1")###", cmd_line); - - Debug::println("_wpopen(%s)", Strings::to_utf8(actual_cmd_line)); - std::wstring output; - wchar_t buf[1024]; - const auto pipe = _wpopen(actual_cmd_line.c_str(), L"r"); - if (pipe == nullptr) - { - return {1, Strings::to_utf8(output)}; - } - while (fgetws(buf, 1024, pipe)) - { - output.append(buf); - } - if (!feof(pipe)) - { - return {1, Strings::to_utf8(output)}; - } - - const auto ec = _pclose(pipe); - Debug::println("_pclose() returned %d", ec); - remove_byte_order_marks(&output); - return {ec, Strings::to_utf8(output)}; - } - - std::wstring create_powershell_script_cmd(const fs::path& script_path, const CWStringView args) - { - // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - return Strings::wformat( - LR"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.native(), args); - } - - void println() { println(Strings::EMPTY); } - - void print(const CStringView message) { fputs(message.c_str(), stdout); } - - void println(const CStringView message) - { - print(message); - putchar('\n'); - } - - void print(const Color c, const CStringView message) - { - const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); - - CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info{}; - GetConsoleScreenBufferInfo(console_handle, &console_screen_buffer_info); - const auto original_color = console_screen_buffer_info.wAttributes; - - SetConsoleTextAttribute(console_handle, static_cast(c) | (original_color & 0xF0)); - print(message); - SetConsoleTextAttribute(console_handle, original_color); - } - - void println(const Color c, const CStringView message) - { - print(c, message); - putchar('\n'); - } - - Optional get_environment_variable(const CWStringView varname) noexcept - { - const auto sz = GetEnvironmentVariableW(varname.c_str(), nullptr, 0); - if (sz == 0) return nullopt; - - std::wstring ret(sz, L'\0'); - - Checks::check_exit(VCPKG_LINE_INFO, MAXDWORD >= ret.size()); - const auto sz2 = GetEnvironmentVariableW(varname.c_str(), ret.data(), static_cast(ret.size())); - Checks::check_exit(VCPKG_LINE_INFO, sz2 + 1 == sz); - ret.pop_back(); - return ret; - } - - static bool is_string_keytype(DWORD hkey_type) - { - return hkey_type == REG_SZ || hkey_type == REG_MULTI_SZ || hkey_type == REG_EXPAND_SZ; - } - - Optional get_registry_string(HKEY base, const CWStringView sub_key, const CWStringView valuename) - { - HKEY k = nullptr; - const LSTATUS ec = RegOpenKeyExW(base, sub_key.c_str(), NULL, KEY_READ, &k); - if (ec != ERROR_SUCCESS) return nullopt; - - DWORD dw_buffer_size = 0; - DWORD dw_type = 0; - auto rc = RegQueryValueExW(k, valuename.c_str(), nullptr, &dw_type, nullptr, &dw_buffer_size); - if (rc != ERROR_SUCCESS || !is_string_keytype(dw_type) || dw_buffer_size == 0 || - dw_buffer_size % sizeof(wchar_t) != 0) - return nullopt; - std::wstring ret; - ret.resize(dw_buffer_size / sizeof(wchar_t)); - - rc = RegQueryValueExW( - k, valuename.c_str(), nullptr, &dw_type, reinterpret_cast(ret.data()), &dw_buffer_size); - if (rc != ERROR_SUCCESS || !is_string_keytype(dw_type) || dw_buffer_size != sizeof(wchar_t) * ret.size()) - return nullopt; - - ret.pop_back(); // remove extra trailing null byte - return ret; - } - - static const fs::path& get_program_files() - { - static const fs::path PATH = System::get_environment_variable(L"PROGRAMFILES").value_or_exit(VCPKG_LINE_INFO); - return PATH; - } - - const fs::path& get_program_files_32_bit() - { - static const fs::path PATH = []() -> fs::path { - auto value = System::get_environment_variable(L"ProgramFiles(x86)"); - if (auto v = value.get()) - { - return std::move(*v); - } - return get_program_files(); - }(); - return PATH; - } - - const fs::path& get_program_files_platform_bitness() - { - static const fs::path PATH = []() -> fs::path { - auto value = System::get_environment_variable(L"ProgramW6432"); - if (auto v = value.get()) - { - return std::move(*v); - } - return get_program_files(); - }(); - return PATH; - } -} - -namespace vcpkg::Debug -{ - void println(const CStringView message) - { - if (GlobalState::debugging) - { - System::println("[DEBUG] %s", message); - } - } - - void println(const System::Color c, const CStringView message) - { - if (GlobalState::debugging) - { - System::println(c, "[DEBUG] %s", message); - } - } -} diff --git a/toolsrc/src/vcpkg_metrics_uploader.cpp b/toolsrc/src/vcpkg_metrics_uploader.cpp deleted file mode 100644 index cef4f4756..000000000 --- a/toolsrc/src/vcpkg_metrics_uploader.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "metrics.h" -#include "vcpkg_Checks.h" -#include "vcpkg_Files.h" -#include - -using namespace vcpkg; - -int WINAPI WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int) -{ - int argCount; - LPWSTR* szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount); - - Checks::check_exit(VCPKG_LINE_INFO, argCount == 2, "Requires exactly one argument, the path to the payload file"); - auto v = Files::get_real_filesystem().read_contents(szArgList[1]).value_or_exit(VCPKG_LINE_INFO); - Metrics::g_metrics.lock()->upload(v); - LocalFree(szArgList); - return 0; -} diff --git a/toolsrc/src/vcpkglib.cpp b/toolsrc/src/vcpkglib.cpp deleted file mode 100644 index 5cdafdbc8..000000000 --- a/toolsrc/src/vcpkglib.cpp +++ /dev/null @@ -1,247 +0,0 @@ -#include "pch.h" - -#include "Paragraphs.h" -#include "metrics.h" -#include "vcpkg_Files.h" -#include "vcpkg_Strings.h" -#include "vcpkg_Util.h" -#include "vcpkglib.h" - -namespace vcpkg -{ - static StatusParagraphs load_current_database(Files::Filesystem& fs, - const fs::path& vcpkg_dir_status_file, - const fs::path& vcpkg_dir_status_file_old) - { - if (!fs.exists(vcpkg_dir_status_file)) - { - if (!fs.exists(vcpkg_dir_status_file_old)) - { - // no status file, use empty db - return StatusParagraphs(); - } - - fs.rename(vcpkg_dir_status_file_old, vcpkg_dir_status_file); - } - - auto pghs = Paragraphs::get_paragraphs(fs, vcpkg_dir_status_file).value_or_exit(VCPKG_LINE_INFO); - - std::vector> status_pghs; - for (auto&& p : pghs) - { - status_pghs.push_back(std::make_unique(std::move(p))); - } - - return StatusParagraphs(std::move(status_pghs)); - } - - StatusParagraphs database_load_check(const VcpkgPaths& paths) - { - auto& fs = paths.get_filesystem(); - - const auto updates_dir = paths.vcpkg_dir_updates; - - std::error_code ec; - fs.create_directory(paths.installed, ec); - fs.create_directory(paths.vcpkg_dir, ec); - fs.create_directory(paths.vcpkg_dir_info, ec); - fs.create_directory(updates_dir, ec); - - const fs::path& status_file = paths.vcpkg_dir_status_file; - const fs::path status_file_old = status_file.parent_path() / "status-old"; - const fs::path status_file_new = status_file.parent_path() / "status-new"; - - StatusParagraphs current_status_db = load_current_database(fs, status_file, status_file_old); - - auto update_files = fs.get_files_non_recursive(updates_dir); - if (update_files.empty()) - { - // updates directory is empty, control file is up-to-date. - return current_status_db; - } - for (auto&& file : update_files) - { - if (!fs.is_regular_file(file)) continue; - if (file.filename() == "incomplete") continue; - - auto pghs = Paragraphs::get_paragraphs(fs, file).value_or_exit(VCPKG_LINE_INFO); - for (auto&& p : pghs) - { - current_status_db.insert(std::make_unique(std::move(p))); - } - } - - fs.write_contents(status_file_new, Strings::serialize(current_status_db)); - - fs.rename(status_file_new, status_file); - - for (auto&& file : update_files) - { - if (!fs.is_regular_file(file)) continue; - - fs.remove(file); - } - - return current_status_db; - } - - void write_update(const VcpkgPaths& paths, const StatusParagraph& p) - { - static int update_id = 0; - auto& fs = paths.get_filesystem(); - - const auto my_update_id = update_id++; - const auto tmp_update_filename = paths.vcpkg_dir_updates / "incomplete"; - const auto update_filename = paths.vcpkg_dir_updates / std::to_string(my_update_id); - - fs.write_contents(tmp_update_filename, Strings::serialize(p)); - fs.rename(tmp_update_filename, update_filename); - } - - static void upgrade_to_slash_terminated_sorted_format(Files::Filesystem& fs, - std::vector* lines, - const fs::path& listfile_path) - { - static bool was_tracked = false; - - if (lines->empty()) - { - return; - } - - if (lines->at(0).back() == '/') - { - return; // File already in the new format - } - - if (!was_tracked) - { - was_tracked = true; - Metrics::g_metrics.lock()->track_property("listfile", "update to new format"); - } - - // The files are sorted such that directories are placed just before the files they contain - // (They are not necessarily sorted alphabetically, e.g. libflac) - // Therefore we can detect the entries that represent directories by comparing every element with the next one - // and checking if the next has a slash immediately after the current one's length - for (size_t i = 0; i < lines->size() - 1; i++) - { - std::string& current_string = lines->at(i); - const std::string& next_string = lines->at(i + 1); - - const size_t potential_slash_char_index = current_string.length(); - // Make sure the index exists first - if (next_string.size() > potential_slash_char_index && next_string.at(potential_slash_char_index) == '/') - { - current_string += '/'; // Mark as a directory - } - } - - // After suffixing the directories with a slash, we can now sort. - // We cannot sort before adding the suffixes because the following (actual example): - /* - x86-windows/include/FLAC <<<<<< This would be separated from its group due to sorting - x86-windows/include/FLAC/all.h - x86-windows/include/FLAC/assert.h - x86-windows/include/FLAC/callback.h - x86-windows/include/FLAC++ - x86-windows/include/FLAC++/all.h - x86-windows/include/FLAC++/decoder.h - x86-windows/include/FLAC++/encoder.h - * - x86-windows/include/FLAC/ <<<<<< This will now be kept with its group when sorting - x86-windows/include/FLAC/all.h - x86-windows/include/FLAC/assert.h - x86-windows/include/FLAC/callback.h - x86-windows/include/FLAC++/ - x86-windows/include/FLAC++/all.h - x86-windows/include/FLAC++/decoder.h - x86-windows/include/FLAC++/encoder.h - */ - // Note that after sorting, the FLAC++/ group will be placed before the FLAC/ group - // The new format is lexicographically sorted - std::sort(lines->begin(), lines->end()); - - // Replace the listfile on disk - const fs::path updated_listfile_path = listfile_path.generic_string() + "_updated"; - fs.write_lines(updated_listfile_path, *lines); - fs.rename(updated_listfile_path, listfile_path); - } - - std::vector get_installed_ports(const StatusParagraphs& status_db) - { - std::vector installed_packages; - for (auto&& pgh : status_db) - { - if (pgh->state != InstallState::INSTALLED || pgh->want != Want::INSTALL) continue; - installed_packages.push_back(pgh.get()); - } - - return installed_packages; - } - - std::vector get_installed_files(const VcpkgPaths& paths, - const StatusParagraphs& status_db) - { - auto& fs = paths.get_filesystem(); - - std::vector installed_files; - - for (const std::unique_ptr& pgh : status_db) - { - if (pgh->state != InstallState::INSTALLED || !pgh->package.feature.empty()) - { - continue; - } - - const fs::path listfile_path = paths.listfile_path(pgh->package); - std::vector installed_files_of_current_pgh = - fs.read_lines(listfile_path).value_or_exit(VCPKG_LINE_INFO); - Strings::trim_all_and_remove_whitespace_strings(&installed_files_of_current_pgh); - upgrade_to_slash_terminated_sorted_format(fs, &installed_files_of_current_pgh, listfile_path); - - // Remove the directories - Util::erase_remove_if(installed_files_of_current_pgh, - [](const std::string& file) { return file.back() == '/'; }); - - StatusParagraphAndAssociatedFiles pgh_and_files = { - *pgh, SortedVector(std::move(installed_files_of_current_pgh))}; - installed_files.push_back(std::move(pgh_and_files)); - } - - return installed_files; - } - - CMakeVariable::CMakeVariable(const CWStringView varname, const wchar_t* varvalue) - : s(Strings::wformat(LR"("-D%s=%s")", varname, varvalue)) - { - } - CMakeVariable::CMakeVariable(const CWStringView varname, const std::string& varvalue) - : CMakeVariable(varname, Strings::to_utf16(varvalue).c_str()) - { - } - CMakeVariable::CMakeVariable(const CWStringView varname, const std::wstring& varvalue) - : CMakeVariable(varname, varvalue.c_str()) - { - } - CMakeVariable::CMakeVariable(const CWStringView varname, const fs::path& path) - : CMakeVariable(varname, path.generic_wstring()) - { - } - - std::wstring make_cmake_cmd(const fs::path& cmake_exe, - const fs::path& cmake_script, - const std::vector& pass_variables) - { - const std::wstring cmd_cmake_pass_variables = Strings::join(L" ", pass_variables, [](auto&& v) { return v.s; }); - return Strings::wformat( - LR"("%s" %s -P "%s")", cmake_exe.native(), cmd_cmake_pass_variables, cmake_script.generic_wstring()); - } - - std::string shorten_text(const std::string& desc, size_t length) - { - Checks::check_exit(VCPKG_LINE_INFO, length >= 3); - auto simple_desc = std::regex_replace(desc, std::regex("\\s+"), " "); - return simple_desc.size() <= length ? simple_desc : simple_desc.substr(0, length - 3) + "..."; - } -} diff --git a/toolsrc/src/vcpkgmetricsuploader.cpp b/toolsrc/src/vcpkgmetricsuploader.cpp new file mode 100644 index 000000000..2239fe750 --- /dev/null +++ b/toolsrc/src/vcpkgmetricsuploader.cpp @@ -0,0 +1,20 @@ +#include + +#include +#include + +#include + +using namespace vcpkg; + +int WINAPI WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int) +{ + int argCount; + LPWSTR* szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount); + + Checks::check_exit(VCPKG_LINE_INFO, argCount == 2, "Requires exactly one argument, the path to the payload file"); + auto v = Files::get_real_filesystem().read_contents(szArgList[1]).value_or_exit(VCPKG_LINE_INFO); + Metrics::g_metrics.lock()->upload(v); + LocalFree(szArgList); + return 0; +} -- cgit v1.2.3 From bea4c2ff4936f22b4024c2afef5e403533c7b291 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Fri, 13 Oct 2017 20:58:00 -0700 Subject: [vcpkg] Begin refactor to use CommandStructure to represent command parsing --- toolsrc/src/vcpkg/commands.autocomplete.cpp | 77 ++++++++++++++++++----------- toolsrc/src/vcpkg/install.cpp | 38 +++++++++++--- toolsrc/src/vcpkg/remove.cpp | 37 ++++++++++++-- 3 files changed, 112 insertions(+), 40 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp index b8c633142..2e191b428 100644 --- a/toolsrc/src/vcpkg/commands.autocomplete.cpp +++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include namespace vcpkg::Commands::Autocomplete @@ -47,6 +49,7 @@ namespace vcpkg::Commands::Autocomplete { const SortedVector sorted_results(results); System::println(Strings::join("\n", sorted_results)); + Checks::exit_success(line_info); } @@ -63,21 +66,23 @@ namespace vcpkg::Commands::Autocomplete { const std::string requested_command = match[1].str(); - std::vector valid_commands = {"install", - "search", - "remove", - "list", - "update", - "hash", - "help", - "integrate", - "export", - "edit", - "create", - "owns", - "cache", - "version", - "contact"}; + std::vector valid_commands = { + "install", + "search", + "remove", + "list", + "update", + "hash", + "help", + "integrate", + "export", + "edit", + "create", + "owns", + "cache", + "version", + "contact", + }; Util::unstable_keep_if(valid_commands, [&](const std::string& s) { return Strings::case_insensitive_ascii_starts_with(s, requested_command); @@ -86,22 +91,36 @@ namespace vcpkg::Commands::Autocomplete output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(valid_commands)); } - // Handles vcpkg install - if (std::regex_match(to_autocomplete, match, std::regex{R"###(^install.* (\S*)$)###"})) + struct CommandEntry { - const std::string start_with = match[1].str(); - auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); - auto& source_paragraphs = sources_and_errors.paragraphs; - output_sorted_results_and_exit(VCPKG_LINE_INFO, autocomplete_install(source_paragraphs, start_with)); - } - - // Handles vcpkg remove - if (std::regex_match(to_autocomplete, match, std::regex{R"###(^remove.* (\S*)$)###"})) + CStringView regex; + const CommandStructure& structure; + }; + static constexpr CommandEntry commands[] = { + {R"###(^install\s(.*\s|)(\S*)$)###", Install::INSTALL_COMMAND_STRUCTURE}, + {R"###(^remove\s(.*\s|)(\S*)$)###", Remove::REMOVE_COMMAND_STRUCTURE}, + }; + + for (auto&& command : commands) { - const std::string start_with = match[1].str(); - const StatusParagraphs status_db = database_load_check(paths); - const std::vector installed_packages = get_installed_ports(status_db); - output_sorted_results_and_exit(VCPKG_LINE_INFO, autocomplete_remove(installed_packages, start_with)); + if (std::regex_match(to_autocomplete, match, std::regex{command.regex.c_str()})) + { + auto prefix = match[2].str(); + std::vector v; + if (Strings::case_insensitive_ascii_starts_with(prefix, "-")) + { + v = Util::fmap(command.structure.switches, [](auto&& s) -> std::string { return s; }); + } + else + { + v = command.structure.valid_arguments(paths); + } + + Util::unstable_keep_if( + v, [&](const std::string& s) { return Strings::case_insensitive_ascii_starts_with(s, prefix); }); + + output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(v)); + } } Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 28d6d1cc6..6a564b00c 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -525,14 +525,40 @@ namespace vcpkg::Install Checks::exit_success(VCPKG_LINE_INFO); } - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + static const std::string OPTION_DRY_RUN = "--dry-run"; + static const std::string OPTION_USE_HEAD_VERSION = "--head"; + static const std::string OPTION_NO_DOWNLOADS = "--no-downloads"; + static const std::string OPTION_RECURSE = "--recurse"; + static const std::string OPTION_KEEP_GOING = "--keep-going"; + + static const std::array INSTALL_SWITCHES = { + OPTION_DRY_RUN, + OPTION_USE_HEAD_VERSION, + OPTION_NO_DOWNLOADS, + OPTION_RECURSE, + OPTION_KEEP_GOING, + }; + static const std::array INSTALL_SETTINGS; + + static std::vector valid_arguments(const VcpkgPaths& paths) { - static const std::string OPTION_DRY_RUN = "--dry-run"; - static const std::string OPTION_USE_HEAD_VERSION = "--head"; - static const std::string OPTION_NO_DOWNLOADS = "--no-downloads"; - static const std::string OPTION_RECURSE = "--recurse"; - static const std::string OPTION_KEEP_GOING = "--keep-going"; + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + + return Util::fmap(sources_and_errors.paragraphs, + [](auto&& pgh) -> std::string { return pgh->core_paragraph->name; }); + } + const CommandStructure INSTALL_COMMAND_STRUCTURE = { + "install zlib zlib:x64-windows curl boost", + 1, + SIZE_MAX, + INSTALL_SWITCHES, + INSTALL_SETTINGS, + &valid_arguments, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { // input sanitization static const std::string EXAMPLE = Help::create_example_string("install zlib zlib:x64-windows curl boost"); args.check_min_arg_count(1, EXAMPLE); diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index 3751566f7..30b3d6cd8 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -163,13 +163,40 @@ namespace vcpkg::Remove } } + static const std::string OPTION_PURGE = "--purge"; + static const std::string OPTION_NO_PURGE = "--no-purge"; + static const std::string OPTION_RECURSE = "--recurse"; + static const std::string OPTION_DRY_RUN = "--dry-run"; + static const std::string OPTION_OUTDATED = "--outdated"; + + static const std::array REMOVE_SWITCHES = { + OPTION_PURGE, + OPTION_NO_PURGE, + OPTION_RECURSE, + OPTION_DRY_RUN, + OPTION_OUTDATED, + }; + static const std::array REMOVE_SETTINGS; + + static std::vector valid_arguments(const VcpkgPaths& paths) + { + const StatusParagraphs status_db = database_load_check(paths); + const std::vector installed_packages = get_installed_ports(status_db); + + return Util::fmap(installed_packages, [](auto&& pgh) -> std::string { return pgh->package.spec.to_string(); }); + } + + const CommandStructure REMOVE_COMMAND_STRUCTURE = { + "remove zlib zlib:x64-windows curl boost", + 1, + SIZE_MAX, + REMOVE_SWITCHES, + REMOVE_SETTINGS, + &valid_arguments, + }; + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) { - static const std::string OPTION_PURGE = "--purge"; - static const std::string OPTION_NO_PURGE = "--no-purge"; - static const std::string OPTION_RECURSE = "--recurse"; - static const std::string OPTION_DRY_RUN = "--dry-run"; - static const std::string OPTION_OUTDATED = "--outdated"; static const std::string EXAMPLE = Help::create_example_string("remove zlib zlib:x64-windows curl boost"); const std::unordered_set options = args.check_and_get_optional_command_arguments( {OPTION_PURGE, OPTION_NO_PURGE, OPTION_RECURSE, OPTION_DRY_RUN, OPTION_OUTDATED}); -- cgit v1.2.3 From bf41a93b9de45f5d407fd03a36096f2cd37902b4 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Sat, 14 Oct 2017 02:07:42 -0700 Subject: [vcpkg] Prefer the current vcpkg directory over the one the vcpkg.exe is located in --- toolsrc/src/vcpkg.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index 748b4f0ee..f1a86ae22 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -74,8 +74,14 @@ static void inner(const VcpkgCmdArguments& args) } else { - vcpkg_root_dir = Files::get_real_filesystem().find_file_recursively_up( - fs::stdfs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root"); + const fs::path current_path = fs::stdfs::current_path(); + vcpkg_root_dir = Files::get_real_filesystem().find_file_recursively_up(current_path, ".vcpkg-root"); + + if (vcpkg_root_dir.empty()) + { + vcpkg_root_dir = Files::get_real_filesystem().find_file_recursively_up( + fs::stdfs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root"); + } } } @@ -88,6 +94,7 @@ static void inner(const VcpkgCmdArguments& args) vcpkg_root_dir.string(), expected_paths.error().message()); const VcpkgPaths paths = expected_paths.value_or_exit(VCPKG_LINE_INFO); + const int exit_code = _wchdir(paths.root.c_str()); Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Changing the working dir failed"); Commands::Version::warn_if_vcpkg_version_mismatch(paths); -- cgit v1.2.3 From b73327558ba79354769961a8c3f68e61fe9b673e Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Sat, 14 Oct 2017 02:07:56 -0700 Subject: `integrate` Fix quotes in command (in case of space in the path) --- toolsrc/src/vcpkg/commands.integrate.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp index 7f6639222..85b567adb 100644 --- a/toolsrc/src/vcpkg/commands.integrate.cpp +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -230,13 +230,14 @@ namespace vcpkg::Commands::Integrate } System::println(System::Color::success, "Applied user-wide integration for this vcpkg root."); const fs::path cmake_toolchain = paths.buildsystems / "vcpkg.cmake"; - System::println("\n" - "All MSBuild C++ projects can now #include any installed libraries.\n" - "Linking will be handled automatically.\n" - "Installing new libraries will make them instantly available.\n" - "\n" - "CMake projects should use -DCMAKE_TOOLCHAIN_FILE=%s", - cmake_toolchain.generic_string()); + System::println( + R"( +All MSBuild C++ projects can now #include any installed libraries. +Linking will be handled automatically. +Installing new libraries will make them instantly available. + +CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")", + cmake_toolchain.generic_string()); Checks::exit_success(VCPKG_LINE_INFO); } -- cgit v1.2.3 From 58fd38c8200ce4c96ebe1aae3f85b324456b0ff7 Mon Sep 17 00:00:00 2001 From: Robert Schumacher Date: Sat, 14 Oct 2017 02:16:55 -0700 Subject: [vcpkg-autocomplete] Add edit completion --- toolsrc/src/vcpkg/commands.autocomplete.cpp | 5 +++-- toolsrc/src/vcpkg/commands.edit.cpp | 27 +++++++++++++++++++++++++-- toolsrc/src/vcpkg/install.cpp | 2 +- toolsrc/src/vcpkg/remove.cpp | 2 +- 4 files changed, 30 insertions(+), 6 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp index 2e191b428..23c2c2f7e 100644 --- a/toolsrc/src/vcpkg/commands.autocomplete.cpp +++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp @@ -97,8 +97,9 @@ namespace vcpkg::Commands::Autocomplete const CommandStructure& structure; }; static constexpr CommandEntry commands[] = { - {R"###(^install\s(.*\s|)(\S*)$)###", Install::INSTALL_COMMAND_STRUCTURE}, - {R"###(^remove\s(.*\s|)(\S*)$)###", Remove::REMOVE_COMMAND_STRUCTURE}, + {R"###(^install\s(.*\s|)(\S*)$)###", Install::COMMAND_STRUCTURE}, + {R"###(^edit\s(.*\s|)(\S*)$)###", Edit::COMMAND_STRUCTURE}, + {R"###(^remove\s(.*\s|)(\S*)$)###", Remove::COMMAND_STRUCTURE}, }; for (auto&& command : commands) diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp index 9497bd76e..a90ae1bc3 100644 --- a/toolsrc/src/vcpkg/commands.edit.cpp +++ b/toolsrc/src/vcpkg/commands.edit.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace vcpkg::Commands::Edit { @@ -30,10 +31,32 @@ namespace vcpkg::Commands::Edit return output; } - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + static const std::string OPTION_BUILDTREES = "--buildtrees"; + + static const std::array SWITCHES = { + OPTION_BUILDTREES, + }; + static const std::array SETTINGS; + + static std::vector valid_arguments(const VcpkgPaths& paths) { - static const std::string OPTION_BUILDTREES = "--buildtrees"; + auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); + + return Util::fmap(sources_and_errors.paragraphs, + [](auto&& pgh) -> std::string { return pgh->core_paragraph->name; }); + } + + const CommandStructure COMMAND_STRUCTURE = { + "edit zlib", + 1, + 1, + SWITCHES, + SETTINGS, + &valid_arguments, + }; + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { static const fs::path VS_CODE_INSIDERS = fs::path{"Microsoft VS Code Insiders"} / "Code - Insiders.exe"; static const fs::path VS_CODE = fs::path{"Microsoft VS Code"} / "Code.exe"; diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 6a564b00c..26f729a57 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -548,7 +548,7 @@ namespace vcpkg::Install [](auto&& pgh) -> std::string { return pgh->core_paragraph->name; }); } - const CommandStructure INSTALL_COMMAND_STRUCTURE = { + const CommandStructure COMMAND_STRUCTURE = { "install zlib zlib:x64-windows curl boost", 1, SIZE_MAX, diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index 30b3d6cd8..04ddba1e4 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -186,7 +186,7 @@ namespace vcpkg::Remove return Util::fmap(installed_packages, [](auto&& pgh) -> std::string { return pgh->package.spec.to_string(); }); } - const CommandStructure REMOVE_COMMAND_STRUCTURE = { + const CommandStructure COMMAND_STRUCTURE = { "remove zlib zlib:x64-windows curl boost", 1, SIZE_MAX, -- cgit v1.2.3