aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorWimok Nupphiboon <wimok.mok@gmail.com>2018-03-24 11:53:55 +0700
committerWimok Nupphiboon <wimok.mok@gmail.com>2018-03-24 11:53:55 +0700
commit663be4bbffd435cf5e5fc62a0774c784c10ddc68 (patch)
treed214f24405fa75d4dad9dfb938a04846d2ca6102 /toolsrc/src
parent1e380dde21317e73d1859dad1c64c06eb88cc502 (diff)
parentaa57df6d6ed6d17000522492b66fc93d3f32ab86 (diff)
downloadvcpkg-663be4bbffd435cf5e5fc62a0774c784c10ddc68.tar.gz
vcpkg-663be4bbffd435cf5e5fc62a0774c784c10ddc68.zip
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/tests.plan.cpp114
-rw-r--r--toolsrc/src/vcpkg.cpp12
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp4
-rw-r--r--toolsrc/src/vcpkg/base/strings.cpp20
-rw-r--r--toolsrc/src/vcpkg/base/system.cpp74
-rw-r--r--toolsrc/src/vcpkg/build.cpp462
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp159
-rw-r--r--toolsrc/src/vcpkg/commands.edit.cpp20
-rw-r--r--toolsrc/src/vcpkg/commands.env.cpp54
-rw-r--r--toolsrc/src/vcpkg/commands.hash.cpp126
-rw-r--r--toolsrc/src/vcpkg/commands.integrate.cpp6
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp13
-rw-r--r--toolsrc/src/vcpkg/help.cpp6
-rw-r--r--toolsrc/src/vcpkg/install.cpp61
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp2
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp2
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp10
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp103
18 files changed, 921 insertions, 327 deletions
diff --git a/toolsrc/src/tests.plan.cpp b/toolsrc/src/tests.plan.cpp
index 08d3c1dab..a99f1abb9 100644
--- a/toolsrc/src/tests.plan.cpp
+++ b/toolsrc/src/tests.plan.cpp
@@ -593,6 +593,87 @@ namespace UnitTest1
features_check(&install_plan[0], "a", {"core"}, Triplet::X64_WINDOWS);
}
+ TEST_METHOD(install_plan_action_dependencies)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_c = spec_map.emplace("c");
+ auto spec_b = spec_map.emplace("b", "c");
+ spec_map.emplace("a", "b");
+
+ // Install "a" (without explicit feature specification)
+ auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map,
+ FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ Assert::IsTrue(install_plan.size() == 3);
+ features_check(&install_plan[0], "c", {"core"}, Triplet::X64_WINDOWS);
+
+ features_check(&install_plan[1], "b", {"core"}, Triplet::X64_WINDOWS);
+ Assert::IsTrue(install_plan[1].install_action.get()->computed_dependencies ==
+ std::vector<PackageSpec>{spec_c});
+
+ features_check(&install_plan[2], "a", {"core"}, Triplet::X64_WINDOWS);
+ Assert::IsTrue(install_plan[2].install_action.get()->computed_dependencies ==
+ std::vector<PackageSpec>{spec_b});
+ }
+
+ TEST_METHOD(install_plan_action_dependencies_2)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_c = spec_map.emplace("c");
+ auto spec_b = spec_map.emplace("b", "c");
+ spec_map.emplace("a", "c, b");
+
+ // Install "a" (without explicit feature specification)
+ auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map,
+ FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ Assert::IsTrue(install_plan.size() == 3);
+ features_check(&install_plan[0], "c", {"core"}, Triplet::X64_WINDOWS);
+
+ features_check(&install_plan[1], "b", {"core"}, Triplet::X64_WINDOWS);
+ Assert::IsTrue(install_plan[1].install_action.get()->computed_dependencies ==
+ std::vector<PackageSpec>{spec_c});
+
+ features_check(&install_plan[2], "a", {"core"}, Triplet::X64_WINDOWS);
+ Assert::IsTrue(install_plan[2].install_action.get()->computed_dependencies ==
+ std::vector<PackageSpec>{spec_b, spec_c});
+ }
+
+ TEST_METHOD(install_plan_action_dependencies_3)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "", {{"0", ""}, {"1", "a[0]"}}, {"1"});
+
+ // Install "a" (without explicit feature specification)
+ auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map,
+ FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ Assert::IsTrue(install_plan.size() == 1);
+ features_check(&install_plan[0], "a", {"1", "0", "core"}, Triplet::X64_WINDOWS);
+ Assert::IsTrue(install_plan[0].install_action.get()->computed_dependencies == std::vector<PackageSpec>{});
+ }
+
TEST_METHOD(upgrade_with_default_features_1)
{
std::vector<std::unique_ptr<StatusParagraph>> pghs;
@@ -619,7 +700,6 @@ namespace UnitTest1
Assert::IsTrue(plan[0].remove_action.has_value());
Assert::AreEqual("a", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].install_action.has_value());
features_check(&plan[1], "a", {"core", "0"}, Triplet::X86_WINDOWS);
}
@@ -651,11 +731,9 @@ namespace UnitTest1
Assert::IsTrue(plan[0].remove_action.has_value());
Assert::AreEqual("b", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].install_action.has_value());
features_check(&plan[1], "b", {"b0", "core"}, Triplet::X64_WINDOWS);
Assert::AreEqual("a", plan[2].spec().name().c_str());
- Assert::IsTrue(plan[2].install_action.has_value());
features_check(&plan[2], "a", {"core"}, Triplet::X64_WINDOWS);
}
@@ -996,6 +1074,34 @@ namespace UnitTest1
features_check(&plan[1], "a", {"core", "a1"});
}
+
+ TEST_METHOD(basic_upgrade_scheme_with_self_features)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_feature_pgh("a", "a1", ""));
+ pghs.push_back(make_status_feature_pgh("a", "a2", "a[a1]"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map(Triplet::X86_WINDOWS);
+ auto spec_a = spec_map.emplace("a", "", {{"a1", ""}, {"a2", "a[a1]"}});
+
+ Dependencies::MapPortFileProvider provider(spec_map.map);
+ Dependencies::PackageGraph graph(provider, status_db);
+
+ graph.upgrade(spec_a);
+
+ auto plan = graph.serialize();
+
+ Assert::AreEqual(size_t(2), plan.size());
+
+ Assert::AreEqual("a", plan[0].spec().name().c_str());
+ Assert::IsTrue(plan[0].remove_action.has_value());
+
+ Assert::AreEqual("a", plan[1].spec().name().c_str());
+ Assert::IsTrue(plan[1].install_action.has_value());
+ Assert::IsTrue(plan[1].install_action.get()->feature_list == std::set<std::string>{"core", "a1", "a2"});
+ }
};
class ExportPlanTests : public TestClass<ExportPlanTests>
@@ -1069,7 +1175,7 @@ namespace UnitTest1
Assert::IsTrue(plan[0].plan_type == Dependencies::ExportPlanType::NOT_BUILT);
}
- TEST_METHOD(basic_upgrade_scheme_with_features)
+ TEST_METHOD(basic_export_scheme_with_features)
{
std::vector<std::unique_ptr<StatusParagraph>> pghs;
pghs.push_back(make_status_pgh("b"));
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index ef68e6f72..a65045aa8 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -94,7 +94,9 @@ static void inner(const VcpkgCmdArguments& args)
Checks::check_exit(VCPKG_LINE_INFO, !vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root.");
- const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir);
+ auto default_vs_path = System::get_environment_variable("VCPKG_DEFAULT_VS_PATH").value_or("");
+
+ const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir, default_vs_path);
Checks::check_exit(VCPKG_LINE_INFO,
!expected_paths.error(),
"Error: Invalid vcpkg root directory %s: %s",
@@ -155,7 +157,15 @@ static void inner(const VcpkgCmdArguments& args)
}
else
{
+#if defined(_WIN32)
default_triplet = Triplet::X86_WINDOWS;
+#elif defined(__APPLE__)
+ default_triplet = Triplet::from_canonical_name("x64-osx");
+#elif defined(__FreeBSD__)
+ default_triplet = Triplet::from_canonical_name("x64-freebsd");
+#else
+ default_triplet = Triplet::from_canonical_name("x64-linux");
+#endif
}
}
diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index b59104a59..4e61666b7 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -108,6 +108,10 @@ namespace vcpkg::Files
output.close();
}
+ virtual void rename(const fs::path& oldpath, const fs::path& newpath, std::error_code& ec) override
+ {
+ fs::stdfs::rename(oldpath, newpath, ec);
+ }
virtual void rename(const fs::path& oldpath, const fs::path& newpath) override
{
fs::stdfs::rename(oldpath, newpath);
diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp
index d912734e3..5fedf3e1f 100644
--- a/toolsrc/src/vcpkg/base/strings.cpp
+++ b/toolsrc/src/vcpkg/base/strings.cpp
@@ -7,10 +7,11 @@
namespace vcpkg::Strings::details
{
// To disambiguate between two overloads
- static const auto isspace = [](const char c) { return std::isspace(c); };
+ static bool IS_SPACE(const char c) { return std::isspace(c) != 0; };
// Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower()
static char tolower_char(const char c) { return static_cast<char>(std::tolower(c)); }
+ static char toupper_char(const char c) { return static_cast<char>(std::toupper(c)); }
#if defined(_WIN32)
static _locale_t& c_locale()
@@ -108,11 +109,16 @@ namespace vcpkg::Strings
#endif
}
- std::string ascii_to_lowercase(const std::string& input)
+ std::string ascii_to_lowercase(std::string s)
{
- std::string output(input);
- std::transform(output.begin(), output.end(), output.begin(), &details::tolower_char);
- return output;
+ std::transform(s.begin(), s.end(), s.begin(), &details::tolower_char);
+ return s;
+ }
+
+ std::string ascii_to_uppercase(std::string s)
+ {
+ std::transform(s.begin(), s.end(), s.begin(), &details::toupper_char);
+ return s;
}
bool case_insensitive_ascii_starts_with(const std::string& s, const std::string& pattern)
@@ -137,8 +143,8 @@ namespace vcpkg::Strings
std::string 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));
+ s.erase(std::find_if_not(s.rbegin(), s.rend(), details::IS_SPACE).base(), s.end());
+ s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), details::IS_SPACE));
return std::move(s);
}
diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp
index e55db9461..171dd2bbf 100644
--- a/toolsrc/src/vcpkg/base/system.cpp
+++ b/toolsrc/src/vcpkg/base/system.cpp
@@ -8,7 +8,11 @@
#include <time.h>
#if defined(__APPLE__)
-# include <mach-o/dyld.h>
+#include <mach-o/dyld.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <sys/sysctl.h>
#endif
#pragma comment(lib, "Advapi32")
@@ -35,14 +39,22 @@ namespace vcpkg::System
const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
if (bytes == 0) std::abort();
return fs::path(buf, buf + bytes);
-#elif __APPLE__
+#elif defined(__APPLE__)
uint32_t size = 1024 * 32;
char buf[size] = {};
bool result = _NSGetExecutablePath(buf, &size);
Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
- std::unique_ptr<char> canonicalPath (realpath(buf, NULL));
+ std::unique_ptr<char> canonicalPath(realpath(buf, NULL));
Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
return fs::path(std::string(canonicalPath.get()));
+#elif defined(__FreeBSD__)
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ char exePath[2048];
+ size_t len = sizeof(exePath);
+ auto rcode = sysctl(mib, 4, exePath, &len, NULL, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, rcode == 0, "Could not determine current executable path.");
+ Checks::check_exit(VCPKG_LINE_INFO, len > 0, "Could not determine current executable path.");
+ return fs::path(exePath, exePath + len - 1);
#else /* LINUX */
std::array<char, 1024 * 4> buf;
auto written = readlink("/proc/self/exe", buf.data(), buf.size());
@@ -141,12 +153,12 @@ namespace vcpkg::System
R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), args);
}
- int cmd_execute_clean(const CStringView cmd_line)
+ int cmd_execute_clean(const CStringView cmd_line, const std::unordered_map<std::string, std::string>& extra_env)
{
#if defined(_WIN32)
static const std::string SYSTEM_ROOT = get_environment_variable("SystemRoot").value_or_exit(VCPKG_LINE_INFO);
static const std::string SYSTEM_32 = SYSTEM_ROOT + R"(\system32)";
- static const std::string NEW_PATH = Strings::format(
+ std::string NEW_PATH = Strings::format(
R"(Path=%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)", SYSTEM_32, SYSTEM_ROOT, SYSTEM_32, SYSTEM_32);
std::vector<std::wstring> env_wstrings = {
@@ -212,11 +224,22 @@ namespace vcpkg::System
env_cstr.push_back(L'\0');
}
+ if (extra_env.find("PATH") != extra_env.end())
+ NEW_PATH += Strings::format(";%s", extra_env.find("PATH")->second);
env_cstr.append(Strings::to_utf16(NEW_PATH));
env_cstr.push_back(L'\0');
env_cstr.append(L"VSLANG=1033");
env_cstr.push_back(L'\0');
+ for (auto item : extra_env)
+ {
+ if (item.first == "PATH") continue;
+ env_cstr.append(Strings::to_utf16(item.first));
+ env_cstr.push_back(L'=');
+ env_cstr.append(Strings::to_utf16(item.second));
+ env_cstr.push_back(L'\0');
+ }
+
STARTUPINFOW startup_info;
memset(&startup_info, 0, sizeof(STARTUPINFOW));
startup_info.cb = sizeof(STARTUPINFOW);
@@ -262,14 +285,14 @@ namespace vcpkg::System
fflush(nullptr);
// Basically we are wrapping it in quotes
- const std::string& actual_cmd_line = Strings::format(R"###("%s")###", cmd_line);
#if defined(_WIN32)
+ const std::string& actual_cmd_line = Strings::format(R"###("%s")###", cmd_line);
Debug::println("_wsystem(%s)", actual_cmd_line);
const int exit_code = _wsystem(Strings::to_utf16(actual_cmd_line).c_str());
Debug::println("_wsystem() returned %d", exit_code);
#else
- Debug::println("_system(%s)", actual_cmd_line);
- const int exit_code = system(actual_cmd_line.c_str());
+ Debug::println("_system(%s)", cmd_line);
+ const int exit_code = system(cmd_line.c_str());
Debug::println("_system() returned %d", exit_code);
#endif
return exit_code;
@@ -291,6 +314,8 @@ namespace vcpkg::System
// Flush stdout before launching external process
fflush(stdout);
+ auto timer = Chrono::ElapsedTimer::create_started();
+
#if defined(_WIN32)
const auto actual_cmd_line = Strings::format(R"###("%s 2>&1")###", cmd_line);
@@ -312,8 +337,10 @@ namespace vcpkg::System
}
const auto ec = _pclose(pipe);
- Debug::println("_pclose() returned %d", ec);
remove_byte_order_marks(&output);
+
+ Debug::println("_pclose() returned %d after %8d us", ec, (int)timer.microseconds());
+
return {ec, Strings::to_utf8(output)};
#else
const auto actual_cmd_line = Strings::format(R"###(%s 2>&1)###", cmd_line);
@@ -336,7 +363,9 @@ namespace vcpkg::System
}
const auto ec = pclose(pipe);
- Debug::println("pclose() returned %d", ec);
+
+ Debug::println("_pclose() returned %d after %8d us", ec, (int)timer.microseconds());
+
return {ec, output};
#endif
}
@@ -502,32 +531,41 @@ namespace vcpkg::System
}
#endif
- static const fs::path& get_program_files()
+ static const Optional<fs::path>& get_program_files()
{
- static const fs::path PATH = System::get_environment_variable("PROGRAMFILES").value_or_exit(VCPKG_LINE_INFO);
+ static const auto PATH = []() -> Optional<fs::path> {
+ auto value = System::get_environment_variable("PROGRAMFILES");
+ if (auto v = value.get())
+ {
+ return *v;
+ }
+
+ return nullopt;
+ }();
+
return PATH;
}
- const fs::path& get_program_files_32_bit()
+ const Optional<fs::path>& get_program_files_32_bit()
{
- static const fs::path PATH = []() -> fs::path {
+ static const auto PATH = []() -> Optional<fs::path> {
auto value = System::get_environment_variable("ProgramFiles(x86)");
if (auto v = value.get())
{
- return std::move(*v);
+ return *v;
}
return get_program_files();
}();
return PATH;
}
- const fs::path& get_program_files_platform_bitness()
+ const Optional<fs::path>& get_program_files_platform_bitness()
{
- static const fs::path PATH = []() -> fs::path {
+ static const auto PATH = []() -> Optional<fs::path> {
auto value = System::get_environment_variable("ProgramW6432");
if (auto v = value.get())
{
- return std::move(*v);
+ return *v;
}
return get_program_files();
}();
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index 5870bd187..953e77460 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -209,6 +209,7 @@ namespace vcpkg::Build
std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset)
{
if (pre_build_info.external_toolchain_file.has_value()) return "";
+ if (!pre_build_info.cmake_system_name.empty() && pre_build_info.cmake_system_name != "WindowsStore") return "";
const char* tonull = " >nul";
if (GlobalState::debugging)
@@ -272,12 +273,10 @@ namespace vcpkg::Build
return filter_dependencies(config.scf.core_paragraph->depends, triplet);
}
- auto it =
- Util::find_if(config.scf.feature_paragraphs,
- [&](std::unique_ptr<FeatureParagraph> const& fpgh) { return fpgh->name == feature; });
- Checks::check_exit(VCPKG_LINE_INFO, it != config.scf.feature_paragraphs.end());
+ auto maybe_feature = config.scf.find_feature(feature);
+ Checks::check_exit(VCPKG_LINE_INFO, maybe_feature.has_value());
- return filter_dependencies(it->get()->depends, triplet);
+ return filter_dependencies(maybe_feature.get()->depends, triplet);
});
auto dep_fspecs = FeatureSpec::from_strings_and_triplet(dep_strings, triplet);
@@ -314,63 +313,134 @@ namespace vcpkg::Build
}
static ExtendedBuildResult do_build_package(const VcpkgPaths& paths,
+ const PreBuildInfo& pre_build_info,
+ const PackageSpec& spec,
+ const std::string& abi_tag,
const BuildPackageConfig& config,
const StatusParagraphs& status_db)
{
auto& fs = paths.get_filesystem();
- const Triplet& triplet = config.triplet;
+ const Triplet& triplet = spec.triplet();
+
+ const fs::path& cmake_exe_path = paths.get_cmake_exe();
+ const fs::path& git_exe_path = paths.get_git_exe();
- struct AbiEntry
+ std::string all_features;
+ for (auto& feature : config.scf.feature_paragraphs)
{
- std::string key;
- std::string value;
- };
+ all_features.append(feature->name + ";");
+ }
- std::vector<AbiEntry> abi_tag_entries;
+ const Toolset& toolset = paths.get_toolset(pre_build_info);
+ const std::string cmd_launch_cmake = System::make_cmake_cmd(
+ cmake_exe_path,
+ paths.ports_cmake,
+ {
+ {"CMD", "BUILD"},
+ {"PORT", config.scf.core_paragraph->name},
+ {"CURRENT_PORT_DIR", config.port_dir / "/."},
+ {"TARGET_TRIPLET", spec.triplet().canonical_name()},
+ {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
+ {"VCPKG_USE_HEAD_VERSION",
+ Util::Enum::to_bool(config.build_package_options.use_head_version) ? "1" : "0"},
+ {"_VCPKG_NO_DOWNLOADS", !Util::Enum::to_bool(config.build_package_options.allow_downloads) ? "1" : "0"},
+ {"GIT", git_exe_path},
+ {"FEATURES", Strings::join(";", config.feature_list)},
+ {"ALL_FEATURES", all_features},
+ });
- const PackageSpec spec =
- PackageSpec::from_name_and_triplet(config.scf.core_paragraph->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+ auto command = make_build_env_cmd(pre_build_info, toolset);
+ if (!command.empty()) command.append(" && ");
+ command.append(cmd_launch_cmake);
- std::vector<FeatureSpec> required_fspecs = compute_required_feature_specs(config, status_db);
+ const auto timer = Chrono::ElapsedTimer::create_started();
- // extract out the actual package ids
- auto dep_pspecs = Util::fmap(required_fspecs, [](FeatureSpec const& fspec) { return fspec.spec(); });
- Util::sort_unique_erase(dep_pspecs);
-
- // Find all features that aren't installed. This destroys required_fspecs.
- Util::unstable_keep_if(required_fspecs, [&](FeatureSpec const& fspec) {
- return !status_db.is_installed(fspec) && fspec.name() != spec.name();
- });
+ const int return_code = System::cmd_execute_clean(command);
+ const auto buildtimeus = timer.microseconds();
+ const auto spec_string = spec.to_string();
- if (!required_fspecs.empty())
{
- return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
+ auto locked_metrics = Metrics::g_metrics.lock();
+ locked_metrics->track_buildtime(spec.to_string() + ":[" + Strings::join(",", config.feature_list) + "]",
+ buildtimeus);
+ if (return_code != 0)
+ {
+ locked_metrics->track_property("error", "build failed");
+ locked_metrics->track_property("build_error", spec_string);
+ return BuildResult::BUILD_FAILED;
+ }
}
- // dep_pspecs was not destroyed
- for (auto&& pspec : dep_pspecs)
+ const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(spec));
+ const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info);
+
+ auto bcf = create_binary_control_file(*config.scf.core_paragraph, triplet, build_info, abi_tag);
+
+ if (error_count != 0)
{
- if (pspec == spec) continue;
- auto status_it = status_db.find_installed(pspec);
- Checks::check_exit(VCPKG_LINE_INFO, status_it != status_db.end());
- abi_tag_entries.emplace_back(
- AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi});
+ return BuildResult::POST_BUILD_CHECKS_FAILED;
+ }
+ for (auto&& feature : config.feature_list)
+ {
+ for (auto&& f_pgh : config.scf.feature_paragraphs)
+ {
+ if (f_pgh->name == feature)
+ bcf->features.push_back(
+ create_binary_feature_control_file(*config.scf.core_paragraph, *f_pgh, triplet));
+ }
}
- const fs::path& cmake_exe_path = paths.get_cmake_exe();
- const fs::path& git_exe_path = paths.get_git_exe();
+ write_binary_control_file(paths, *bcf);
+ return {BuildResult::SUCCEEDED, std::move(bcf)};
+ }
- const fs::path ports_cmake_script_path = paths.ports_cmake;
+ static ExtendedBuildResult do_build_package_and_clean_buildtrees(const VcpkgPaths& paths,
+ const PreBuildInfo& pre_build_info,
+ const PackageSpec& spec,
+ const std::string& abi_tag,
+ const BuildPackageConfig& config,
+ const StatusParagraphs& status_db)
+ {
+ auto result = do_build_package(paths, pre_build_info, spec, abi_tag, config, status_db);
- if (GlobalState::g_binary_caching)
+ if (config.build_package_options.clean_buildtrees == CleanBuildtrees::YES)
{
- abi_tag_entries.emplace_back(AbiEntry{
- "portfile", Commands::Hash::get_file_hash(cmake_exe_path, config.port_dir / "portfile.cmake", "SHA1")});
- abi_tag_entries.emplace_back(AbiEntry{
- "control", Commands::Hash::get_file_hash(cmake_exe_path, config.port_dir / "CONTROL", "SHA1")});
+ auto& fs = paths.get_filesystem();
+ const fs::path buildtrees_dir = paths.buildtrees / config.scf.core_paragraph->name;
+ auto buildtree_files = fs.get_files_non_recursive(buildtrees_dir);
+ for (auto&& file : buildtree_files)
+ {
+ if (fs.is_directory(file) && file.filename() != "src")
+ {
+ std::error_code ec;
+ fs.remove_all(file, ec);
+ }
+ }
}
- const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet);
+ return result;
+ }
+
+ Optional<AbiTagAndFile> compute_abi_tag(const VcpkgPaths& paths,
+ const BuildPackageConfig& config,
+ const PreBuildInfo& pre_build_info,
+ Span<const AbiEntry> dependency_abis)
+ {
+ if (!GlobalState::g_binary_caching) return nullopt;
+
+ auto& fs = paths.get_filesystem();
+ const Triplet& triplet = config.triplet;
+ const std::string& name = config.scf.core_paragraph->name;
+
+ std::vector<AbiEntry> abi_tag_entries;
+
+ abi_tag_entries.insert(abi_tag_entries.end(), dependency_abis.begin(), dependency_abis.end());
+
+ abi_tag_entries.emplace_back(
+ AbiEntry{"portfile", Commands::Hash::get_file_hash(paths, config.port_dir / "portfile.cmake", "SHA1")});
+ abi_tag_entries.emplace_back(
+ AbiEntry{"control", Commands::Hash::get_file_hash(paths, config.port_dir / "CONTROL", "SHA1")});
+
abi_tag_entries.emplace_back(AbiEntry{"triplet", pre_build_info.triplet_abi_tag});
std::string features = Strings::join(";", config.feature_list);
@@ -379,204 +449,198 @@ namespace vcpkg::Build
if (config.build_package_options.use_head_version == UseHeadVersion::YES)
abi_tag_entries.emplace_back(AbiEntry{"head", ""});
+ Util::sort(abi_tag_entries);
+
std::string full_abi_info =
Strings::join("", abi_tag_entries, [](const AbiEntry& p) { return p.key + " " + p.value + "\n"; });
- std::string abi_tag;
-
- if (GlobalState::g_binary_caching)
+ if (GlobalState::debugging)
{
- if (GlobalState::debugging)
+ System::println("[DEBUG] <abientries>");
+ for (auto&& entry : abi_tag_entries)
{
- System::println("[DEBUG] <abientries>");
- for (auto&& entry : abi_tag_entries)
- {
- System::println("[DEBUG] %s|%s", entry.key, entry.value);
- }
- System::println("[DEBUG] </abientries>");
+ System::println("[DEBUG] %s|%s", entry.key, entry.value);
}
+ System::println("[DEBUG] </abientries>");
+ }
- auto abi_tag_entries_missing = abi_tag_entries;
- Util::stable_keep_if(abi_tag_entries_missing, [](const AbiEntry& p) { return p.value.empty(); });
+ auto abi_tag_entries_missing = abi_tag_entries;
+ Util::stable_keep_if(abi_tag_entries_missing, [](const AbiEntry& p) { return p.value.empty(); });
- if (abi_tag_entries_missing.empty())
- {
- std::error_code ec;
- fs.create_directories(paths.buildtrees / spec.name(), ec);
- auto abi_file_path = paths.buildtrees / spec.name() / "vcpkg_abi_info";
- fs.write_contents(abi_file_path, full_abi_info);
+ if (abi_tag_entries_missing.empty())
+ {
+ std::error_code ec;
+ fs.create_directories(paths.buildtrees / name, ec);
+ auto abi_file_path = paths.buildtrees / name / (triplet.canonical_name() + ".vcpkg_abi_info.txt");
+ fs.write_contents(abi_file_path, full_abi_info);
- abi_tag = Commands::Hash::get_file_hash(paths.get_cmake_exe(), abi_file_path, "SHA1");
- }
- else
- {
- System::println("Warning: binary caching disabled because abi keys are missing values:\n%s",
- Strings::join("", abi_tag_entries_missing, [](const AbiEntry& e) {
- return " " + e.key + "\n";
- }));
- }
+ return AbiTagAndFile{Commands::Hash::get_file_hash(paths, abi_file_path, "SHA1"), abi_file_path};
}
-
- std::unique_ptr<BinaryControlFile> bcf;
-
- auto archives_dir = paths.root / "archives";
- if (!abi_tag.empty())
+ else
{
- archives_dir /= abi_tag.substr(0, 2);
+ System::println(
+ "Warning: binary caching disabled because abi keys are missing values:\n%s",
+ Strings::join("", abi_tag_entries_missing, [](const AbiEntry& e) { return " " + e.key + "\n"; }));
+
+ return nullopt;
}
- auto archive_path = archives_dir / (abi_tag + ".zip");
+ }
- if (GlobalState::g_binary_caching && !abi_tag.empty() && fs.exists(archive_path))
- {
- System::println("Using cached binary package: %s", archive_path.u8string());
+ static void decompress_archive(const VcpkgPaths& paths, const PackageSpec& spec, const fs::path& archive_path)
+ {
+ auto& fs = paths.get_filesystem();
- auto pkg_path = paths.package_dir(spec);
- std::error_code ec;
- fs.remove_all(pkg_path, ec);
- fs.create_directories(pkg_path, ec);
- auto files = fs.get_files_non_recursive(pkg_path);
- Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string());
+ auto pkg_path = paths.package_dir(spec);
+ std::error_code ec;
+ fs.remove_all(pkg_path, ec);
+ fs.create_directories(pkg_path, ec);
+ auto files = fs.get_files_non_recursive(pkg_path);
+ Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string());
+
+#if defined(_WIN32)
+ auto&& _7za = paths.get_7za_exe();
+
+ System::cmd_execute_clean(Strings::format(
+ R"("%s" x "%s" -o"%s" -y >nul)", _7za.u8string(), archive_path.u8string(), pkg_path.u8string()));
+#else
+ System::cmd_execute_clean(Strings::format(
+ R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string()));
+#endif
+ }
- auto&& _7za = paths.get_7za_exe();
+ static void compress_archive(const VcpkgPaths& paths, const PackageSpec& spec, const fs::path& tmp_archive_path)
+ {
+ auto& fs = paths.get_filesystem();
+
+ std::error_code ec;
+
+ fs.remove(tmp_archive_path, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !fs.exists(tmp_archive_path), "Could not remove file: %s", tmp_archive_path.u8string());
+#if defined(_WIN32)
+ auto&& _7za = paths.get_7za_exe();
+
+ System::cmd_execute_clean(Strings::format(
+ R"("%s" a "%s" "%s\*" >nul)",
+ _7za.u8string(),
+ tmp_archive_path.u8string(),
+ paths.package_dir(spec).u8string()));
+#else
+ System::cmd_execute_clean(Strings::format(
+ R"(cd '%s' && zip --quiet -r '%s' *)", paths.package_dir(spec).u8string(), tmp_archive_path.u8string()));
+#endif
+ }
+
+ ExtendedBuildResult build_package(const VcpkgPaths& paths,
+ const BuildPackageConfig& config,
+ const StatusParagraphs& status_db)
+ {
+ auto& fs = paths.get_filesystem();
+ const Triplet& triplet = config.triplet;
+ const std::string& name = config.scf.core_paragraph->name;
+
+ std::vector<FeatureSpec> required_fspecs = compute_required_feature_specs(config, status_db);
- System::cmd_execute_clean(Strings::format(
- R"("%s" x "%s" -o"%s" -y >nul)", _7za.u8string(), archive_path.u8string(), pkg_path.u8string()));
+ // extract out the actual package ids
+ auto dep_pspecs = Util::fmap(required_fspecs, [](FeatureSpec const& fspec) { return fspec.spec(); });
+ Util::sort_unique_erase(dep_pspecs);
- auto maybe_bcf = Paragraphs::try_load_cached_control_package(paths, spec);
- bcf = std::make_unique<BinaryControlFile>(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO));
+ // Find all features that aren't installed. This destroys required_fspecs.
+ Util::unstable_keep_if(required_fspecs, [&](FeatureSpec const& fspec) {
+ return !status_db.is_installed(fspec) && fspec.name() != name;
+ });
+
+ if (!required_fspecs.empty())
+ {
+ return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
}
- else
+
+ const PackageSpec spec =
+ PackageSpec::from_name_and_triplet(config.scf.core_paragraph->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+
+ std::vector<AbiEntry> dependency_abis;
+
+ // dep_pspecs was not destroyed
+ for (auto&& pspec : dep_pspecs)
{
- if (GlobalState::g_binary_caching && !abi_tag.empty())
- {
- System::println("Could not locate cached archive: %s", archive_path.u8string());
- }
+ if (pspec == spec) continue;
+ auto status_it = status_db.find_installed(pspec);
+ Checks::check_exit(VCPKG_LINE_INFO, status_it != status_db.end());
+ dependency_abis.emplace_back(
+ AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi});
+ }
- std::string all_features;
- for (auto& feature : config.scf.feature_paragraphs)
- {
- all_features.append(feature->name + ";");
- }
+ const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet);
- const Toolset& toolset = paths.get_toolset(pre_build_info);
- const std::string cmd_launch_cmake = System::make_cmake_cmd(
- cmake_exe_path,
- ports_cmake_script_path,
- {
- {"CMD", "BUILD"},
- {"PORT", config.scf.core_paragraph->name},
- {"CURRENT_PORT_DIR", config.port_dir / "/."},
- {"TARGET_TRIPLET", triplet.canonical_name()},
- {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
- {"VCPKG_USE_HEAD_VERSION",
- Util::Enum::to_bool(config.build_package_options.use_head_version) ? "1" : "0"},
- {"_VCPKG_NO_DOWNLOADS",
- !Util::Enum::to_bool(config.build_package_options.allow_downloads) ? "1" : "0"},
- {"GIT", git_exe_path},
- {"FEATURES", features},
- {"ALL_FEATURES", all_features},
- });
-
- auto command = make_build_env_cmd(pre_build_info, toolset);
- if (!command.empty()) command.append(" && ");
- command.append(cmd_launch_cmake);
-
- const auto timer = Chrono::ElapsedTimer::create_started();
-
- const int return_code = System::cmd_execute_clean(command);
- const auto buildtimeus = timer.microseconds();
- const auto spec_string = spec.to_string();
+ auto maybe_abi_tag_and_file = compute_abi_tag(paths, config, pre_build_info, dependency_abis);
- {
- auto locked_metrics = Metrics::g_metrics.lock();
- locked_metrics->track_buildtime(spec.to_string() + ":[" + Strings::join(",", config.feature_list) + "]",
- buildtimeus);
- if (return_code != 0)
- {
- locked_metrics->track_property("error", "build failed");
- locked_metrics->track_property("build_error", spec_string);
- return BuildResult::BUILD_FAILED;
- }
- }
+ std::unique_ptr<BinaryControlFile> bcf;
- const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(spec));
- const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info);
+ auto abi_tag_and_file = maybe_abi_tag_and_file.get();
- bcf = create_binary_control_file(*config.scf.core_paragraph, triplet, build_info, abi_tag);
+ if (GlobalState::g_binary_caching && abi_tag_and_file)
+ {
+ auto archives_root_dir = paths.root / "archives";
+ auto archive_name = abi_tag_and_file->tag + ".zip";
+ auto archive_subpath = fs::u8path(abi_tag_and_file->tag.substr(0, 2)) / archive_name;
+ auto archive_path = archives_root_dir / archive_subpath;
+ auto archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
- if (error_count != 0)
+ if (fs.exists(archive_path))
{
- return BuildResult::POST_BUILD_CHECKS_FAILED;
+ System::println("Using cached binary package: %s", archive_path.u8string());
+
+ decompress_archive(paths, spec, archive_path);
+
+ auto maybe_bcf = Paragraphs::try_load_cached_package(paths, spec);
+ bcf = std::make_unique<BinaryControlFile>(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO));
+ return {BuildResult::SUCCEEDED, std::move(bcf)};
}
- for (auto&& feature : config.feature_list)
+ else if (fs.exists(archive_tombstone_path))
{
- for (auto&& f_pgh : config.scf.feature_paragraphs)
- {
- if (f_pgh->name == feature)
- bcf->features.push_back(
- create_binary_feature_control_file(*config.scf.core_paragraph, *f_pgh, triplet));
- }
+ System::println("Found failure tombstone: %s", archive_tombstone_path.u8string());
+ return BuildResult::BUILD_FAILED;
}
- if (GlobalState::g_binary_caching && !abi_tag.empty())
- {
- std::error_code ec;
- fs.write_contents(
- paths.package_dir(spec) / "share" / spec.name() / "vcpkg_abi_info.txt", full_abi_info, ec);
- }
+ System::println("Could not locate cached archive: %s", archive_path.u8string());
+
+ ExtendedBuildResult result = do_build_package_and_clean_buildtrees(
+ paths, pre_build_info, spec, maybe_abi_tag_and_file.value_or(AbiTagAndFile{}).tag, config, status_db);
- write_binary_control_file(paths, *bcf);
+ std::error_code ec;
+ fs.create_directories(paths.package_dir(spec) / "share" / spec.name(), ec);
+ auto abi_file_in_package = paths.package_dir(spec) / "share" / spec.name() / "vcpkg_abi_info.txt";
+ fs.copy_file(abi_tag_and_file->tag_file, abi_file_in_package, fs::stdfs::copy_options::none, ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not copy into file: %s", abi_file_in_package.u8string());
- if (GlobalState::g_binary_caching && !abi_tag.empty())
+ if (result.code == BuildResult::SUCCEEDED)
{
auto tmp_archive_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".zip");
- std::error_code ec;
- fs.remove(tmp_archive_path, ec);
- Checks::check_exit(VCPKG_LINE_INFO,
- !fs.exists(tmp_archive_path),
- "Could not remove file: %s",
- tmp_archive_path.u8string());
- auto&& _7za = paths.get_7za_exe();
-
- System::cmd_execute_clean(Strings::format(
- R"("%s" a "%s" "%s\*" >nul)",
- _7za.u8string(),
- tmp_archive_path.u8string(),
- paths.package_dir(spec).u8string()));
-
- fs.create_directories(archives_dir, ec);
-
- fs.rename(tmp_archive_path, archive_path);
+ compress_archive(paths, spec, tmp_archive_path);
- System::println("Stored binary cache: %s", archive_path.u8string());
+ fs.create_directories(archive_path.parent_path(), ec);
+ fs.rename(tmp_archive_path, archive_path, ec);
+ if (ec)
+ System::println(
+ System::Color::warning, "Failed to store binary cache: %s", archive_path.u8string());
+ else
+ System::println("Stored binary cache: %s", archive_path.u8string());
}
- }
- return {BuildResult::SUCCEEDED, std::move(bcf)};
- }
-
- ExtendedBuildResult build_package(const VcpkgPaths& paths,
- const BuildPackageConfig& config,
- const StatusParagraphs& status_db)
- {
- ExtendedBuildResult result = do_build_package(paths, config, status_db);
-
- if (config.build_package_options.clean_buildtrees == CleanBuildtrees::YES)
- {
- auto& fs = paths.get_filesystem();
- const fs::path buildtrees_dir = paths.buildtrees / config.scf.core_paragraph->name;
- auto buildtree_files = fs.get_files_non_recursive(buildtrees_dir);
- for (auto&& file : buildtree_files)
+ else if (result.code == BuildResult::BUILD_FAILED || result.code == BuildResult::POST_BUILD_CHECKS_FAILED)
{
- if (fs.is_directory(file) && file.filename() != "src")
- {
- std::error_code ec;
- fs.remove_all(file, ec);
- }
+ // Build failed, so store tombstone archive
+ fs.create_directories(archive_tombstone_path.parent_path(), ec);
+ fs.write_contents(archive_tombstone_path, "", ec);
}
- }
- return result;
+ return result;
+ }
+ else
+ {
+ return do_build_package_and_clean_buildtrees(
+ paths, pre_build_info, spec, maybe_abi_tag_and_file.value_or(AbiTagAndFile{}).tag, config, status_db);
+ }
}
const std::string& to_string(const BuildResult build_result)
@@ -700,7 +764,7 @@ namespace vcpkg::Build
{
return it_hash->second;
}
- auto hash = Commands::Hash::get_file_hash(paths.get_cmake_exe(), triplet_file_path, "SHA1");
+ auto hash = Commands::Hash::get_file_hash(paths, triplet_file_path, "SHA1");
s_hash_cache.emplace(triplet_file_path, hash);
return hash;
}
diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
index c43f25b40..45eb1c83e 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
+#include <vcpkg/base/cache.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/stringliteral.h>
#include <vcpkg/base/system.h>
@@ -7,6 +8,7 @@
#include <vcpkg/build.h>
#include <vcpkg/commands.h>
#include <vcpkg/dependencies.h>
+#include <vcpkg/globalstate.h>
#include <vcpkg/help.h>
#include <vcpkg/input.h>
#include <vcpkg/install.h>
@@ -24,6 +26,7 @@ namespace vcpkg::Commands::CI
Install::InstallSummary summary;
};
+ static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
static constexpr StringLiteral OPTION_EXCLUDE = "--exclude";
static constexpr StringLiteral OPTION_XUNIT = "--x-xunit";
@@ -32,16 +35,131 @@ namespace vcpkg::Commands::CI
{OPTION_XUNIT, "File to output results in XUnit format (internal)"},
}};
+ static constexpr std::array<CommandSwitch, 1> CI_SWITCHES = {
+ {{OPTION_DRY_RUN, "Print out plan without execution"}}};
+
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("ci x64-windows"),
- 0,
+ 1,
SIZE_MAX,
- {{}, CI_SETTINGS},
+ {CI_SWITCHES, CI_SETTINGS},
nullptr,
};
+ UnknownCIPortsResults find_unknown_ports_for_ci(const VcpkgPaths& paths,
+ const std::set<std::string>& exclusions,
+ const Dependencies::PortFileProvider& provider,
+ const std::vector<FeatureSpec>& fspecs)
+ {
+ UnknownCIPortsResults ret;
+
+ auto& fs = paths.get_filesystem();
+
+ std::map<PackageSpec, std::string> abi_tag_map;
+ std::set<PackageSpec> will_fail;
+
+ const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::YES,
+ Build::CleanPackages::YES};
+
+ vcpkg::Cache<Triplet, Build::PreBuildInfo> pre_build_info_cache;
+
+ auto action_plan = Dependencies::create_feature_install_plan(provider, fspecs, StatusParagraphs{});
+
+ for (auto&& action : action_plan)
+ {
+ if (auto p = action.install_action.get())
+ {
+ // determine abi tag
+ std::string abi;
+ if (auto scf = p->source_control_file.get())
+ {
+ auto triplet = p->spec.triplet();
+
+ const Build::BuildPackageConfig build_config{p->source_control_file.value_or_exit(VCPKG_LINE_INFO),
+ triplet,
+ paths.port_dir(p->spec),
+ install_plan_options,
+ p->feature_list};
+
+ auto dependency_abis =
+ Util::fmap(p->computed_dependencies, [&](const PackageSpec& spec) -> Build::AbiEntry {
+ auto it = abi_tag_map.find(spec);
+
+ if (it == abi_tag_map.end())
+ return {spec.name(), ""};
+ else
+ return {spec.name(), it->second};
+ });
+ const auto& pre_build_info = pre_build_info_cache.get_lazy(
+ triplet, [&]() { return Build::PreBuildInfo::from_triplet_file(paths, triplet); });
+
+ auto maybe_tag_and_file =
+ Build::compute_abi_tag(paths, build_config, pre_build_info, dependency_abis);
+ if (auto tag_and_file = maybe_tag_and_file.get())
+ {
+ abi = tag_and_file->tag;
+ abi_tag_map.emplace(p->spec, abi);
+ }
+ }
+ else if (auto ipv = p->installed_package.get())
+ {
+ abi = ipv->core->package.abi;
+ if (!abi.empty()) abi_tag_map.emplace(p->spec, abi);
+ }
+
+ std::string state;
+
+ auto archives_root_dir = paths.root / "archives";
+ auto archive_name = abi + ".zip";
+ auto archive_subpath = fs::u8path(abi.substr(0, 2)) / archive_name;
+ auto archive_path = archives_root_dir / archive_subpath;
+ auto archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
+
+ bool b_will_build = false;
+
+ if (Util::Sets::contains(exclusions, p->spec.name()))
+ {
+ ret.known.emplace(p->spec, BuildResult::EXCLUDED);
+ will_fail.emplace(p->spec);
+ }
+ else if (std::any_of(p->computed_dependencies.begin(),
+ p->computed_dependencies.end(),
+ [&](const PackageSpec& spec) { return Util::Sets::contains(will_fail, spec); }))
+ {
+ ret.known.emplace(p->spec, BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES);
+ will_fail.emplace(p->spec);
+ }
+ else if (fs.exists(archive_path))
+ {
+ state += "pass";
+ ret.known.emplace(p->spec, BuildResult::SUCCEEDED);
+ }
+ else if (fs.exists(archive_tombstone_path))
+ {
+ state += "fail";
+ ret.known.emplace(p->spec, BuildResult::BUILD_FAILED);
+ will_fail.emplace(p->spec);
+ }
+ else
+ {
+ ret.unknown.push_back(p->spec);
+ b_will_build = true;
+ }
+
+ System::println("%40s: %1s %8s: %s", p->spec, (b_will_build ? "*" : " "), state, abi);
+ }
+ }
+
+ return ret;
+ }
+
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
+ Checks::check_exit(
+ VCPKG_LINE_INFO, GlobalState::g_binary_caching, "The ci command requires binary caching to be enabled.");
+
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
std::set<std::string> exclusions_set;
@@ -52,6 +170,8 @@ namespace vcpkg::Commands::CI
exclusions_set.insert(exclusions.begin(), exclusions.end());
}
+ auto is_dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
+
std::vector<Triplet> triplets;
for (const std::string& triplet : args.command_arguments)
{
@@ -71,16 +191,21 @@ namespace vcpkg::Commands::CI
Build::CleanBuildtrees::YES,
Build::CleanPackages::YES};
- std::vector<std::string> ports = Install::get_all_port_names(paths);
+ std::vector<std::map<PackageSpec, BuildResult>> all_known_results;
+
+ std::vector<std::string> all_ports = Install::get_all_port_names(paths);
std::vector<TripletAndSummary> results;
for (const Triplet& triplet : triplets)
{
Input::check_triplet(triplet, paths);
- std::vector<PackageSpec> specs = PackageSpec::to_package_specs(ports, triplet);
+
+ std::vector<PackageSpec> specs = PackageSpec::to_package_specs(all_ports, triplet);
// Install the default features for every package
- const auto featurespecs = Util::fmap(specs, [](auto& spec) { return FeatureSpec(spec, ""); });
+ auto all_fspecs = Util::fmap(specs, [](auto& spec) { return FeatureSpec(spec, ""); });
+ auto split_specs = find_unknown_ports_for_ci(paths, exclusions_set, paths_port_file, all_fspecs);
+ auto fspecs = Util::fmap(split_specs.unknown, [](auto& spec) { return FeatureSpec(spec, ""); });
- auto action_plan = Dependencies::create_feature_install_plan(paths_port_file, featurespecs, status_db);
+ auto action_plan = Dependencies::create_feature_install_plan(paths_port_file, fspecs, status_db);
for (auto&& action : action_plan)
{
@@ -94,8 +219,18 @@ namespace vcpkg::Commands::CI
}
}
- auto summary = Install::perform(action_plan, Install::KeepGoing::YES, paths, status_db);
- results.push_back({triplet, std::move(summary)});
+ if (is_dry_run)
+ {
+ Dependencies::print_plan(action_plan);
+ }
+ else
+ {
+ auto summary = Install::perform(action_plan, Install::KeepGoing::YES, paths, status_db);
+ for (auto&& result : summary.results)
+ split_specs.known.erase(result.spec);
+ results.push_back({triplet, std::move(summary)});
+ all_known_results.emplace_back(std::move(split_specs.known));
+ }
}
for (auto&& result : results)
@@ -112,6 +247,14 @@ namespace vcpkg::Commands::CI
for (auto&& result : results)
xunit_doc += result.summary.xunit_results();
+ for (auto&& known_result : all_known_results)
+ {
+ for (auto&& result : known_result)
+ {
+ xunit_doc +=
+ Install::InstallSummary::xunit_result(result.first, Chrono::ElapsedTime{}, result.second);
+ }
+ }
xunit_doc += "</collection></assembly></assemblies>\n";
paths.get_filesystem().write_contents(fs::u8path(it_xunit->second), xunit_doc);
diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp
index 41f261a53..ac34a6720 100644
--- a/toolsrc/src/vcpkg/commands.edit.cpp
+++ b/toolsrc/src/vcpkg/commands.edit.cpp
@@ -69,14 +69,24 @@ namespace vcpkg::Commands::Edit
std::vector<fs::path> candidate_paths;
auto maybe_editor_path = System::get_environment_variable("EDITOR");
- if (auto editor_path = maybe_editor_path.get())
+ if (const std::string* editor_path = maybe_editor_path.get())
{
candidate_paths.emplace_back(*editor_path);
}
- 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 auto& program_files = System::get_program_files_platform_bitness();
+ if (const fs::path* pf = program_files.get())
+ {
+ candidate_paths.push_back(*pf / VS_CODE_INSIDERS);
+ candidate_paths.push_back(*pf / VS_CODE);
+ }
+
+ const auto& program_files_32_bit = System::get_program_files_32_bit();
+ if (const fs::path* pf = program_files_32_bit.get())
+ {
+ candidate_paths.push_back(*pf / VS_CODE_INSIDERS);
+ candidate_paths.push_back(*pf / VS_CODE);
+ }
const std::vector<fs::path> from_registry = find_from_registry();
candidate_paths.insert(candidate_paths.end(), from_registry.cbegin(), from_registry.cend());
diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp
index f3beef6cd..d078baedb 100644
--- a/toolsrc/src/vcpkg/commands.env.cpp
+++ b/toolsrc/src/vcpkg/commands.env.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
+#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
#include <vcpkg/build.h>
#include <vcpkg/commands.h>
@@ -7,25 +8,66 @@
namespace vcpkg::Commands::Env
{
+ static constexpr StringLiteral OPTION_BIN = "--bin";
+ static constexpr StringLiteral OPTION_INCLUDE = "--include";
+ static constexpr StringLiteral OPTION_DEBUG_BIN = "--debug-bin";
+ static constexpr StringLiteral OPTION_TOOLS = "--tools";
+ static constexpr StringLiteral OPTION_PYTHON = "--python";
+
+ static constexpr std::array<CommandSwitch, 5> SWITCHES = {{
+ {OPTION_BIN, "Add installed bin/ to PATH"},
+ {OPTION_INCLUDE, "Add installed include/ to INCLUDE"},
+ {OPTION_DEBUG_BIN, "Add installed debug/bin/ to PATH"},
+ {OPTION_TOOLS, "Add installed tools/*/ to PATH"},
+ {OPTION_PYTHON, "Add installed python/ to PYTHONPATH"},
+ }};
+
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("env --triplet x64-windows"),
0,
0,
- {},
+ {SWITCHES, {}},
nullptr,
};
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& triplet)
{
- args.parse_arguments(COMMAND_STRUCTURE);
+ const auto& fs = paths.get_filesystem();
+
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
- const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet);
+ const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, triplet);
const Toolset& toolset = paths.get_toolset(pre_build_info);
auto env_cmd = Build::make_build_env_cmd(pre_build_info, toolset);
+
+ std::unordered_map<std::string, std::string> extra_env = {};
+ const bool add_bin = Util::Sets::contains(options.switches, OPTION_BIN);
+ const bool add_include = Util::Sets::contains(options.switches, OPTION_INCLUDE);
+ const bool add_debug_bin = Util::Sets::contains(options.switches, OPTION_DEBUG_BIN);
+ const bool add_tools = Util::Sets::contains(options.switches, OPTION_TOOLS);
+ const bool add_python = Util::Sets::contains(options.switches, OPTION_PYTHON);
+
+ std::vector<std::string> path_vars;
+ if (add_bin) path_vars.push_back((paths.installed / triplet.to_string() / "bin").u8string());
+ if (add_debug_bin) path_vars.push_back((paths.installed / triplet.to_string() / "debug" / "bin").u8string());
+ if (add_include) extra_env.emplace("INCLUDE", (paths.installed / triplet.to_string() / "include").u8string());
+ if (add_tools)
+ {
+ auto tools_dir = paths.installed / triplet.to_string() / "tools";
+ auto tool_files = fs.get_files_non_recursive(tools_dir);
+ path_vars.push_back(tools_dir.u8string());
+ for (auto&& tool_dir : tool_files)
+ {
+ if (fs.is_directory(tool_dir)) path_vars.push_back(tool_dir.u8string());
+ }
+ }
+ if (add_python) extra_env.emplace("PYTHONPATH", (paths.installed / triplet.to_string() / "python").u8string());
+ if (path_vars.size() > 0) extra_env.emplace("PATH", Strings::join(";", path_vars));
+
if (env_cmd.empty())
- System::cmd_execute_clean("cmd");
+ System::cmd_execute_clean("cmd", extra_env);
else
- System::cmd_execute_clean(env_cmd + " && cmd");
+ System::cmd_execute_clean(env_cmd + " && cmd", extra_env);
Checks::exit_success(VCPKG_LINE_INFO);
}
diff --git a/toolsrc/src/vcpkg/commands.hash.cpp b/toolsrc/src/vcpkg/commands.hash.cpp
index bbbbed036..9e1b54390 100644
--- a/toolsrc/src/vcpkg/commands.hash.cpp
+++ b/toolsrc/src/vcpkg/commands.hash.cpp
@@ -1,17 +1,118 @@
#include "pch.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
+#if defined(_WIN32)
+#include <bcrypt.h>
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
namespace vcpkg::Commands::Hash
{
- std::string get_file_hash(fs::path const& cmake_exe_path, fs::path const& path, std::string const& hash_type)
+ namespace
+ {
+ static std::string to_hex(const unsigned char* string, const size_t bytes)
+ {
+ static constexpr char HEX_MAP[] = "0123456789abcdef";
+
+ std::string output;
+ output.resize(2 * bytes);
+
+ size_t current_char = 0;
+ for (size_t i = 0; i < bytes; i++)
+ {
+ // high
+ output[current_char] = HEX_MAP[(string[i] & 0xF0) >> 4];
+ ++current_char;
+ // low
+ output[current_char] = HEX_MAP[(string[i] & 0x0F)];
+ ++current_char;
+ }
+
+ return output;
+ }
+
+ struct BCryptAlgorithmHandle : Util::ResourceBase
+ {
+ BCRYPT_ALG_HANDLE handle = nullptr;
+
+ ~BCryptAlgorithmHandle()
+ {
+ if (handle) BCryptCloseAlgorithmProvider(handle, 0);
+ }
+ };
+
+ struct BCryptHashHandle : Util::ResourceBase
+ {
+ BCRYPT_HASH_HANDLE handle = nullptr;
+
+ ~BCryptHashHandle()
+ {
+ if (handle) BCryptDestroyHash(handle);
+ }
+ };
+ }
+
+ std::string get_file_hash(const VcpkgPaths&, const fs::path& path, const std::string& hash_type)
+ {
+ BCryptAlgorithmHandle algorithm_handle;
+
+ NTSTATUS error_code = BCryptOpenAlgorithmProvider(
+ &algorithm_handle.handle, Strings::to_utf16(Strings::ascii_to_uppercase(hash_type)).c_str(), nullptr, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to open the algorithm provider");
+
+ DWORD hash_buffer_bytes;
+ DWORD cb_data;
+ error_code = BCryptGetProperty(algorithm_handle.handle,
+ BCRYPT_HASH_LENGTH,
+ reinterpret_cast<PUCHAR>(&hash_buffer_bytes),
+ sizeof(DWORD),
+ &cb_data,
+ 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to get hash length");
+ const ULONG length_in_bytes = hash_buffer_bytes;
+
+ BCryptHashHandle hash_handle;
+
+ error_code = BCryptCreateHash(algorithm_handle.handle, &hash_handle.handle, nullptr, 0, nullptr, 0, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to initialize the hasher");
+
+ FILE* file = nullptr;
+ const auto ec = _wfopen_s(&file, path.c_str(), L"rb");
+ Checks::check_exit(VCPKG_LINE_INFO, ec == 0, "Failed to open file: %s", path.u8string());
+ unsigned char buffer[4096];
+ while (const auto actual_size = fread(buffer, 1, sizeof(buffer), file))
+ {
+ error_code = BCryptHashData(hash_handle.handle, buffer, static_cast<ULONG>(actual_size), 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to hash data");
+ }
+
+ fclose(file);
+
+ std::unique_ptr<unsigned char[]> hash_buffer = std::make_unique<UCHAR[]>(length_in_bytes);
+
+ error_code = BCryptFinishHash(hash_handle.handle, hash_buffer.get(), length_in_bytes, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to finalize the hash");
+
+ return to_hex(hash_buffer.get(), length_in_bytes);
+ }
+}
+
+#else
+namespace vcpkg::Commands::Hash
+{
+ std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type)
{
const std::string cmd_line = Strings::format(
R"("%s" -E %ssum "%s")",
- cmake_exe_path.u8string(),
+ paths.get_cmake_exe().u8string(),
Strings::ascii_to_lowercase(hash_type),
path.u8string());
@@ -32,7 +133,11 @@ namespace vcpkg::Commands::Hash
Util::erase_remove_if(hash, isspace);
return hash;
}
+}
+#endif
+namespace vcpkg::Commands::Hash
+{
const CommandStructure COMMAND_STRUCTURE = {
Strings::format("The argument should be a file path\n%s",
Help::create_example_string("hash boost_1_62_0.tar.bz2")),
@@ -44,19 +149,12 @@ namespace vcpkg::Commands::Hash
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- args.parse_arguments(COMMAND_STRUCTURE);
-
- if (args.command_arguments.size() == 1)
- {
- auto hash = get_file_hash(paths.get_cmake_exe(), args.command_arguments[0], "SHA512");
- System::println(hash);
- }
- if (args.command_arguments.size() == 2)
- {
- auto hash = get_file_hash(paths.get_cmake_exe(), args.command_arguments[0], args.command_arguments[1]);
- System::println(hash);
- }
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
+ const fs::path file_to_hash = args.command_arguments[0];
+ const std::string algorithm = args.command_arguments.size() == 2 ? args.command_arguments[1] : "SHA512";
+ const std::string hash = get_file_hash(paths, file_to_hash, algorithm);
+ System::println(hash);
Checks::exit_success(VCPKG_LINE_INFO);
}
}
diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp
index 460e99b88..36e4e56e7 100644
--- a/toolsrc/src/vcpkg/commands.integrate.cpp
+++ b/toolsrc/src/vcpkg/commands.integrate.cpp
@@ -148,12 +148,12 @@ namespace vcpkg::Commands::Integrate
static void integrate_install(const VcpkgPaths& paths)
{
static const std::array<fs::path, 2> OLD_SYSTEM_TARGET_FILES = {
- System::get_program_files_32_bit() /
+ System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) /
"MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets",
- System::get_program_files_32_bit() /
+ System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) /
"MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets"};
static const fs::path SYSTEM_WIDE_TARGETS_FILE =
- System::get_program_files_32_bit() /
+ System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) /
"MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props";
auto& fs = paths.get_filesystem();
diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp
index 1f853014b..f6d81c973 100644
--- a/toolsrc/src/vcpkg/dependencies.cpp
+++ b/toolsrc/src/vcpkg/dependencies.cpp
@@ -144,12 +144,14 @@ namespace vcpkg::Dependencies
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const SourceControlFile& scf,
const std::set<std::string>& features,
- const RequestType& request_type)
+ const RequestType& request_type,
+ std::vector<PackageSpec>&& dependencies)
: spec(spec)
, source_control_file(scf)
, plan_type(InstallPlanType::BUILD_AND_INSTALL)
, request_type(request_type)
, feature_list(features)
+ , computed_dependencies(std::move(dependencies))
{
}
@@ -162,6 +164,7 @@ namespace vcpkg::Dependencies
, plan_type(InstallPlanType::ALREADY_INSTALLED)
, request_type(request_type)
, feature_list(features)
+ , computed_dependencies(installed_package.get()->dependencies())
{
}
@@ -530,7 +533,7 @@ namespace vcpkg::Dependencies
for (auto&& depend : remove_edges_edges)
{
auto& depend_cluster = graph.get(depend.spec());
- graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster});
+ if (&depend_cluster != &cluster) graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster});
mark_minus(depend_cluster, graph, graph_plan);
}
}
@@ -683,11 +686,17 @@ namespace vcpkg::Dependencies
// 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);
+
+ auto dep_specs = Util::fmap(m_graph_plan->install_graph.adjacency_list(p_cluster),
+ [](ClusterPtr const& p) { return p->spec; });
+ Util::sort_unique_erase(dep_specs);
+
plan.emplace_back(InstallPlanAction{
p_cluster->spec,
*pscf,
p_cluster->to_install_features,
p_cluster->request_type,
+ std::move(dep_specs),
});
}
else
diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp
index b7d355742..743619937 100644
--- a/toolsrc/src/vcpkg/help.cpp
+++ b/toolsrc/src/vcpkg/help.cpp
@@ -98,14 +98,10 @@ namespace vcpkg::Help
" vcpkg create <pkg> <url>\n"
" [archivename] Create a new package\n"
" vcpkg owns <pat> Search for files in installed packages\n"
- " vcpkg cache List cached compiled packages\n"
+ " vcpkg env Creates a clean shell environment for development or compiling.\n"
" vcpkg version Display version information\n"
" vcpkg contact Display contact information to send feedback\n"
"\n"
- //"internal commands:\n"
- //" --check-build-deps <controlfile>\n"
- //" --create-binary-control <controlfile>\n"
- //"\n"
"Options:\n"
" --triplet <t> Specify the target architecture triplet.\n"
" (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n"
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index 46c7c53b8..e30a34c18 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -306,7 +306,7 @@ namespace vcpkg::Install
System::println("Building package %s... done", display_name_with_features);
auto bcf = std::make_unique<BinaryControlFile>(
- Paragraphs::try_load_cached_control_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO));
+ Paragraphs::try_load_cached_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO));
auto code = aux_install(display_name_with_features, *bcf);
if (action.build_options.clean_packages == Build::CleanPackages::YES)
@@ -668,39 +668,42 @@ namespace vcpkg::Install
return nullptr;
}
+ std::string InstallSummary::xunit_result(const PackageSpec& spec, Chrono::ElapsedTime time, BuildResult code)
+ {
+ std::string inner_block;
+ const char* result_string = "";
+ switch (code)
+ {
+ case BuildResult::POST_BUILD_CHECKS_FAILED:
+ case BuildResult::FILE_CONFLICTS:
+ case BuildResult::BUILD_FAILED:
+ result_string = "Fail";
+ inner_block = Strings::format("<failure><message><![CDATA[%s]]></message></failure>", to_string(code));
+ break;
+ case BuildResult::EXCLUDED:
+ case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES:
+ result_string = "Skip";
+ inner_block = Strings::format("<reason><![CDATA[%s]]></reason>", to_string(code));
+ break;
+ case BuildResult::SUCCEEDED: result_string = "Pass"; break;
+ default: Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ return Strings::format(R"(<test name="%s" method="%s" time="%lld" result="%s">%s</test>)"
+ "\n",
+ spec,
+ spec,
+ time.as<std::chrono::seconds>().count(),
+ result_string,
+ inner_block);
+ }
+
std::string InstallSummary::xunit_results() const
{
std::string xunit_doc;
for (auto&& result : results)
{
- std::string inner_block;
- const char* result_string = "";
- switch (result.build_result.code)
- {
- case BuildResult::POST_BUILD_CHECKS_FAILED:
- case BuildResult::FILE_CONFLICTS:
- case BuildResult::BUILD_FAILED:
- result_string = "Fail";
- inner_block = Strings::format("<failure><message><![CDATA[%s]]></message></failure>",
- to_string(result.build_result.code));
- break;
- case BuildResult::EXCLUDED:
- case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES:
- result_string = "Skip";
- inner_block =
- Strings::format("<reason><![CDATA[%s]]></reason>", to_string(result.build_result.code));
- break;
- case BuildResult::SUCCEEDED: result_string = "Pass"; break;
- default: Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- xunit_doc += Strings::format(R"(<test name="%s" method="%s" time="%lld" result="%s">%s</test>)"
- "\n",
- result.spec,
- result.spec,
- result.timing.as<std::chrono::seconds>().count(),
- result_string,
- inner_block);
+ xunit_doc += xunit_result(result.spec, result.timing, result.build_result.code);
}
return xunit_doc;
}
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
index d49cadbe2..9e17b237d 100644
--- a/toolsrc/src/vcpkg/metrics.cpp
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -92,7 +92,7 @@ namespace vcpkg::Metrics
{
encoded.append("\\\"");
}
- else if (ch < 0x20 || ch >= 0x80)
+ else if (ch < 0x20 || static_cast<unsigned char>(ch) >= 0x80)
{
// Note: this treats incoming Strings as Latin-1
static constexpr const char HEX[16] = {
diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp
index b66d53994..d0b0c0abf 100644
--- a/toolsrc/src/vcpkg/paragraphs.cpp
+++ b/toolsrc/src/vcpkg/paragraphs.cpp
@@ -228,7 +228,7 @@ namespace vcpkg::Paragraphs
return error_info;
}
- Expected<BinaryControlFile> try_load_cached_control_package(const VcpkgPaths& paths, const PackageSpec& spec)
+ Expected<BinaryControlFile> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
{
Expected<std::vector<std::unordered_map<std::string, std::string>>> pghs =
get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
index 0b4baf189..ed61cb42a 100644
--- a/toolsrc/src/vcpkg/sourceparagraph.cpp
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -158,6 +158,16 @@ namespace vcpkg
return std::move(control_file);
}
+ Optional<const FeatureParagraph&> SourceControlFile::find_feature(const std::string& featurename) const
+ {
+ auto it = Util::find_if(feature_paragraphs,
+ [&](const std::unique_ptr<FeatureParagraph>& p) { return p->name == featurename; });
+ if (it != feature_paragraphs.end())
+ return **it;
+ else
+ return nullopt;
+ }
+
Dependency Dependency::parse_dependency(std::string name, std::string qualifier)
{
Dependency dep;
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index e62ef8662..c36def15d 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -19,6 +19,8 @@ namespace vcpkg
{
std::array<int, 3> required_version;
fs::path downloaded_exe_path;
+ std::string url;
+ fs::path downloaded_path;
};
static Optional<std::array<int, 3>> parse_version_string(const std::string& version_as_string)
@@ -41,8 +43,24 @@ namespace vcpkg
static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
{
+#if defined(_WIN32)
+ static constexpr StringLiteral OS_STRING = "";
+#elif defined(__APPLE__)
+ static constexpr StringLiteral OS_STRING = " os=\"osx\"";
+#else // assume linux
+ static constexpr StringLiteral OS_STRING = " os=\"linux\"";
+#endif
+
static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml";
+ const auto maybe_get_string_inside_tags = [](const std::string& input,
+ const std::regex& regex) -> Optional<std::string> {
+ std::smatch match;
+ const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
+ if (!has_match) return nullopt;
+ return match[1];
+ };
+
const auto get_string_inside_tags =
[](const std::string& input, const std::regex& regex, const std::string& tag_name) -> std::string {
std::smatch match;
@@ -57,7 +75,10 @@ namespace vcpkg
static const std::regex VERSION_REGEX{
Strings::format(R"###(<requiredVersion>([\s\S]*?)</requiredVersion>)###", tool)};
static const std::regex EXE_RELATIVE_PATH_REGEX{
- Strings::format(R"###(<exeRelativePath>([\s\S]*?)</exeRelativePath>)###", tool)};
+ Strings::format(R"###(<exeRelativePath%s>([\s\S]*?)</exeRelativePath>)###", OS_STRING)};
+ static const std::regex ARCHIVE_RELATIVE_PATH_REGEX{
+ Strings::format(R"###(<archiveRelativePath%s>([\s\S]*?)</archiveRelativePath>)###", OS_STRING)};
+ static const std::regex URL_REGEX{Strings::format(R"###(<url%s>([\s\S]*?)</url>)###", OS_STRING)};
const std::regex tool_regex{Strings::format(R"###(<tool[\s]+name="%s">([\s\S]*?)</tool>)###", tool)};
@@ -74,9 +95,13 @@ namespace vcpkg
const std::string required_version_as_string =
get_string_inside_tags(tool_data_as_string, VERSION_REGEX, "requiredVersion");
+ const std::string url = get_string_inside_tags(tool_data_as_string, URL_REGEX, "url");
+
const std::string exe_relative_path =
get_string_inside_tags(tool_data_as_string, EXE_RELATIVE_PATH_REGEX, "exeRelativePath");
+ auto archive_relative_path = maybe_get_string_inside_tags(tool_data_as_string, ARCHIVE_RELATIVE_PATH_REGEX);
+
const Optional<std::array<int, 3>> required_version = parse_version_string(required_version_as_string);
Checks::check_exit(VCPKG_LINE_INFO,
required_version.has_value(),
@@ -85,7 +110,10 @@ namespace vcpkg
required_version_as_string);
const fs::path exe_path = paths.downloads / exe_relative_path;
- return ToolData{*required_version.get(), exe_path};
+ return ToolData{*required_version.get(),
+ exe_path,
+ url,
+ paths.downloads / archive_relative_path.value_or(exe_relative_path)};
}
static bool exists_and_has_equal_or_greater_version(const std::string& version_cmd,
@@ -144,8 +172,10 @@ namespace vcpkg
return data_lines;
}
- static fs::path fetch_tool(const fs::path& scripts_folder, const std::string& tool_name, const ToolData& tool_data)
+ static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data)
{
+ const auto& fs = paths.get_filesystem();
+ const fs::path& scripts_folder = paths.scripts;
const std::array<int, 3>& version = tool_data.required_version;
const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]);
@@ -154,6 +184,7 @@ namespace vcpkg
version_as_string,
tool_name,
version_as_string);
+#if defined(_WIN32)
const fs::path script = scripts_folder / "fetchtool.ps1";
const std::string title = Strings::format(
"Fetching %s version %s (No sufficient installed version was found)", tool_name, version_as_string);
@@ -173,27 +204,44 @@ namespace vcpkg
expected_downloaded_path.u8string(),
actual_downloaded_path.u8string());
return actual_downloaded_path;
+#else
+ if (!fs.exists(tool_data.downloaded_path))
+ {
+ auto code = System::cmd_execute(
+ Strings::format(R"(curl '%s' --create-dirs --output '%s')", tool_data.url, tool_data.downloaded_path));
+ Checks::check_exit(VCPKG_LINE_INFO, code == 0, "curl failed while downloading %s", tool_data.url);
+ }
+ auto code = System::cmd_execute(
+ Strings::format(R"(cd '%s' && tar xzf '%s')", paths.downloads, tool_data.downloaded_path));
+ Checks::check_exit(VCPKG_LINE_INFO, code == 0, "tar failed while extracting %s", tool_data.downloaded_path);
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ fs.exists(tool_data.downloaded_exe_path),
+ "Expected %s to exist after extracting",
+ tool_data.downloaded_exe_path);
+
+ return tool_data.downloaded_exe_path;
+#endif
}
static fs::path get_cmake_path(const VcpkgPaths& paths)
{
-#if defined(_WIN32)
+ std::vector<fs::path> candidate_paths;
+#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "cmake");
+ candidate_paths.push_back(TOOL_DATA.downloaded_exe_path);
#else
static const ToolData TOOL_DATA = ToolData{{3, 5, 1}, ""};
#endif
static const std::string VERSION_CHECK_ARGUMENTS = "--version";
- std::vector<fs::path> candidate_paths;
-#if defined(_WIN32)
- candidate_paths.push_back(TOOL_DATA.downloaded_exe_path);
-#endif
const std::vector<fs::path> from_path = Files::find_from_PATH("cmake");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
-#if defined(_WIN32)
- 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");
-#endif
+
+ const auto& program_files = System::get_program_files_platform_bitness();
+ if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
+ const auto& program_files_32_bit = System::get_program_files_32_bit();
+ if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
@@ -202,7 +250,7 @@ namespace vcpkg
return *p;
}
- return fetch_tool(paths.scripts, "cmake", TOOL_DATA);
+ return fetch_tool(paths, "cmake", TOOL_DATA);
}
static fs::path get_7za_path(const VcpkgPaths& paths)
@@ -211,7 +259,7 @@ namespace vcpkg
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "7zip");
if (!paths.get_filesystem().exists(TOOL_DATA.downloaded_exe_path))
{
- return fetch_tool(paths.scripts, "7zip", TOOL_DATA);
+ return fetch_tool(paths, "7zip", TOOL_DATA);
}
return TOOL_DATA.downloaded_exe_path;
#else
@@ -234,7 +282,7 @@ namespace vcpkg
return *p;
}
- return fetch_tool(paths.scripts, "nuget", TOOL_DATA);
+ return fetch_tool(paths, "nuget", TOOL_DATA);
}
static fs::path get_git_path(const VcpkgPaths& paths)
@@ -252,10 +300,11 @@ namespace vcpkg
#endif
const std::vector<fs::path> from_path = Files::find_from_PATH("git");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
-#if defined(_WIN32)
- 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");
-#endif
+
+ const auto& program_files = System::get_program_files_platform_bitness();
+ if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
+ const auto& program_files_32_bit = System::get_program_files_32_bit();
+ if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
@@ -264,7 +313,7 @@ namespace vcpkg
return *p;
}
- return fetch_tool(paths.scripts, "git", TOOL_DATA);
+ return fetch_tool(paths, "git", TOOL_DATA);
}
static fs::path get_ifw_installerbase_path(const VcpkgPaths& paths)
@@ -290,10 +339,10 @@ namespace vcpkg
return *p;
}
- return fetch_tool(paths.scripts, "installerbase", TOOL_DATA);
+ return fetch_tool(paths, "installerbase", TOOL_DATA);
}
- Expected<VcpkgPaths> VcpkgPaths::create(const fs::path& vcpkg_root_dir)
+ Expected<VcpkgPaths> VcpkgPaths::create(const fs::path& vcpkg_root_dir, const std::string& default_vs_path)
{
std::error_code ec;
const fs::path canonical_vcpkg_root_dir = fs::stdfs::canonical(vcpkg_root_dir, ec);
@@ -304,6 +353,7 @@ namespace vcpkg
VcpkgPaths paths;
paths.root = canonical_vcpkg_root_dir;
+ paths.default_vs_path = default_vs_path;
if (paths.root.empty())
{
@@ -621,7 +671,8 @@ namespace vcpkg
const Toolset& VcpkgPaths::get_toolset(const Build::PreBuildInfo& prebuildinfo) const
{
- if (prebuildinfo.external_toolchain_file)
+ if (prebuildinfo.external_toolchain_file ||
+ (!prebuildinfo.cmake_system_name.empty() && prebuildinfo.cmake_system_name != "WindowsStore"))
{
static Toolset external_toolset = []() -> Toolset {
Toolset ret;
@@ -643,7 +694,11 @@ namespace vcpkg
std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets);
const auto tsv = prebuildinfo.platform_toolset.get();
- const auto vsp = prebuildinfo.visual_studio_path.get();
+ auto vsp = prebuildinfo.visual_studio_path.get();
+ if (!vsp && !default_vs_path.empty())
+ {
+ vsp = &default_vs_path;
+ }
if (tsv && vsp)
{