diff options
| author | LiGuilin <liguilin0522@qq.com> | 2016-10-08 08:34:12 +0800 |
|---|---|---|
| committer | LiGuilin <liguilin0522@qq.com> | 2016-10-08 08:34:12 +0800 |
| commit | c91da2b0c4c3d9218c0b4d1712d744bb35245a61 (patch) | |
| tree | e1ae0664a4f21f3948bde8c8f9f9e55dea0cb11f /toolsrc/src | |
| parent | 280d88b34033ab728e02f725d8d8ff5f9250c6de (diff) | |
| parent | a0f621c0fca2c3de8bd5249f023979b800c543cf (diff) | |
| download | vcpkg-c91da2b0c4c3d9218c0b4d1712d744bb35245a61.tar.gz vcpkg-c91da2b0c4c3d9218c0b4d1712d744bb35245a61.zip | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'toolsrc/src')
35 files changed, 917 insertions, 628 deletions
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp index 274bd879e..48d04f686 100644 --- a/toolsrc/src/BinaryParagraph.cpp +++ b/toolsrc/src/BinaryParagraph.cpp @@ -8,55 +8,55 @@ namespace vcpkg { BinaryParagraph::BinaryParagraph() = default; - BinaryParagraph::BinaryParagraph(const std::unordered_map<std::string, std::string>& fields) + BinaryParagraph::BinaryParagraph(const std::unordered_map<std::string, std::string>& fields) : + version(required_field(fields, "Version")), + description(optional_field(fields, "Description")), + maintainer(optional_field(fields, "Maintainer")) { - details::required_field(fields, name, "Package"); - required_field(fields, version, "Version"); - required_field(fields, target_triplet.value, "Architecture"); + const std::string name = required_field(fields, "Package"); + const triplet target_triplet = triplet::from_canonical_name(required_field(fields, "Architecture")); + this->spec = package_spec::from_name_and_triplet(name, target_triplet).get_or_throw(); + { - std::string multi_arch; - required_field(fields, multi_arch, "Multi-Arch"); + std::string multi_arch = required_field(fields, "Multi-Arch"); Checks::check_throw(multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch); } - optional_field(fields, description, "Description"); - std::string deps; - optional_field(fields, deps, "Depends"); + + std::string deps = optional_field(fields, "Depends"); if (!deps.empty()) { - depends.clear(); - parse_depends(deps, depends); + this->depends.clear(); + this->depends = parse_depends(deps); } - optional_field(fields, maintainer, "Maintainer"); } BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const triplet& target_triplet) { - this->name = spgh.name; + this->spec = package_spec::from_name_and_triplet(spgh.name, target_triplet).get_or_throw(); this->version = spgh.version; this->description = spgh.description; this->maintainer = spgh.maintainer; this->depends = spgh.depends; - this->target_triplet = target_triplet; } std::string BinaryParagraph::displayname() const { - return Strings::format("%s:%s", this->name, this->target_triplet); + return Strings::format("%s:%s", this->spec.name(), this->spec.target_triplet()); } std::string BinaryParagraph::dir() const { - return Strings::format("%s_%s", this->name, this->target_triplet); + return this->spec.dir(); } std::string BinaryParagraph::fullstem() const { - return Strings::format("%s_%s_%s", this->name, this->version, this->target_triplet); + return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.target_triplet()); } std::ostream& operator<<(std::ostream& os, const BinaryParagraph& p) { - os << "Package: " << p.name << "\n"; + os << "Package: " << p.spec.name() << "\n"; os << "Version: " << p.version << "\n"; if (!p.depends.empty()) { @@ -71,7 +71,7 @@ namespace vcpkg os << "\n"; } - os << "Architecture: " << p.target_triplet << "\n"; + os << "Architecture: " << p.spec.target_triplet() << "\n"; os << "Multi-Arch: same\n"; if (!p.maintainer.empty()) os << "Maintainer: " << p.maintainer << "\n"; diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp index 7e3b0403e..374121ae9 100644 --- a/toolsrc/src/SourceParagraph.cpp +++ b/toolsrc/src/SourceParagraph.cpp @@ -5,17 +5,16 @@ using namespace vcpkg::details; vcpkg::SourceParagraph::SourceParagraph() = default; -vcpkg::SourceParagraph::SourceParagraph(const std::unordered_map<std::string, std::string>& fields) +vcpkg::SourceParagraph::SourceParagraph(const std::unordered_map<std::string, std::string>& fields): + name(required_field(fields, "Source")), + version(required_field(fields, "Version")), + description(optional_field(fields, "Description")), + maintainer(optional_field(fields, "Maintainer")) { - required_field(fields, name, "Source"); - required_field(fields, version, "Version"); - optional_field(fields, description, "Description"); - std::string deps; - optional_field(fields, deps, "Build-Depends"); + std::string deps = optional_field(fields, "Build-Depends"); if (!deps.empty()) { - depends.clear(); - parse_depends(deps, depends); - } - optional_field(fields, maintainer, "Maintainer"); + this->depends.clear(); + this->depends = parse_depends(deps); + }; } diff --git a/toolsrc/src/StatusParagraph.cpp b/toolsrc/src/StatusParagraph.cpp index 09a3b4d45..5aa425969 100644 --- a/toolsrc/src/StatusParagraph.cpp +++ b/toolsrc/src/StatusParagraph.cpp @@ -19,8 +19,7 @@ namespace vcpkg StatusParagraph::StatusParagraph(const std::unordered_map<std::string, std::string>& fields) : package(fields) { - std::string status_field; - required_field(fields, status_field, "Status"); + std::string status_field = required_field(fields, "Status"); auto b = status_field.begin(); auto mark = b; diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp index 463e3e3b8..3e23c519a 100644 --- a/toolsrc/src/StatusParagraphs.cpp +++ b/toolsrc/src/StatusParagraphs.cpp @@ -13,17 +13,19 @@ namespace vcpkg StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, const triplet& target_triplet) const { - return std::find_if(begin(), end(), [&](const auto& pgh) + return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { - return pgh->package.name == name && pgh->package.target_triplet == target_triplet; + const package_spec& spec = pgh->package.spec; + return spec.name() == name && spec.target_triplet() == target_triplet; }); } StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, const triplet& target_triplet) { - return std::find_if(begin(), end(), [&](const auto& pgh) + return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { - return pgh->package.name == name && pgh->package.target_triplet == target_triplet; + const package_spec& spec = pgh->package.spec; + return spec.name() == name && spec.target_triplet() == target_triplet; }); } @@ -41,18 +43,17 @@ namespace vcpkg StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr<StatusParagraph> pgh) { Checks::check_throw(pgh != nullptr, "Inserted null paragraph"); - auto ptr = find(pgh->package.name, pgh->package.target_triplet); + const package_spec& spec = pgh->package.spec; + auto ptr = find(spec.name(), spec.target_triplet()); if (ptr == end()) { paragraphs.push_back(std::move(pgh)); return paragraphs.rbegin(); } - else - { - // consume data from provided pgh. - **ptr = std::move(*pgh); - return ptr; - } + + // consume data from provided pgh. + **ptr = std::move(*pgh); + return ptr; } std::ostream& vcpkg::operator<<(std::ostream& os, const StatusParagraphs& l) diff --git a/toolsrc/src/commands_cache.cpp b/toolsrc/src/commands_cache.cpp new file mode 100644 index 000000000..0d70f0f29 --- /dev/null +++ b/toolsrc/src/commands_cache.cpp @@ -0,0 +1,39 @@ +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "vcpkg_Files.h" +#include "vcpkg.h" + +namespace vcpkg +{ + void cache_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + args.check_exact_arg_count(0); + + auto begin_it = fs::directory_iterator(paths.packages); + auto end_it = fs::directory_iterator(); + + if (begin_it == end_it) + { + System::println("No packages are cached."); + exit(EXIT_SUCCESS); + } + + for (; begin_it != end_it; ++begin_it) + { + const auto& path = begin_it->path(); + + auto file_contents = Files::get_contents(path / "CONTROL"); + if (auto text = file_contents.get()) + { + auto pghs = parse_paragraphs(*text); + if (pghs.size() != 1) + continue; + + auto src = BinaryParagraph(pghs[0]); + System::println(src.displayname().c_str()); + } + } + + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_create.cpp b/toolsrc/src/commands_create.cpp new file mode 100644 index 000000000..d1611eb5c --- /dev/null +++ b/toolsrc/src/commands_create.cpp @@ -0,0 +1,37 @@ +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "vcpkg_Environment.h" +#include "vcpkg_Files.h" +#include "vcpkg_Input.h" + +namespace vcpkg +{ + void create_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + static const std::string example = create_example_string(R"###(create zlib2 http://zlib.net/zlib128.zip "zlib128-2.zip")###"); + args.check_max_arg_count(3, example.c_str()); + args.check_min_arg_count(2, example.c_str()); + + const std::string port_name = args.command_arguments.at(0); + Environment::ensure_utilities_on_path(paths); + + // Space OR define the FILENAME with proper spacing + std::wstring custom_filename = L" "; + if (args.command_arguments.size() >= 3) + { + const std::string& zip_file_name = args.command_arguments.at(2); + Checks::check_exit(!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); + custom_filename = Strings::wformat(LR"( -DFILENAME="%s" )", Strings::utf8_to_utf16(zip_file_name)); + } + + const std::wstring cmdline = Strings::wformat(LR"(cmake -DCMD=CREATE -DPORT=%s -DURL=%s%s-P "%s")", + Strings::utf8_to_utf16(port_name), + Strings::utf8_to_utf16(args.command_arguments.at(1)), + custom_filename, + paths.ports_cmake.generic_wstring()); + + exit(System::cmd_execute(cmdline)); + } +} diff --git a/toolsrc/src/commands_edit.cpp b/toolsrc/src/commands_edit.cpp new file mode 100644 index 000000000..f07a15875 --- /dev/null +++ b/toolsrc/src/commands_edit.cpp @@ -0,0 +1,33 @@ +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "vcpkg_Input.h" + +namespace vcpkg +{ + void edit_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + static const std::string example = create_example_string("edit zlib"); + args.check_exact_arg_count(1, example.c_str()); + const std::string port_name = args.command_arguments.at(0); + + const fs::path portpath = paths.ports / port_name; + + // Find editor + std::wstring env_EDITOR = System::wdupenv_str(L"EDITOR"); + if (env_EDITOR.empty()) + { + static const std::wstring CODE_EXE_PATH = LR"(C:\Program Files (x86)\Microsoft VS Code\Code.exe)"; + if (fs::exists(CODE_EXE_PATH)) + { + env_EDITOR = CODE_EXE_PATH; + } + else + { + Checks::exit_with_message("Visual Studio Code was not found and the environmental variable EDITOR is not set"); + } + } + + std::wstring cmdLine = Strings::wformat(LR"("%s" "%s" "%s")", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native()); + exit(System::cmd_execute(cmdLine)); + } +} diff --git a/toolsrc/src/commands_help.cpp b/toolsrc/src/commands_help.cpp index 4e1ae9c49..194e809b1 100644 --- a/toolsrc/src/commands_help.cpp +++ b/toolsrc/src/commands_help.cpp @@ -6,18 +6,17 @@ namespace vcpkg { void version_command(const vcpkg_cmd_arguments& args) { - args.check_max_args(0); + args.check_exact_arg_count(0); System::println("Vcpkg package management program version %s\n" "\n" - "Vcpkg is provided \"as-is\" without warranty of any kind, express or implied.\n" - "All rights reserved.", vcpkg::version() + "See LICENSE.txt for license information.", vcpkg::version() ); exit(EXIT_SUCCESS); } void help_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - args.check_max_args(1); + args.check_max_arg_count(1); if (args.command_arguments.empty()) { print_usage(); @@ -37,8 +36,9 @@ namespace vcpkg exit(EXIT_SUCCESS); } - void contact_command(const vcpkg_cmd_arguments& /*args*/) + void contact_command(const vcpkg_cmd_arguments& args) { + args.check_exact_arg_count(0); System::println("Send an email to vcpkg@microsoft.com with any feedback."); exit(EXIT_SUCCESS); } diff --git a/toolsrc/src/commands_import.cpp b/toolsrc/src/commands_import.cpp new file mode 100644 index 000000000..9cfc53d6c --- /dev/null +++ b/toolsrc/src/commands_import.cpp @@ -0,0 +1,25 @@ +#include "vcpkg_Commands.h" +#include "vcpkg.h" + +namespace vcpkg +{ + void import_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + static const std::string example = create_example_string(R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"); + args.check_exact_arg_count(3, example.c_str()); + + 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]); + + auto pghs = get_paragraphs(control_file_path); + Checks::check_throw(pghs.size() == 1, "Invalid control file for package"); + + StatusParagraph spgh; + spgh.package = BinaryParagraph(pghs[0]); + auto& control_file_data = spgh.package; + + vcpkg::binary_import(paths, include_directory, project_directory, control_file_data); + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_installation.cpp b/toolsrc/src/commands_installation.cpp index b70ab0249..6fe6aa9a1 100644 --- a/toolsrc/src/commands_installation.cpp +++ b/toolsrc/src/commands_installation.cpp @@ -1,13 +1,13 @@ #include "vcpkg_Commands.h" #include "vcpkg.h" -#include <iostream> #include <fstream> -#include <iomanip> #include "vcpkg_Environment.h" #include "metrics.h" #include "vcpkg_Files.h" #include "post_build_lint.h" #include "vcpkg_System.h" +#include "vcpkg_Dependencies.h" +#include "vcpkg_Input.h" namespace vcpkg { @@ -23,12 +23,13 @@ namespace vcpkg static void build_internal(const package_spec& spec, const vcpkg_paths& paths, const fs::path& port_dir) { const fs::path ports_cmake_script_path = paths.ports_cmake; - const std::wstring command = Strings::format(LR"("%%VS140COMNTOOLS%%..\..\VC\vcvarsall.bat" %s && cmake -DCMD=BUILD -DPORT=%s -DTARGET_TRIPLET=%s "-DCURRENT_PORT_DIR=%s/." -P "%s")", - Strings::utf8_to_utf16(spec.target_triplet.architecture()), - Strings::utf8_to_utf16(spec.name), - Strings::utf8_to_utf16(spec.target_triplet.value), - port_dir.generic_wstring(), - ports_cmake_script_path.generic_wstring()); + auto&& target_triplet = spec.target_triplet(); + const std::wstring command = Strings::wformat(LR"("%%VS140COMNTOOLS%%..\..\VC\vcvarsall.bat" %s && cmake -DCMD=BUILD -DPORT=%s -DTARGET_TRIPLET=%s "-DCURRENT_PORT_DIR=%s/." -P "%s")", + Strings::utf8_to_utf16(target_triplet.architecture()), + Strings::utf8_to_utf16(spec.name()), + Strings::utf8_to_utf16(target_triplet.canonical_name()), + port_dir.generic_wstring(), + ports_cmake_script_path.generic_wstring()); System::Stopwatch timer; timer.start(); @@ -38,15 +39,22 @@ namespace vcpkg if (return_code != 0) { - System::println(System::color::error, "Error: build command failed"); + System::println(System::color::error, "Error: building package %s failed", to_string(spec)); + System::println("Please ensure sure 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." + , to_string(spec), version()); TrackProperty("error", "build failed"); - TrackProperty("build_error", std::to_string(return_code)); + TrackProperty("build_error", to_string(spec)); exit(EXIT_FAILURE); } perform_all_checks(spec, paths); - create_binary_control_file(paths, port_dir, spec.target_triplet); + create_binary_control_file(paths, port_dir, target_triplet); // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name; // delete_directory(port_buildtrees_dir); @@ -54,33 +62,37 @@ namespace vcpkg static void build_internal(const package_spec& spec, const vcpkg_paths& paths) { - return build_internal(spec, paths, paths.ports / spec.name); + return build_internal(spec, paths, paths.ports / spec.name()); } void install_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) { + static const std::string example = create_example_string("install zlib zlib:x64-windows curl boost"); + args.check_min_arg_count(1, example.c_str()); StatusParagraphs status_db = database_load_check(paths); - std::vector<package_spec> specs = args.extract_package_specs_with_unmet_dependencies(paths, default_target_triplet, status_db); - Checks::check_exit(!specs.empty(), "Specs cannot be empty"); - std::string specs_string = to_string(specs[0]); - for (size_t i = 1; i < specs.size(); ++i) + std::vector<package_spec> specs = Input::check_and_get_package_specs(args.command_arguments, default_target_triplet, example.c_str()); + Input::check_triplets(specs, paths); + std::vector<package_spec> install_plan = Dependencies::create_dependency_ordered_install_plan(paths, specs, status_db); + Checks::check_exit(!install_plan.empty(), "Install plan cannot be empty"); + std::string specs_string = to_string(install_plan[0]); + for (size_t i = 1; i < install_plan.size(); ++i) { specs_string.push_back(','); - specs_string.append(to_string(specs[i])); + specs_string.append(to_string(install_plan[i])); } TrackProperty("installplan", specs_string); Environment::ensure_utilities_on_path(paths); - for (const package_spec& spec : specs) + for (const package_spec& spec : install_plan) { - if (status_db.find_installed(spec.name, spec.target_triplet) != status_db.end()) + if (status_db.find_installed(spec.name(), spec.target_triplet()) != status_db.end()) { System::println(System::color::success, "Package %s is already installed", spec); continue; } - fs::path package_path = find_available_package(paths, spec); + fs::path package_path = paths.package_dir(spec); expected<std::string> file_contents = Files::get_contents(package_path / "CONTROL"); @@ -111,98 +123,46 @@ namespace vcpkg exit(EXIT_SUCCESS); } - void search_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) - { - args.check_max_args(1); - - if (args.command_arguments.size() == 1) - { - System::println(System::color::warning, "Search strings are not yet implemented; showing full list of packages."); - } - - auto begin_it = fs::directory_iterator(paths.ports); - auto end_it = fs::directory_iterator(); - for (; begin_it != end_it; ++begin_it) - { - const auto& path = begin_it->path(); - - try - { - auto pghs = get_paragraphs(path / "CONTROL"); - if (pghs.empty()) - continue; - auto srcpgh = SourceParagraph(pghs[0]); - std::cout << std::left - << std::setw(20) << srcpgh.name << ' ' - << std::setw(16) << srcpgh.version << ' ' - << shorten_description(srcpgh.description) << '\n'; - } - catch (std::runtime_error const&) - { - } - } - - System::println("\nIf your library is not listed, please open an issue at:\n" - " https://github.com/Microsoft/vcpkg/issues"); - - exit(EXIT_SUCCESS); - } - - void cache_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void build_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) { - args.check_max_args(0); + static const std::string example = create_example_string("build zlib:x64-windows"); - auto begin_it = fs::directory_iterator(paths.packages); - auto end_it = fs::directory_iterator(); + // Installing multiple packages leads to unintuitive behavior if one of them depends on another. + // Allowing only 1 package for now. - if (begin_it == end_it) - { - System::println("No packages are cached."); - exit(EXIT_SUCCESS); - } + args.check_exact_arg_count(1, example.c_str()); + StatusParagraphs status_db = database_load_check(paths); - for (; begin_it != end_it; ++begin_it) + const package_spec spec = Input::check_and_get_package_spec(args.command_arguments.at(0), default_target_triplet, example.c_str()); + Input::check_triplet(spec.target_triplet(), paths); + std::unordered_set<package_spec> unmet_dependencies = Dependencies::find_unmet_dependencies(paths, spec, status_db); + if (!unmet_dependencies.empty()) { - const auto& path = begin_it->path(); - - auto file_contents = Files::get_contents(path / "CONTROL"); - if (auto text = file_contents.get()) + 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 package_spec& p : unmet_dependencies) { - auto pghs = parse_paragraphs(*text); - if (pghs.size() != 1) - continue; - - auto src = BinaryParagraph(pghs[0]); - System::println(src.displayname().c_str()); + System::println(" %s", to_string(p)); } + System::println(""); + exit(EXIT_FAILURE); } - exit(EXIT_SUCCESS); - } - - void build_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) - { - std::vector<package_spec> specs = args.parse_all_arguments_as_package_specs(default_target_triplet); Environment::ensure_utilities_on_path(paths); - for (const package_spec& spec : specs) - { - build_internal(spec, paths); - } + build_internal(spec, paths); exit(EXIT_SUCCESS); } void build_external_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) { - if (args.command_arguments.size() != 2) - { - System::println(System::color::error, "Error: buildexternal requires the package name and the directory containing the CONTROL file"); - print_example(R"(buildexternal mylib C:\path\to\mylib\)"); - exit(EXIT_FAILURE); - } + static const std::string example = create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"); + args.check_exact_arg_count(2, example.c_str()); - expected<package_spec> current_spec = vcpkg::parse(args.command_arguments[0], default_target_triplet); + expected<package_spec> current_spec = package_spec::from_string(args.command_arguments[0], default_target_triplet); if (auto spec = current_spec.get()) { + Input::check_triplet(spec->target_triplet(), paths); Environment::ensure_utilities_on_path(paths); const fs::path port_dir = args.command_arguments.at(1); build_internal(*spec, paths, port_dir); diff --git a/toolsrc/src/commands_integration.cpp b/toolsrc/src/commands_integration.cpp index 178d40e83..6a11d6ec4 100644 --- a/toolsrc/src/commands_integration.cpp +++ b/toolsrc/src/commands_integration.cpp @@ -195,7 +195,7 @@ namespace vcpkg const fs::path sys_src_path = tmp_dir / "vcpkg.system.targets"; std::ofstream(sys_src_path) << create_system_targets_shortcut(); - const std::string param = Strings::format(R"(/c XCOPY "%s" "%s*" /Y > nul)", sys_src_path.string(), system_wide_targets_file.string()); + 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()); elevation_prompt_user_choice user_choice = elevated_cmd_execute(param); switch (user_choice) { @@ -238,7 +238,7 @@ namespace vcpkg exit(EXIT_SUCCESS); } - const std::wstring cmd_line = Strings::format(LR"(DEL "%s")", get_appdata_targets_path().native()); + const std::wstring cmd_line = Strings::wformat(LR"(DEL "%s")", get_appdata_targets_path().native()); const int exit_code = System::cmd_execute(cmd_line); if (exit_code) { @@ -269,7 +269,7 @@ namespace vcpkg std::ofstream(nuspec_file_path) << create_nuspec_file(paths.root, nuget_id, nupkg_version); // Using all forward slashes for the command line - const std::wstring cmd_line = Strings::format(LR"(nuget.exe pack -OutputDirectory "%s" "%s" > nul)", buildsystems_dir.native(), nuspec_file_path.native()); + const std::wstring cmd_line = Strings::wformat(LR"(nuget.exe pack -OutputDirectory "%s" "%s" > nul)", buildsystems_dir.native(), nuspec_file_path.native()); const int exit_code = System::cmd_execute(cmd_line); @@ -297,13 +297,9 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console void integrate_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - if (args.command_arguments.size() != 1) - { - std::cout << "Commands:\n" << - INTEGRATE_COMMAND_HELPSTRING << - "\n"; - exit(EXIT_FAILURE); - } + static const std::string example = Strings::format("Commands:\n" + "%s", INTEGRATE_COMMAND_HELPSTRING); + args.check_exact_arg_count(1, example.c_str()); if (args.command_arguments[0] == "install") { diff --git a/toolsrc/src/commands_list.cpp b/toolsrc/src/commands_list.cpp new file mode 100644 index 000000000..194e4b435 --- /dev/null +++ b/toolsrc/src/commands_list.cpp @@ -0,0 +1,32 @@ +#include "vcpkg_Commands.h" +#include "vcpkg.h" +#include "vcpkg_System.h" + +namespace vcpkg +{ + void list_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + args.check_exact_arg_count(0); + + std::vector<std::string> packages_output; + for (auto&& pgh : database_load_check(paths)) + { + if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge) + continue; + packages_output.push_back(Strings::format("%-27s %-16s %s", + pgh->package.displayname(), + pgh->package.version, + shorten_description(pgh->package.description))); + } + std::sort(packages_output.begin(), packages_output.end()); + for (auto&& package : packages_output) + { + System::println(package.c_str()); + } + if (packages_output.empty()) + { + System::println("No packages are installed. Did you mean `search`?"); + } + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_other.cpp b/toolsrc/src/commands_other.cpp index c7dcc2586..07549a437 100644 --- a/toolsrc/src/commands_other.cpp +++ b/toolsrc/src/commands_other.cpp @@ -1,16 +1,13 @@ #include "vcpkg_Commands.h" -#include <iostream> -#include <unordered_set> -#include "vcpkg_Environment.h" -#include "vcpkg.h" #include "vcpkg_System.h" -#include "vcpkg_Files.h" +#include "vcpkg.h" namespace vcpkg { void print_usage() { - std::cout << "Commands:\n" + System::println( + "Commands:\n" " vcpkg search [pat] Search for packages available to be built\n" " vcpkg install <pkg> Install a package\n" " vcpkg remove <pkg> Uninstall a package. \n" @@ -18,9 +15,9 @@ namespace vcpkg " vcpkg list List installed packages\n" " vcpkg update Display list of packages for updating\n" "\n" - << INTEGRATE_COMMAND_HELPSTRING << + "%s" // Integration help "\n" - " vcpkg edit <pkg> Open up a port for editing (uses %EDITOR%, default 'code')\n" + " vcpkg edit <pkg> Open up a port for editing (uses %%EDITOR%%, default 'code')\n" " vcpkg import <pkg> Import a pre-built library\n" " vcpkg create <pkg> <url>\n" " [archivename] Create a new package\n" @@ -35,215 +32,25 @@ namespace vcpkg //"\n" "Options:\n" " --triplet <t> Specify the target architecture triplet.\n" - " (default: x86-windows, see 'vcpkg help triplet')\n" + " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n" "\n" " --vcpkg-root <path> Specify the vcpkg root directory\n" - " (default: %VCPKG_ROOT%)\n" + " (default: %%VCPKG_ROOT%%)\n" "\n" "For more help (including examples) see the accompanying README.md." - "\n"; + , INTEGRATE_COMMAND_HELPSTRING); } - void print_example(const char* command_and_arguments) + std::string create_example_string(const char* command_and_arguments) { - std::cout << - "Example:\n" - " vcpkg " << command_and_arguments << "\n"; + std::string cs = Strings::format("Example:\n" + " vcpkg %s", command_and_arguments); + return cs; } - void update_command(const vcpkg_cmd_arguments& /*args*/, const vcpkg_paths& paths) - { - auto status_db = database_load_check(paths); - - std::unordered_map<std::string, std::string> src_names_to_versions; - - auto begin_it = fs::directory_iterator(paths.ports); - auto end_it = fs::directory_iterator(); - for (; begin_it != end_it; ++begin_it) - { - const auto& path = begin_it->path(); - try - { - auto pghs = get_paragraphs(path / "CONTROL"); - if (pghs.empty()) - continue; - auto srcpgh = SourceParagraph(pghs[0]); - src_names_to_versions.emplace(srcpgh.name, srcpgh.version); - } - catch (std::runtime_error const&) - { - } - } - - std::string packages_list; - - std::vector<std::string> packages_output; - for (auto&& pgh : database_load_check(paths)) - { - if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge) - continue; - auto it = src_names_to_versions.find(pgh->package.name); - if (it == src_names_to_versions.end()) - { - // Package was not installed from portfile - continue; - } - if (it->second != pgh->package.version) - { - packages_output.push_back(Strings::format("%-27s %s -> %s", - pgh->package.displayname(), - pgh->package.version, - it->second)); - packages_list.append(" " + pgh->package.displayname()); - } - } - std::sort(packages_output.begin(), packages_output.end()); - if (packages_output.empty()) - { - System::println("No packages need updating."); - } - else - { - System::println("The following packages differ from their port versions:"); - for (auto&& package : packages_output) - { - System::println(" %s", package.c_str()); - } - System::println("\nTo update these packages, run\n vcpkg remove --purge <pkgs>...\n vcpkg install <pkgs>..."); - } - - auto version_file = Files::get_contents(paths.root / "toolsrc" / "VERSION.txt"); - if (auto version_contents = version_file.get()) - { - int maj1, min1, rev1; - auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1); - - int maj2, min2, rev2; - auto num2 = sscanf_s(version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2); - - if (num1 == 3 && num2 == 3) - { - if (maj1 != maj2 || min1 != min2 || rev1 != rev2) - { - System::println("Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use scripts\\bootstrap.ps1 to update.", - maj2, min2, rev2, - maj1, min1, rev1); - } - } - } - - exit(EXIT_SUCCESS); - } - - void edit_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) - { - static auto example = "edit zlib"; - args.check_max_args(1, example); - package_spec spec = args.parse_all_arguments_as_package_specs(default_target_triplet, example).at(0); - - // Find editor - std::wstring env_EDITOR = System::wdupenv_str(L"EDITOR"); - if (env_EDITOR.empty()) - env_EDITOR = LR"(C:\Program Files (x86)\Microsoft VS Code\Code.exe)"; - - auto portpath = paths.ports / spec.name; - std::wstring cmdLine = Strings::format(LR"("%s" "%s" "%s")", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native()); - exit(System::cmd_execute(cmdLine)); - } - - void create_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) - { - args.check_max_args(3); - package_spec spec = args.parse_all_arguments_as_package_specs(default_target_triplet).at(0); - if (args.command_arguments.size() < 2) - { - System::println(System::color::error, "Error: create requires the archive's URL as the second argument."); - print_usage(); - exit(EXIT_FAILURE); - } - Environment::ensure_utilities_on_path(paths); - - // Space OR define the FILENAME with proper spacing - std::wstring custom_filename = L" "; - if (args.command_arguments.size() >= 3) - { - custom_filename = Strings::format(L" -DFILENAME=%s ", Strings::utf8_to_utf16(args.command_arguments.at(2))); - } - - const std::wstring cmdline = Strings::format(LR"(cmake -DCMD=SCAFFOLD -DPORT=%s -DTARGET_TRIPLET=%s -DURL=%s%s-P "%s")", - Strings::utf8_to_utf16(spec.name), - Strings::utf8_to_utf16(spec.target_triplet.value), - Strings::utf8_to_utf16(args.command_arguments.at(1)), - custom_filename, - paths.ports_cmake.generic_wstring()); - - exit(System::cmd_execute(cmdline)); - } - - void list_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) - { - args.check_max_args(0); - - std::vector<std::string> packages_output; - for (auto&& pgh : database_load_check(paths)) - { - if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge) - continue; - packages_output.push_back(Strings::format("%-27s %-16s %s", - pgh->package.displayname(), - pgh->package.version, - shorten_description(pgh->package.description))); - } - std::sort(packages_output.begin(), packages_output.end()); - for (auto&& package : packages_output) - { - System::println(package.c_str()); - } - if (packages_output.empty()) - { - System::println("No packages are installed. Did you mean `search`?"); - } - exit(EXIT_SUCCESS); - } - - void import_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) - { - if (args.command_arguments.size() != 3) - { - System::println(System::color::error, "Error: %s requires 3 parameters", args.command); - print_example(Strings::format(R"(%s C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)", args.command).c_str()); - exit(EXIT_FAILURE); - } - - 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]); - - auto pghs = get_paragraphs(control_file_path); - Checks::check_throw(pghs.size() == 1, "Invalid control file for package"); - - StatusParagraph spgh; - spgh.package = BinaryParagraph(pghs[0]); - auto& control_file_data = spgh.package; - - vcpkg::binary_import(paths, include_directory, project_directory, control_file_data); - exit(EXIT_SUCCESS); - } - - void owns_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void print_example(const char* command_and_arguments) { - args.check_max_args(1); - if (args.command_arguments.size() == 0) - { - System::println(System::color::error, "Error: owns requires a pattern to search for as the first argument."); - std::cout << - "example:\n" - " vcpkg owns .dll\n"; - exit(EXIT_FAILURE); - } - StatusParagraphs status_db = database_load_check(paths); - search_file(paths, args.command_arguments[0], status_db); - exit(EXIT_SUCCESS); + System::println(create_example_string(command_and_arguments).c_str()); } void internal_test_command(const vcpkg_cmd_arguments& /*args*/, const vcpkg_paths& /*paths*/) @@ -259,8 +66,6 @@ namespace vcpkg {"install", install_command}, {"remove", remove_command}, {"build", build_command}, - {"edit", edit_command}, - {"create", create_command}, {"build_external", build_external_command} }; return t; @@ -275,6 +80,8 @@ namespace vcpkg {"integrate", integrate_command}, {"owns", owns_command}, {"update", update_command}, + {"edit", edit_command}, + {"create", create_command}, {"import", import_command}, {"cache", cache_command}, {"internal_test", internal_test_command}, diff --git a/toolsrc/src/commands_owns.cpp b/toolsrc/src/commands_owns.cpp new file mode 100644 index 000000000..b3dab2e44 --- /dev/null +++ b/toolsrc/src/commands_owns.cpp @@ -0,0 +1,16 @@ +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "vcpkg.h" + +namespace vcpkg +{ + void owns_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + static const std::string example = Strings::format("The argument should be a pattern to search for. %s", create_example_string("owns zlib.dll")); + args.check_exact_arg_count(1, example.c_str()); + + StatusParagraphs status_db = database_load_check(paths); + search_file(paths, args.command_arguments[0], status_db); + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp index f5315ccb1..5bb9ecc96 100644 --- a/toolsrc/src/commands_remove.cpp +++ b/toolsrc/src/commands_remove.cpp @@ -1,6 +1,7 @@ #include "vcpkg_Commands.h" #include "vcpkg.h" #include "vcpkg_System.h" +#include "vcpkg_Input.h" namespace vcpkg { @@ -22,10 +23,14 @@ namespace vcpkg void remove_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) { + static const std::string example = create_example_string("remove zlib zlib:x64-windows curl boost"); + args.check_min_arg_count(1, example.c_str()); + const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_PURGE}); auto status_db = database_load_check(paths); - std::vector<package_spec> specs = args.parse_all_arguments_as_package_specs(default_target_triplet); + std::vector<package_spec> specs = Input::check_and_get_package_specs(args.command_arguments, default_target_triplet, example.c_str()); + Input::check_triplets(specs, paths); bool alsoRemoveFolderFromPackages = options.find(OPTION_PURGE) != options.end(); for (const package_spec& spec : specs) diff --git a/toolsrc/src/commands_search.cpp b/toolsrc/src/commands_search.cpp new file mode 100644 index 000000000..c90538e86 --- /dev/null +++ b/toolsrc/src/commands_search.cpp @@ -0,0 +1,64 @@ +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "vcpkg.h" +#include <iostream> +#include <iomanip> + +namespace fs = std::tr2::sys; + +namespace vcpkg +{ + template <class Pred> + static void do_print(const vcpkg_paths& paths, Pred predicate) + { + for (auto it = fs::directory_iterator(paths.ports); it != fs::directory_iterator(); ++it) + { + const fs::path& path = it->path(); + + try + { + auto pghs = get_paragraphs(path / "CONTROL"); + if (pghs.empty()) + continue; + auto srcpgh = SourceParagraph(pghs[0]); + + if (predicate(srcpgh.name)) + { + std::cout << std::left + << std::setw(20) << srcpgh.name << ' ' + << std::setw(16) << srcpgh.version << ' ' + << shorten_description(srcpgh.description) << '\n'; + } + } + catch (std::runtime_error const&) + { + } + } + } + + void search_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& 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", create_example_string("search png")); + args.check_max_arg_count(1, example.c_str()); + + if (args.command_arguments.size() == 0) + { + do_print(paths, [](std::string&) -> bool + { + return true; + }); + exit(EXIT_SUCCESS); + } + + // At this point there is 1 argument + do_print(paths, [&](std::string& port_name) -> bool + { + return Strings::case_insensitive_ascii_find(port_name, args.command_arguments[0]) != port_name.end(); + }); + + System::println("\nIf your library is not listed, please open an issue at:\n" + " https://github.com/Microsoft/vcpkg/issues"); + + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_update.cpp b/toolsrc/src/commands_update.cpp new file mode 100644 index 000000000..5d531ef39 --- /dev/null +++ b/toolsrc/src/commands_update.cpp @@ -0,0 +1,94 @@ +#include "vcpkg_Commands.h" +#include "vcpkg.h" +#include "vcpkg_System.h" +#include "vcpkg_Files.h" + +namespace vcpkg +{ + void update_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + { + args.check_exact_arg_count(0); + System::println("Using local portfile versions. To update the local portfiles, use `git pull`."); + + auto status_db = database_load_check(paths); + + std::unordered_map<std::string, std::string> src_names_to_versions; + + auto begin_it = fs::directory_iterator(paths.ports); + auto end_it = fs::directory_iterator(); + for (; begin_it != end_it; ++begin_it) + { + const auto& path = begin_it->path(); + try + { + auto pghs = get_paragraphs(path / "CONTROL"); + if (pghs.empty()) + continue; + auto srcpgh = SourceParagraph(pghs[0]); + src_names_to_versions.emplace(srcpgh.name, srcpgh.version); + } + catch (std::runtime_error const&) + { + } + } + + std::string packages_list; + + std::vector<std::string> packages_output; + for (auto&& pgh : database_load_check(paths)) + { + if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge) + continue; + 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) + { + packages_output.push_back(Strings::format("%-27s %s -> %s", + pgh->package.displayname(), + pgh->package.version, + it->second)); + packages_list.append(" " + pgh->package.displayname()); + } + } + std::sort(packages_output.begin(), packages_output.end()); + if (packages_output.empty()) + { + System::println("No packages need updating."); + } + else + { + System::println("The following packages differ from their port versions:"); + for (auto&& package : packages_output) + { + System::println(" %s", package.c_str()); + } + System::println("\nTo update these packages, run\n vcpkg remove --purge <pkgs>...\n vcpkg install <pkgs>..."); + } + + auto version_file = Files::get_contents(paths.root / "toolsrc" / "VERSION.txt"); + if (auto version_contents = version_file.get()) + { + int maj1, min1, rev1; + auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1); + + int maj2, min2, rev2; + auto num2 = sscanf_s(version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2); + + if (num1 == 3 && num2 == 3) + { + if (maj1 != maj2 || min1 != min2 || rev1 != rev2) + { + System::println("Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use scripts\\bootstrap.ps1 to update.", + maj2, min2, rev2, + maj1, min1, rev1); + } + } + } + + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/lib.cpp b/toolsrc/src/lib.cpp index 2a3b95182..45b73ee07 100644 --- a/toolsrc/src/lib.cpp +++ b/toolsrc/src/lib.cpp @@ -133,26 +133,6 @@ static std::string get_fullpkgname_from_listfile(const fs::path& path) return ret; } -fs::path vcpkg::control_file_for_package(const fs::path& package) -{ - return package / "CONTROL"; -} - -fs::path vcpkg::find_available_package(const vcpkg_paths& paths, const package_spec& spec) -{ - return paths.packages / Strings::format("%s_%s", spec.name, spec.target_triplet); -} - -fs::path vcpkg::find_available_port_file(const vcpkg_paths& paths, const package_spec& spec) -{ - return paths.ports / spec.name; -} - -static fs::path prefix_path_for_package(const vcpkg_paths& paths, const BinaryParagraph& pgh) -{ - return find_available_package(paths, {pgh.name, pgh.target_triplet}); -} - static void write_update(const vcpkg_paths& paths, const StatusParagraph& p) { static int update_id = 0; @@ -169,12 +149,14 @@ static void install_and_write_listfile(const vcpkg_paths& paths, const BinaryPar { std::fstream listfile(listfile_path(paths, bpgh), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); - auto package_prefix_path = prefix_path_for_package(paths, bpgh); + auto package_prefix_path = paths.package_dir(bpgh.spec); auto prefix_length = package_prefix_path.native().size(); + const triplet& target_triplet = bpgh.spec.target_triplet(); + const std::string& target_triplet_as_string = target_triplet.canonical_name(); std::error_code ec; - fs::create_directory(paths.installed / bpgh.target_triplet.value, ec); - listfile << bpgh.target_triplet << "\n"; + fs::create_directory(paths.installed / target_triplet_as_string, ec); + listfile << target_triplet << "\n"; for (auto it = fs::recursive_directory_iterator(package_prefix_path); it != fs::recursive_directory_iterator(); ++it) { @@ -186,12 +168,12 @@ static void install_and_write_listfile(const vcpkg_paths& paths, const BinaryPar } auto suffix = it->path().generic_u8string().substr(prefix_length + 1); - auto target = paths.installed / bpgh.target_triplet.value / suffix; + auto target = paths.installed / target_triplet_as_string / suffix; auto status = it->status(ec); if (ec) { - System::println(System::color::error, "failed: %s", ec.message()); + System::println(System::color::error, "failed: %s: %s", it->path().u8string(), ec.message()); continue; } if (fs::is_directory(status)) @@ -199,26 +181,26 @@ static void install_and_write_listfile(const vcpkg_paths& paths, const BinaryPar fs::create_directory(target, ec); if (ec) { - System::println(System::color::error, "failed: %s", ec.message()); + System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message()); } - listfile << bpgh.target_triplet << "/" << suffix << "\n"; + listfile << target_triplet << "/" << suffix << "\n"; } else if (fs::is_regular_file(status)) { fs::copy_file(*it, target, ec); if (ec) { - System::println(System::color::error, "failed: %s", ec.message()); + System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message()); } - listfile << bpgh.target_triplet << "/" << suffix << "\n"; + listfile << target_triplet << "/" << suffix << "\n"; } else if (!fs::status_known(status)) { - std::cout << "unknown status: " << *it << "\n"; + System::println(System::color::error, "failed: %s: unknown status", it->path().u8string()); } else - std::cout << "warning: file does not exist: " << *it << "\n"; + System::println(System::color::error, "failed: %s: cannot handle file type", it->path().u8string()); } listfile.close(); @@ -228,35 +210,40 @@ static void install_and_write_listfile(const vcpkg_paths& paths, const BinaryPar std::vector<std::string> vcpkg::get_unmet_package_dependencies(const vcpkg_paths& paths, const package_spec& spec, const StatusParagraphs& status_db) { std::vector<std::unordered_map<std::string, std::string>> pghs; - const fs::path packages_dir_control_file_path = find_available_package(paths, spec) / "CONTROL"; + { + const fs::path packages_dir_control_file_path = paths.package_dir(spec) / "CONTROL"; - if (fs::exists(packages_dir_control_file_path)) + auto control_contents_maybe = Files::get_contents(packages_dir_control_file_path); + if (auto control_contents = control_contents_maybe.get()) + { + try + { + pghs = parse_paragraphs(*control_contents); + } + catch (std::runtime_error) + { + } + Checks::check_exit(pghs.size() == 1, "Invalid control file at %s", packages_dir_control_file_path.string()); + return BinaryParagraph(pghs[0]).depends; + } + } + + const fs::path ports_dir_control_file_path = paths.port_dir(spec) / "CONTROL"; + auto control_contents_maybe = Files::get_contents(ports_dir_control_file_path); + if (auto control_contents = control_contents_maybe.get()) { try { - pghs = get_paragraphs(packages_dir_control_file_path); + pghs = parse_paragraphs(*control_contents); } catch (std::runtime_error) { - // ?? } - - Checks::check_throw(pghs.size() == 1, "Invalid control file for package"); - return BinaryParagraph(pghs[0]).depends; + Checks::check_exit(pghs.size() == 1, "Invalid control file at %s", ports_dir_control_file_path.string()); + return SourceParagraph(pghs[0]).depends; } - const fs::path ports_dir_control_file_path = find_available_port_file(paths, spec) / "CONTROL"; - try - { - pghs = get_paragraphs(ports_dir_control_file_path); - } - catch (std::runtime_error) - { - // ?? - } - - Checks::check_exit(pghs.size() == 1, "Invalid control file for package %s", spec); - return SourceParagraph(pghs[0]).depends; + Checks::exit_with_message("Could not find package named %s", spec); } void vcpkg::install_package(const vcpkg_paths& paths, const BinaryParagraph& binary_paragraph, StatusParagraphs& status_db) @@ -267,7 +254,7 @@ void vcpkg::install_package(const vcpkg_paths& paths, const BinaryParagraph& bin spgh.state = install_state_t::half_installed; for (const std::string& dependency : spgh.package.depends) { - if (status_db.find_installed(dependency, spgh.package.target_triplet) == status_db.end()) + if (status_db.find_installed(dependency, spgh.package.spec.target_triplet()) == status_db.end()) { std::abort(); } @@ -307,12 +294,12 @@ static deinstall_plan deinstall_package_plan( { if (inst_pkg->want != want_t::install) continue; - if (inst_pkg->package.target_triplet != pkg.target_triplet) + if (inst_pkg->package.spec.target_triplet() != pkg.spec.target_triplet()) continue; const auto& deps = inst_pkg->package.depends; - if (std::find(deps.begin(), deps.end(), pkg.name) != deps.end()) + if (std::find(deps.begin(), deps.end(), pkg.spec.name()) != deps.end()) { dependencies_out.push_back(inst_pkg.get()); } @@ -326,7 +313,7 @@ static deinstall_plan deinstall_package_plan( void vcpkg::deinstall_package(const vcpkg_paths& paths, const package_spec& spec, StatusParagraphs& status_db) { - auto package_it = status_db.find(spec.name, spec.target_triplet); + auto package_it = status_db.find(spec.name(), spec.target_triplet()); if (package_it == status_db.end()) { System::println(System::color::success, "Package %s is not installed", spec); @@ -389,16 +376,16 @@ void vcpkg::deinstall_package(const vcpkg_paths& paths, const package_spec& spec fs::remove(target, ec); if (ec) { - System::println(System::color::error, "failed: %s", ec.message()); + 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.string()); + System::println(System::color::warning, "Warning: unknown status: %s", target.u8string()); } else { - System::println(System::color::warning, "Warning: ???: %s", target.string()); + System::println(System::color::warning, "Warning: %s: cannot handle file type", target.u8string()); } } @@ -515,7 +502,7 @@ namespace void vcpkg::binary_import(const vcpkg_paths& paths, const fs::path& include_directory, const fs::path& project_directory, const BinaryParagraph& control_file_data) { - fs::path library_destination_path = prefix_path_for_package(paths, control_file_data); + fs::path library_destination_path = paths.package_dir(control_file_data.spec); fs::create_directory(library_destination_path); place_library_files_in(include_directory, project_directory, library_destination_path); diff --git a/toolsrc/src/main.cpp b/toolsrc/src/main.cpp index d3fb855d1..f3d68f5dd 100644 --- a/toolsrc/src/main.cpp +++ b/toolsrc/src/main.cpp @@ -11,6 +11,7 @@ #include <Shlobj.h> #include "vcpkg_Files.h" #include "vcpkg_System.h" +#include "vcpkg_Input.h" using namespace vcpkg; @@ -23,21 +24,6 @@ void invalid_command(const std::string& cmd) exit(EXIT_FAILURE); } -static fs::path find_file_recursively_up(const fs::path& starting_dir, const std::string& filename) -{ - fs::path current_dir = starting_dir; - for (; !current_dir.empty(); current_dir = current_dir.parent_path()) - { - const fs::path candidate = current_dir / filename; - if (fs::exists(candidate)) - { - break; - } - } - - return current_dir; -} - static void inner(const vcpkg_cmd_arguments& args) { TrackProperty("command", args.command); @@ -67,7 +53,7 @@ static void inner(const vcpkg_cmd_arguments& args) } else { - vcpkg_root_dir = find_file_recursively_up(fs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root"); + vcpkg_root_dir = Files::find_file_recursively_up(fs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root"); } } @@ -84,32 +70,26 @@ static void inner(const vcpkg_cmd_arguments& args) return command_function(args, paths); } - triplet default_target_triplet = triplet::X86_WINDOWS; - + triplet default_target_triplet; if (args.target_triplet != nullptr) { - const std::string& target_triplet = *args.target_triplet; - - auto it = fs::directory_iterator(paths.triplets); - for (; it != fs::directory_iterator(); ++it) + default_target_triplet = triplet::from_canonical_name(*args.target_triplet); + } + else + { + const auto vcpkg_default_triplet_env = System::wdupenv_str(L"VCPKG_DEFAULT_TRIPLET"); + if (!vcpkg_default_triplet_env.empty()) { - std::string triplet_file_name = it->path().stem().generic_u8string(); - if (target_triplet == triplet_file_name) // TODO: fuzzy compare - { - default_target_triplet = {triplet_file_name}; - break; - } + default_target_triplet = triplet::from_canonical_name(Strings::utf16_to_utf8(vcpkg_default_triplet_env)); } - - if (it == fs::directory_iterator()) + else { - System::println(System::color::error, "Error: invalid triplet: %s", target_triplet); - TrackProperty("error", "invalid triplet: " + target_triplet); - help_topic_valid_triplet(paths); - exit(EXIT_FAILURE); + default_target_triplet = triplet::X86_WINDOWS; } } + Input::check_triplet(default_target_triplet, paths); + if (auto command_function = find_command(args.command, get_available_commands_type_a())) { return command_function(args, paths, default_target_triplet); diff --git a/toolsrc/src/metrics.cpp b/toolsrc/src/metrics.cpp index 610c71ed1..ada065fd6 100644 --- a/toolsrc/src/metrics.cpp +++ b/toolsrc/src/metrics.cpp @@ -419,7 +419,7 @@ true const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + GenerateRandomUUID() + ".txt"); std::ofstream(vcpkg_metrics_txt_path) << payload; - const std::wstring cmdLine = Strings::format(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native()); + const std::wstring cmdLine = Strings::wformat(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native()); System::cmd_execute(cmdLine); } } diff --git a/toolsrc/src/package_spec.cpp b/toolsrc/src/package_spec.cpp index ece5f91e9..86d4393bd 100644 --- a/toolsrc/src/package_spec.cpp +++ b/toolsrc/src/package_spec.cpp @@ -1,32 +1,63 @@ #include "package_spec.h" +#include <algorithm> namespace vcpkg { - expected<package_spec> parse(const std::string& spec, const triplet& default_target_triplet) + static bool is_valid_package_spec_char(char c) { - auto pos = spec.find(':'); + return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)); + } + + expected<package_spec> package_spec::from_string(const std::string& spec_as_string, const triplet& default_target_triplet) + { + auto pos = spec_as_string.find(':'); if (pos == std::string::npos) { - return package_spec{spec, default_target_triplet}; + return from_name_and_triplet(spec_as_string, default_target_triplet); } - auto pos2 = spec.find(':', pos + 1); + auto pos2 = spec_as_string.find(':', pos + 1); if (pos2 != std::string::npos) { - return std::error_code(package_spec_parse_result::too_many_colons); + return std::error_code(package_spec_parse_result::TOO_MANY_COLONS); } - return package_spec{spec.substr(0, pos), spec.substr(pos + 1)}; + const std::string name = spec_as_string.substr(0, pos); + const triplet target_triplet = triplet::from_canonical_name(spec_as_string.substr(pos + 1)); + return from_name_and_triplet(name, target_triplet); + } + + expected<package_spec> package_spec::from_name_and_triplet(const std::string& name, const triplet& target_triplet) + { + if (std::find_if_not(name.cbegin(), name.cend(), is_valid_package_spec_char) != name.end()) + { + return std::error_code(package_spec_parse_result::INVALID_CHARACTERS); + } + + package_spec p; + p.m_name = name; + p.m_target_triplet = target_triplet; + return p; + } + + const std::string& package_spec::name() const + { + return this->m_name; + } + + const triplet& package_spec::target_triplet() const + { + return this->m_target_triplet; } std::string package_spec::dir() const { - return Strings::format("%s_%s", this->name, this->target_triplet); + return Strings::format("%s_%s", this->m_name, this->m_target_triplet); } std::string to_string(const package_spec& spec) { - return Strings::format("%s:%s", spec.name, spec.target_triplet); + return Strings::format("%s:%s", spec.name(), spec.target_triplet()); } std::string to_printf_arg(const package_spec& spec) @@ -36,7 +67,7 @@ namespace vcpkg bool operator==(const package_spec& left, const package_spec& right) { - return left.name == right.name && left.target_triplet == right.target_triplet; + return left.name() == right.name() && left.target_triplet() == right.target_triplet(); } std::ostream& operator<<(std::ostream& os, const package_spec& spec) diff --git a/toolsrc/src/package_spec_parse_result.cpp b/toolsrc/src/package_spec_parse_result.cpp index 757b6df53..dc377f656 100644 --- a/toolsrc/src/package_spec_parse_result.cpp +++ b/toolsrc/src/package_spec_parse_result.cpp @@ -13,10 +13,12 @@ namespace vcpkg { switch (static_cast<package_spec_parse_result>(ev)) { - case package_spec_parse_result::success: + case package_spec_parse_result::SUCCESS: return "OK"; - case package_spec_parse_result::too_many_colons: + case package_spec_parse_result::TOO_MANY_COLONS: return "Too many colons"; + case package_spec_parse_result::INVALID_CHARACTERS: + return "Contains invalid characters. Only alphanumeric lowercase ASCII characters and dashes are allowed"; default: Checks::unreachable(); } diff --git a/toolsrc/src/post_build_lint.cpp b/toolsrc/src/post_build_lint.cpp index 1d4ca0f67..2b2812d73 100644 --- a/toolsrc/src/post_build_lint.cpp +++ b/toolsrc/src/post_build_lint.cpp @@ -71,12 +71,10 @@ namespace vcpkg 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 - lint_status::ERROR; + return lint_status::ERROR; } - return - lint_status::SUCCESS; + return lint_status::SUCCESS; } static lint_status check_for_files_in_debug_share_directory(const package_spec& spec, const vcpkg_paths& paths) @@ -115,7 +113,7 @@ namespace vcpkg 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); + 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()); print_vector_of_files(misplaced_cmake_files); return lint_status::ERROR; } @@ -153,12 +151,12 @@ namespace vcpkg static lint_status check_for_copyright_file(const package_spec& spec, const vcpkg_paths& paths) { - const fs::path copyright_file = paths.packages / spec.dir() / "share" / spec.name / "copyright"; + const fs::path copyright_file = paths.packages / spec.dir() / "share" / spec.name() / "copyright"; if (fs::exists(copyright_file)) { return lint_status::SUCCESS; } - const fs::path current_buildtrees_dir = paths.buildtrees / spec.name; + const fs::path current_buildtrees_dir = paths.buildtrees / spec.name(); const fs::path current_buildtrees_dir_src = current_buildtrees_dir / "src"; std::vector<fs::path> potential_copyright_files; @@ -177,14 +175,14 @@ namespace vcpkg } } - System::println(System::color::warning, "The software license must be available at ${CURRENT_PACKAGES_DIR}/share/%s/copyright .", spec.name); + 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); + relative_path.generic_string(), spec.name(), spec.name(), found_file.filename().generic_string(), spec.name()); return lint_status::ERROR; } @@ -195,7 +193,7 @@ namespace vcpkg } const fs::path current_packages_dir = paths.packages / spec.dir(); - System::println(" %s/share/%s/copyright", current_packages_dir.generic_string(), spec.name); + System::println(" %s/share/%s/copyright", current_packages_dir.generic_string(), spec.name()); return lint_status::ERROR; } @@ -221,7 +219,7 @@ namespace vcpkg std::vector<fs::path> dlls_with_no_exports; for (const fs::path& dll : dlls) { - const std::wstring cmd_line = Strings::format(LR"("%s" /exports "%s")", DUMPBIN_EXE.native(), dll.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /exports "%s")", DUMPBIN_EXE.native(), dll.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); @@ -252,7 +250,7 @@ namespace vcpkg std::vector<fs::path> dlls_with_improper_uwp_bit; for (const fs::path& dll : dlls) { - const std::wstring cmd_line = Strings::format(LR"("%s" /headers "%s")", DUMPBIN_EXE.native(), dll.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /headers "%s")", DUMPBIN_EXE.native(), dll.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); @@ -284,11 +282,11 @@ namespace vcpkg std::vector<file_and_arch> binaries_with_invalid_architecture; for (const fs::path& f : files) { - const std::wstring cmd_line = Strings::format(LR"("%s" /headers "%s" | findstr machine)", DUMPBIN_EXE.native(), f.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /headers "%s" | findstr machine)", DUMPBIN_EXE.native(), f.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); - if (Strings::case_insensitive_find(ec_data.output, expected_architecture) == ec_data.output.end()) + if (Strings::case_insensitive_ascii_find(ec_data.output, expected_architecture) == ec_data.output.end()) { binaries_with_invalid_architecture.push_back({f, ec_data.output}); } @@ -311,15 +309,27 @@ namespace vcpkg return lint_status::SUCCESS; } - static void operator +=(unsigned int& left, const lint_status& right) + static lint_status check_no_dlls_present(const std::vector<fs::path>& dlls) + { + if (dlls.empty()) + { + return lint_status::SUCCESS; + } + + System::println(System::color::warning, "DLLs should not be present in a static build, but the following DLLs were found:"); + print_vector_of_files(dlls); + return lint_status::ERROR; + } + + static void operator +=(size_t& left, const lint_status& right) { - left += static_cast<unsigned int>(right); + left += static_cast<size_t>(right); } void perform_all_checks(const package_spec& spec, const vcpkg_paths& paths) { System::println("-- Performing post-build validation"); - unsigned int error_count = 0; + size_t error_count = 0; error_count += check_for_files_in_include_directory(spec, paths); error_count += check_for_files_in_debug_include_directory(spec, paths); error_count += check_for_files_in_debug_share_directory(spec, paths); @@ -330,24 +340,42 @@ namespace vcpkg error_count += check_for_copyright_file(spec, paths); error_count += check_for_exes(spec, paths); - std::vector<fs::path> dlls; - recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "bin", ".dll", dlls); - recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "bin", ".dll", dlls); - - error_count += check_exports_of_dlls(dlls); - error_count += check_uwp_bit_of_dlls(spec.target_triplet.system(), dlls); - error_count += check_architecture(spec.target_triplet.architecture(), dlls); + triplet::BuildType build_type = spec.target_triplet().build_type(); + switch (build_type) + { + case triplet::BuildType::DYNAMIC: + { + std::vector<fs::path> dlls; + recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "bin", ".dll", dlls); + recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "bin", ".dll", dlls); + + error_count += check_exports_of_dlls(dlls); + error_count += check_uwp_bit_of_dlls(spec.target_triplet().system(), dlls); + error_count += check_architecture(spec.target_triplet().architecture(), dlls); + break; + } + case triplet::BuildType::STATIC: + { + std::vector<fs::path> dlls; + recursive_find_files_with_extension_in_dir(paths.packages / spec.dir(), ".dll", dlls); + error_count += check_no_dlls_present(dlls); + break; + } + + default: + Checks::unreachable(); + } std::vector<fs::path> libs; recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "lib", ".lib", libs); recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "lib", ".lib", libs); - error_count += check_architecture(spec.target_triplet.architecture(), libs); + error_count += check_architecture(spec.target_triplet().architecture(), libs); if (error_count != 0) { - const fs::path portfile = paths.ports / spec.name / "portfile.cmake"; - System::println(System::color::error, "Found %d error(s). Please correct the portfile:\n %s", error_count, portfile.string()); + 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()); exit(EXIT_FAILURE); } diff --git a/toolsrc/src/test.cpp b/toolsrc/src/test.cpp index 82113abaa..fc49b362d 100644 --- a/toolsrc/src/test.cpp +++ b/toolsrc/src/test.cpp @@ -87,11 +87,11 @@ namespace UnitTest1 {"Multi-Arch", "same"}, }); - Assert::AreEqual("zlib", pgh.name.c_str()); + 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("a", pgh.target_triplet.value.c_str()); + Assert::AreEqual("a", pgh.spec.target_triplet().canonical_name().c_str()); Assert::AreEqual(size_t(0), pgh.depends.size()); } @@ -106,7 +106,7 @@ namespace UnitTest1 {"Description", "d"}, {"Depends", "bd"} }); - Assert::AreEqual("s", pgh.name.c_str()); + 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()); @@ -308,24 +308,24 @@ namespace UnitTest1 TEST_METHOD(package_spec_parse) { - vcpkg::expected<vcpkg::package_spec> spec = vcpkg::parse("zlib", vcpkg::triplet::X86_WINDOWS); - Assert::AreEqual(vcpkg::package_spec_parse_result::success, vcpkg::to_package_spec_parse_result(spec.error_code())); - Assert::AreEqual("zlib", spec.get()->name.c_str()); - Assert::AreEqual(vcpkg::triplet::X86_WINDOWS.value, spec.get()->target_triplet.value); + vcpkg::expected<vcpkg::package_spec> spec = vcpkg::package_spec::from_string("zlib", vcpkg::triplet::X86_WINDOWS); + Assert::AreEqual(vcpkg::package_spec_parse_result::SUCCESS, vcpkg::to_package_spec_parse_result(spec.error_code())); + Assert::AreEqual("zlib", spec.get()->name().c_str()); + Assert::AreEqual(vcpkg::triplet::X86_WINDOWS.canonical_name(), spec.get()->target_triplet().canonical_name()); } TEST_METHOD(package_spec_parse_with_arch) { - vcpkg::expected<vcpkg::package_spec> spec = vcpkg::parse("zlib:x64-uwp", vcpkg::triplet::X86_WINDOWS); - Assert::AreEqual(vcpkg::package_spec_parse_result::success, vcpkg::to_package_spec_parse_result(spec.error_code())); - Assert::AreEqual("zlib", spec.get()->name.c_str()); - Assert::AreEqual(vcpkg::triplet::X64_UWP.value, spec.get()->target_triplet.value); + vcpkg::expected<vcpkg::package_spec> spec = vcpkg::package_spec::from_string("zlib:x64-uwp", vcpkg::triplet::X86_WINDOWS); + Assert::AreEqual(vcpkg::package_spec_parse_result::SUCCESS, vcpkg::to_package_spec_parse_result(spec.error_code())); + Assert::AreEqual("zlib", spec.get()->name().c_str()); + Assert::AreEqual(vcpkg::triplet::X64_UWP.canonical_name(), spec.get()->target_triplet().canonical_name()); } TEST_METHOD(package_spec_parse_with_multiple_colon) { - auto ec = vcpkg::parse("zlib:x86-uwp:", vcpkg::triplet::X86_WINDOWS).error_code(); - Assert::AreEqual(vcpkg::package_spec_parse_result::too_many_colons, vcpkg::to_package_spec_parse_result(ec)); + auto ec = vcpkg::package_spec::from_string("zlib:x86-uwp:", vcpkg::triplet::X86_WINDOWS).error_code(); + Assert::AreEqual(vcpkg::package_spec_parse_result::TOO_MANY_COLONS, vcpkg::to_package_spec_parse_result(ec)); } TEST_METHOD(utf8_to_utf16) diff --git a/toolsrc/src/triplet.cpp b/toolsrc/src/triplet.cpp index 9ad3d8847..af2ca2a72 100644 --- a/toolsrc/src/triplet.cpp +++ b/toolsrc/src/triplet.cpp @@ -1,18 +1,18 @@ #include "triplet.h" -#include "vcpkg_System.h" #include "vcpkg_Checks.h" +#include <algorithm> namespace vcpkg { - const triplet triplet::X86_WINDOWS = {"x86-windows"}; - const triplet triplet::X64_WINDOWS = {"x64-windows"}; - const triplet triplet::X86_UWP = {"x86-uwp"}; - const triplet triplet::X64_UWP = {"x64-uwp"}; - const triplet triplet::ARM_UWP = {"arm-uwp"}; + 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"); std::string to_string(const triplet& t) { - return t.value; + return t.canonical_name(); } std::string to_printf_arg(const triplet& t) @@ -22,7 +22,7 @@ namespace vcpkg bool operator==(const triplet& left, const triplet& right) { - return left.value == right.value; + return left.canonical_name() == right.canonical_name(); } bool operator!=(const triplet& left, const triplet& right) @@ -35,25 +35,43 @@ namespace vcpkg return os << to_string(t); } - std::string triplet::architecture() const + triplet triplet::from_canonical_name(const std::string& triplet_as_string) + { + std::string s(triplet_as_string); + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + + auto it = std::find(s.cbegin(), s.cend(), '-'); + Checks::check_exit(it != s.cend(), "Invalid triplet: %s", triplet_as_string); + + triplet t; + t.m_canonical_name = s; + return t; + } + + const std::string& triplet::canonical_name() const { - if (*this == X86_WINDOWS || *this == X86_UWP) - return "x86"; - if (*this == X64_WINDOWS || *this == X64_UWP) - return "x64"; - if (*this == ARM_UWP) - return "arm"; + return this->m_canonical_name; + } - Checks::exit_with_message("Unknown architecture: %s", value); + std::string triplet::architecture() const + { + auto it = std::find(this->m_canonical_name.cbegin(), this->m_canonical_name.cend(), '-'); + return std::string(this->m_canonical_name.cbegin(), it); } std::string triplet::system() const { - if (*this == X86_WINDOWS || *this == X64_WINDOWS) - return "windows"; - if (*this == X86_UWP || *this == X64_UWP || *this == ARM_UWP) - return "uwp"; + auto it = std::find(this->m_canonical_name.cbegin(), this->m_canonical_name.cend(), '-'); + return std::string(it + 1, this->m_canonical_name.cend()); + } + + triplet::BuildType triplet::build_type() const + { + if (this->m_canonical_name.find("static") != std::string::npos) + { + return BuildType::STATIC; + } - Checks::exit_with_message("Unknown system: %s", value); + return BuildType::DYNAMIC; } } diff --git a/toolsrc/src/vcpkg_Checks.cpp b/toolsrc/src/vcpkg_Checks.cpp index d5433b1f5..db6c03480 100644 --- a/toolsrc/src/vcpkg_Checks.cpp +++ b/toolsrc/src/vcpkg_Checks.cpp @@ -34,8 +34,7 @@ namespace vcpkg {namespace Checks { if (!expression) { - System::println(System::color::error, errorMessage); - exit(EXIT_FAILURE); + exit_with_message(errorMessage); } } }} diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp new file mode 100644 index 000000000..54b37cd11 --- /dev/null +++ b/toolsrc/src/vcpkg_Dependencies.cpp @@ -0,0 +1,67 @@ +#include "vcpkg_Dependencies.h" +#include <vector> +#include "vcpkg_Graphs.h" +#include "vcpkg_paths.h" +#include "package_spec.h" +#include "StatusParagraphs.h" +#include <unordered_set> +#include "vcpkg.h" +#include "vcpkg_Maps.h" +#include "vcpkg_Sets.h" + +namespace vcpkg { namespace Dependencies +{ + static Graphs::Graph<package_spec> build_dependency_graph(const vcpkg_paths& paths, const std::vector<package_spec>& specs, const StatusParagraphs& status_db) + { + std::vector<package_spec> examine_stack(specs); + std::unordered_set<package_spec> was_examined; // Examine = we have checked its immediate (non-recursive) dependencies + Graphs::Graph<package_spec> graph; + graph.add_vertices(examine_stack); + + while (!examine_stack.empty()) + { + const package_spec spec = examine_stack.back(); + examine_stack.pop_back(); + + if (was_examined.find(spec) != was_examined.end()) + { + continue; + } + + std::vector<std::string> dependencies_as_string = get_unmet_package_dependencies(paths, spec, status_db); + + for (const std::string& dep_as_string : dependencies_as_string) + { + const package_spec current_dep = package_spec::from_name_and_triplet(dep_as_string, spec.target_triplet()).get_or_throw(); + auto it = status_db.find(current_dep.name(), current_dep.target_triplet()); + if (it != status_db.end() && (*it)->want == want_t::install) + { + continue; + } + + graph.add_edge(spec, current_dep); + if (was_examined.find(current_dep) == was_examined.end()) + { + examine_stack.push_back(std::move(current_dep)); + } + } + + was_examined.insert(spec); + } + + return graph; + } + + std::vector<package_spec> create_dependency_ordered_install_plan(const vcpkg_paths& paths, const std::vector<package_spec>& specs, const StatusParagraphs& status_db) + { + return build_dependency_graph(paths, specs, status_db).find_topological_sort(); + } + + std::unordered_set<package_spec> find_unmet_dependencies(const vcpkg_paths& paths, const package_spec& spec, const StatusParagraphs& status_db) + { + const Graphs::Graph<package_spec> dependency_graph = build_dependency_graph(paths, {spec}, status_db); + std::unordered_set<package_spec> key_set = Maps::extract_key_set(dependency_graph.adjacency_list()); + key_set.erase(spec); + return key_set; + } +}} diff --git a/toolsrc/src/vcpkg_Environment.cpp b/toolsrc/src/vcpkg_Environment.cpp index f70f2b893..d98b0f220 100644 --- a/toolsrc/src/vcpkg_Environment.cpp +++ b/toolsrc/src/vcpkg_Environment.cpp @@ -48,11 +48,11 @@ namespace vcpkg {namespace Environment void ensure_git_on_path(const vcpkg_paths& paths) { const fs::path downloaded_git = paths.downloads / "PortableGit" / "cmd"; - const std::wstring path_buf = Strings::format(L"%s;%s;%s;%s", - downloaded_git.native(), - System::wdupenv_str(L"PATH"), - default_git_installation_dir.native(), - default_git_installation_dir_x86.native()); + const std::wstring path_buf = Strings::wformat(L"%s;%s;%s;%s", + downloaded_git.native(), + System::wdupenv_str(L"PATH"), + default_git_installation_dir.native(), + default_git_installation_dir_x86.native()); _wputenv_s(L"PATH", path_buf.c_str()); static constexpr std::array<int, 3> git_version = {2,0,0}; @@ -63,11 +63,11 @@ namespace vcpkg {namespace Environment void ensure_cmake_on_path(const vcpkg_paths& paths) { const fs::path downloaded_cmake = paths.downloads / "cmake-3.5.2-win32-x86" / "bin"; - const std::wstring path_buf = Strings::format(L"%s;%s;%s;%s", - downloaded_cmake.native(), - System::wdupenv_str(L"PATH"), - default_cmake_installation_dir.native(), - default_cmake_installation_dir_x86.native()); + const std::wstring path_buf = Strings::wformat(L"%s;%s;%s;%s", + downloaded_cmake.native(), + System::wdupenv_str(L"PATH"), + default_cmake_installation_dir.native(), + default_cmake_installation_dir_x86.native()); _wputenv_s(L"PATH", path_buf.c_str()); static constexpr std::array<int, 3> cmake_version = {3,5,0}; @@ -77,7 +77,7 @@ namespace vcpkg {namespace Environment void ensure_nuget_on_path(const vcpkg_paths& paths) { - const std::wstring path_buf = Strings::format(L"%s;%s", paths.downloads.native(), System::wdupenv_str(L"PATH")); + const std::wstring path_buf = Strings::wformat(L"%s;%s", paths.downloads.native(), System::wdupenv_str(L"PATH")); _wputenv_s(L"PATH", path_buf.c_str()); static constexpr std::array<int, 3> nuget_version = {1,0,0}; diff --git a/toolsrc/src/vcpkg_Files.cpp b/toolsrc/src/vcpkg_Files.cpp index 49a661157..611aa7450 100644 --- a/toolsrc/src/vcpkg_Files.cpp +++ b/toolsrc/src/vcpkg_Files.cpp @@ -1,16 +1,24 @@ #include "vcpkg_Files.h" #include <fstream> #include <filesystem> +#include <regex> namespace fs = std::tr2::sys; namespace vcpkg {namespace Files { + static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])"); + void check_is_directory(const fs::path& dirpath) { Checks::check_throw(fs::is_directory(dirpath), "The path %s is not a directory", dirpath.string()); } + bool has_invalid_chars_for_filesystem(const std::string s) + { + return std::regex_search(s, FILESYSTEM_INVALID_CHARACTERS_REGEX); + } + expected<std::string> get_contents(const fs::path& file_path) noexcept { std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary); @@ -35,4 +43,19 @@ namespace vcpkg {namespace Files return std::move(output); } + + fs::path find_file_recursively_up(const fs::path& starting_dir, const std::string& filename) + { + fs::path current_dir = starting_dir; + for (; !current_dir.empty(); current_dir = current_dir.parent_path()) + { + const fs::path candidate = current_dir / filename; + if (fs::exists(candidate)) + { + break; + } + } + + return current_dir; + } }} diff --git a/toolsrc/src/vcpkg_Input.cpp b/toolsrc/src/vcpkg_Input.cpp new file mode 100644 index 000000000..f7aae1929 --- /dev/null +++ b/toolsrc/src/vcpkg_Input.cpp @@ -0,0 +1,52 @@ +#include "vcpkg_Input.h" +#include "vcpkg_System.h" +#include "metrics.h" +#include "vcpkg_Commands.h" + +namespace vcpkg {namespace Input +{ + package_spec check_and_get_package_spec(const std::string& package_spec_as_string, const triplet& default_target_triplet, const char* example_text) + { + const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string); + expected<package_spec> expected_spec = package_spec::from_string(as_lowercase, default_target_triplet); + if (auto spec = expected_spec.get()) + { + return *spec; + } + + // Intentionally show the lowercased string + System::println(System::color::error, "Error: %s: %s", expected_spec.error_code().message(), as_lowercase); + System::print(example_text); + exit(EXIT_FAILURE); + } + + std::vector<package_spec> check_and_get_package_specs(const std::vector<std::string>& package_specs_as_strings, const triplet& default_target_triplet, const char* example_text) + { + std::vector<package_spec> specs; + for (const std::string& spec : package_specs_as_strings) + { + specs.push_back(check_and_get_package_spec(spec, default_target_triplet, example_text)); + } + + return specs; + } + + void check_triplet(const triplet& t, const vcpkg_paths& paths) + { + if (!paths.is_valid_triplet(t)) + { + System::println(System::color::error, "Error: invalid triplet: %s", t.canonical_name()); + TrackProperty("error", "invalid triplet: " + t.canonical_name()); + help_topic_valid_triplet(paths); + exit(EXIT_FAILURE); + } + } + + void check_triplets(std::vector<package_spec> triplets, const vcpkg_paths& paths) + { + for (const package_spec& spec : triplets) + { + check_triplet(spec.target_triplet(), paths); + } + } +}} diff --git a/toolsrc/src/vcpkg_Strings.cpp b/toolsrc/src/vcpkg_Strings.cpp index b0312536a..56eeae7a0 100644 --- a/toolsrc/src/vcpkg_Strings.cpp +++ b/toolsrc/src/vcpkg_Strings.cpp @@ -20,7 +20,7 @@ namespace vcpkg {namespace Strings {namespace details return output; } - std::wstring format_internal(const wchar_t* fmtstr, ...) + std::wstring wformat_internal(const wchar_t* fmtstr, ...) { va_list lst; va_start(lst, fmtstr); @@ -48,13 +48,20 @@ namespace vcpkg {namespace Strings return conversion.to_bytes(w); } - std::string::const_iterator case_insensitive_find(const std::string& s, const std::string& pattern) + std::string::const_iterator case_insensitive_ascii_find(const std::string& s, const std::string& pattern) { - std::string patter_as_lower_case; - std::transform(pattern.begin(), pattern.end(), back_inserter(patter_as_lower_case), tolower); - return search(s.begin(), s.end(), patter_as_lower_case.begin(), patter_as_lower_case.end(), [](const char a, const char b) + std::string pattern_as_lower_case; + std::transform(pattern.begin(), pattern.end(), back_inserter(pattern_as_lower_case), tolower); + return search(s.begin(), s.end(), pattern_as_lower_case.begin(), pattern_as_lower_case.end(), [](const char a, const char b) { - return (tolower(a) == b); + return tolower(a) == b; }); } + + std::string ascii_to_lowercase(const std::string& input) + { + std::string output = input; + std::transform(output.begin(), output.end(), output.begin(), ::tolower); + return output; + } }} diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp index 71b4087d2..4dc37857d 100644 --- a/toolsrc/src/vcpkg_System.cpp +++ b/toolsrc/src/vcpkg_System.cpp @@ -19,14 +19,14 @@ namespace vcpkg {namespace System int cmd_execute(const wchar_t* cmd_line) { // Basically we are wrapping it in quotes - const std::wstring& actual_cmd_line = Strings::format(LR"###("%s")###", cmd_line); + const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); int exit_code = _wsystem(actual_cmd_line.c_str()); return exit_code; } exit_code_and_output cmd_execute_and_capture_output(const wchar_t* cmd_line) { - const std::wstring& actual_cmd_line = Strings::format(LR"###("%s")###", cmd_line); + const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); std::string output; char buf[1024]; diff --git a/toolsrc/src/vcpkg_cmd_arguments.cpp b/toolsrc/src/vcpkg_cmd_arguments.cpp index 4cfc12716..a286ba9b7 100644 --- a/toolsrc/src/vcpkg_cmd_arguments.cpp +++ b/toolsrc/src/vcpkg_cmd_arguments.cpp @@ -166,90 +166,51 @@ namespace vcpkg return output; } - void vcpkg_cmd_arguments::check_max_args(size_t arg_count, const char* example_text) const + void vcpkg_cmd_arguments::check_max_arg_count(const size_t expected_arg_count) const { - if (command_arguments.size() > arg_count) - { - System::println(System::color::error, "Error: too many arguments to command %s", command); - if (example_text != nullptr) - print_example(example_text); - else - print_usage(); - exit(EXIT_FAILURE); - } + return check_max_arg_count(expected_arg_count, ""); } - std::vector<package_spec> vcpkg_cmd_arguments::extract_package_specs_with_unmet_dependencies(const vcpkg_paths& paths, const triplet& default_target_triplet, const StatusParagraphs& status_db) const + void vcpkg_cmd_arguments::check_min_arg_count(const size_t expected_arg_count) const { - std::vector<package_spec> specs = parse_all_arguments_as_package_specs(default_target_triplet); - std::unordered_set<package_spec> had_its_immediate_dependencies_added; - Graphs::Graph<package_spec> graph; - graph.add_vertices(specs); - - while (!specs.empty()) - { - package_spec spec = specs.back(); - specs.pop_back(); - - if (had_its_immediate_dependencies_added.find(spec) != had_its_immediate_dependencies_added.end()) - { - continue; - } - - std::vector<std::string> dependencies_as_string = get_unmet_package_dependencies(paths, spec, status_db); - - for (const std::string& dep_as_string : dependencies_as_string) - { - package_spec current_dep = {dep_as_string, spec.target_triplet}; - auto it = status_db.find(current_dep.name, current_dep.target_triplet); - if (it != status_db.end() && (*it)->want == want_t::install) - { - continue; - } + return check_min_arg_count(expected_arg_count, ""); + } - graph.add_edge(spec, current_dep); - if (had_its_immediate_dependencies_added.find(current_dep) == had_its_immediate_dependencies_added.end()) - { - specs.push_back(std::move(current_dep)); - } - } + void vcpkg_cmd_arguments::check_exact_arg_count(const size_t expected_arg_count) const + { + return check_exact_arg_count(expected_arg_count, ""); + } - had_its_immediate_dependencies_added.insert(spec); + void vcpkg_cmd_arguments::check_max_arg_count(const size_t expected_arg_count, const char* 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); + exit(EXIT_FAILURE); } - - return graph.find_topological_sort(); } - std::vector<package_spec> vcpkg_cmd_arguments::parse_all_arguments_as_package_specs(const triplet& default_target_triplet, const char* example_text) const + void vcpkg_cmd_arguments::check_min_arg_count(const size_t expected_arg_count, const char* example_text) const { - size_t arg_count = command_arguments.size(); - if (arg_count < 1) + const size_t actual_arg_count = command_arguments.size(); + if (actual_arg_count < expected_arg_count) { - System::println(System::color::error, "Error: %s requires one or more package specifiers", this->command); - if (example_text == nullptr) - print_example(Strings::format("%s zlib zlib:x64-windows curl boost", this->command).c_str()); - else - print_example(example_text); + 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); exit(EXIT_FAILURE); } - std::vector<package_spec> specs; - specs.reserve(arg_count); + } - for (const std::string& command_argument : command_arguments) + void vcpkg_cmd_arguments::check_exact_arg_count(const size_t expected_arg_count, const char* example_text) const + { + const size_t actual_arg_count = command_arguments.size(); + if (actual_arg_count != expected_arg_count) { - expected<package_spec> current_spec = vcpkg::parse(command_argument, default_target_triplet); - if (auto spec = current_spec.get()) - { - specs.push_back(std::move(*spec)); - } - else - { - System::println(System::color::error, "Error: %s: %s", current_spec.error_code().message(), command_argument); - print_example(Strings::format("%s zlib:x64-windows", this->command).c_str()); - exit(EXIT_FAILURE); - } + 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); + exit(EXIT_FAILURE); } - - return specs; } } diff --git a/toolsrc/src/vcpkg_paths.cpp b/toolsrc/src/vcpkg_paths.cpp index 10b6d992a..1f9eb0bc5 100644 --- a/toolsrc/src/vcpkg_paths.cpp +++ b/toolsrc/src/vcpkg_paths.cpp @@ -3,8 +3,7 @@ #include "vcpkg_paths.h" #include "metrics.h" #include "vcpkg_System.h" - -namespace fs = std::tr2::sys; +#include "package_spec.h" namespace vcpkg { @@ -45,4 +44,29 @@ namespace vcpkg paths.ports_cmake = paths.root / "scripts" / "ports.cmake"; return paths; } + + fs::path vcpkg_paths::package_dir(const package_spec& spec) const + { + return this->packages / spec.dir(); + } + + fs::path vcpkg_paths::port_dir(const package_spec& spec) const + { + return this->ports / spec.name(); + } + + bool vcpkg_paths::is_valid_triplet(const triplet& t) const + { + auto it = fs::directory_iterator(this->triplets); + for (; it != fs::directory_iterator(); ++it) + { + std::string triplet_file_name = it->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; + } } diff --git a/toolsrc/src/vcpkglib_helpers.cpp b/toolsrc/src/vcpkglib_helpers.cpp index e947dc647..3aa3735b0 100644 --- a/toolsrc/src/vcpkglib_helpers.cpp +++ b/toolsrc/src/vcpkglib_helpers.cpp @@ -4,29 +4,28 @@ namespace vcpkg {namespace details { - void optional_field(const std::unordered_map<std::string, std::string>& fields, std::string& out, const std::string& fieldname) + std::string optional_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname) { auto it = fields.find(fieldname); if (it == fields.end()) { - out.clear(); + return std::string(); } - else - { - out = it->second; - } + return it->second; }; - void required_field(const std::unordered_map<std::string, std::string>& fields, std::string& out, const std::string& fieldname) + std::string required_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname) { auto it = fields.find(fieldname); vcpkg::Checks::check_throw(it != fields.end(), "Required field not present: %s", fieldname); - out = it->second; + return it->second; }; - void parse_depends(const std::string& depends_string, std::vector<std::string>& out) + std::vector<std::string> parse_depends(const std::string& depends_string) { + std::vector<std::string> out; + size_t cur = 0; do { @@ -34,17 +33,21 @@ namespace vcpkg {namespace details if (pos == std::string::npos) { out.push_back(depends_string.substr(cur)); - return; + break; } out.push_back(depends_string.substr(cur, pos - cur)); // skip comma and space ++pos; if (depends_string[pos] == ' ') + { ++pos; + } cur = pos; } while (cur != std::string::npos); + + return out; } }} |
