Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions azure-pipelines/e2e-assets/ci/ci-baseline-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# CI Baseline for testing early filtering
# skip-dep should be skipped on all triplets
skip-dep:x86-windows=skip
skip-dep:x64-windows=skip
skip-dep:arm64-windows=skip
skip-dep:x64-linux=skip
skip-dep:arm64-linux=skip
skip-dep:x64-osx=skip
skip-dep:arm64-osx=skip
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
vcpkg_minimum_required(VERSION 2024-11-01)

message(STATUS "Installing base-dep")

file(WRITE "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" "Test port")

set(VCPKG_POLICY_EMPTY_PACKAGE enabled)
10 changes: 10 additions & 0 deletions azure-pipelines/e2e-ports/ci-feature-baseline/base-dep/vcpkg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "base-dep",
"version": "1.0.0",
"description": "Test port with optional feature that should be skipped",
"features": {
"feature-to-skip": {
"description": "Optional feature (should be skipped when skip-dep is excluded)"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
vcpkg_minimum_required(VERSION 2024-11-01)

message(STATUS "Installing skip-dep")

file(WRITE "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" "Test port")

set(VCPKG_POLICY_EMPTY_PACKAGE enabled)
11 changes: 11 additions & 0 deletions azure-pipelines/e2e-ports/ci-feature-baseline/skip-dep/vcpkg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "skip-dep",
"version": "1.0.0",
"description": "Test port that depends on base-dep[feature-to-skip]",
"dependencies": [
{
"name": "base-dep",
"features": ["feature-to-skip"]
}
]
}
28 changes: 28 additions & 0 deletions azure-pipelines/end-to-end-tests-dir/ci.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,31 @@ New-Item -ItemType Directory -Path $emptyDir -Force | Out-Null
$Output = Run-VcpkgAndCaptureOutput ci --triplet=$Triplet --x-builtin-ports-root="$emptyDir" --binarysource=clear --overlay-ports="$PSScriptRoot/../e2e-ports/duplicate-file-a" --overlay-ports="$PSScriptRoot/../e2e-ports/duplicate-file-b"
Throw-IfNotFailed
Restore-Problem-Matchers

# Test CI baseline early filtering functionality
# Test that ports marked as skip in ci.baseline.txt are excluded early from CI
$Output = Run-VcpkgAndCaptureOutput ci --dry-run --triplet=$Triplet --x-builtin-ports-root="$PSScriptRoot/../e2e-ports/ci-feature-baseline" --binarysource=clear --ci-baseline="$PSScriptRoot/../e2e-assets/ci/ci-baseline-test.txt"
Throw-IfFailed
# skip-dep should be marked as skip in the output (verifies it appears AND has skip status)
if (-not ($Output -match "skip-dep:${Triplet}:\s+skip:")) {
throw 'skip-dep should be marked as skip in ci.baseline.txt and appear in output'
}
# base-dep should be in the installation list (not skipped)
if (-not ($Output -match "base-dep:${Triplet}@")) {
throw 'base-dep should be in the installation list'
}
# skip-dep should NOT be in the installation list
if ($Output -match "skip-dep:${Triplet}@") {
throw 'skip-dep should NOT be in the installation list (marked as skip)'
}

# Test that without CI baseline, both ports are included
$Output2 = Run-VcpkgAndCaptureOutput ci --dry-run --triplet=$Triplet --x-builtin-ports-root="$PSScriptRoot/../e2e-ports/ci-feature-baseline" --binarysource=clear
Throw-IfFailed
# Both ports should be in the installation list (may have features like [core,feature-to-skip])
if (-not ($Output2 -match "base-dep(\[.+?\])?:${Triplet}@")) {
throw 'base-dep should be in the installation list without baseline'
}
if (-not ($Output2 -match "skip-dep(\[.+?\])?:${Triplet}@")) {
throw 'skip-dep should be in the installation list without baseline'
}
71 changes: 61 additions & 10 deletions src/vcpkg/commands.ci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,43 @@ namespace
std::vector<StringLiteral> action_state_string;
};

// Build a set of package specs to exclude based on the CI baseline exclusions map for the target triplet
SortedVector<PackageSpec> calculate_ci_excluded_specs(const ExclusionsMap& exclusions_map, Triplet target_triplet)
{
std::vector<PackageSpec> exclude_list;
for (const auto& triplet_exclusions : exclusions_map.triplets)
{
if (triplet_exclusions.triplet == target_triplet)
{
for (const auto& port_name : triplet_exclusions.exclusions)
{
exclude_list.emplace_back(port_name, target_triplet);
}
break;
}
}
return SortedVector<PackageSpec>(std::move(exclude_list));
}

// Build the list of package specs to install for CI (all ports with default features, excluding those in the
// exclusion list)
std::vector<FullPackageSpec> calculate_ci_requested_specs(const PortFileProvider& provider,
Triplet target_triplet,
const SortedVector<PackageSpec>& ports_to_exclude)
{
std::vector<FullPackageSpec> ci_requested_default_full_specs;
for (auto scfl : provider.load_all_control_files())
{
PackageSpec pkg_spec{scfl->to_name(), target_triplet};
if (!ports_to_exclude.contains(pkg_spec))
{
ci_requested_default_full_specs.emplace_back(
pkg_spec, InternalFeatureSet{FeatureNameCore.to_string(), FeatureNameDefault.to_string()});
}
}
return ci_requested_default_full_specs;
}

bool supported_for_triplet(const CMakeVars::CMakeVarProvider& var_provider,
const SourceControlFile& source_control_file,
PackageSpec spec)
Expand Down Expand Up @@ -353,18 +390,16 @@ namespace vcpkg

auto registry_set = paths.make_registry_set();
PathsPortFileProvider provider(*registry_set, make_overlay_provider(fs, paths.overlay_ports));

// Build a set of ports to exclude based on the exclusions_map for the target triplet
auto ports_to_exclude = calculate_ci_excluded_specs(exclusions_map, target_triplet);

auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;

const ElapsedTimer timer;
// Install the default features for every package
std::vector<FullPackageSpec> all_default_full_specs;
for (auto scfl : provider.load_all_control_files())
{
all_default_full_specs.emplace_back(
PackageSpec{scfl->to_name(), target_triplet},
InternalFeatureSet{FeatureNameCore.to_string(), FeatureNameDefault.to_string()});
}
auto ci_requested_default_full_specs = calculate_ci_requested_specs(provider, target_triplet, ports_to_exclude);

struct RandomizerInstance : GraphRandomizer
{
Expand All @@ -386,8 +421,12 @@ namespace vcpkg
PackagesDirAssigner packages_dir_assigner{paths.packages()};
CreateInstallPlanOptions create_install_plan_options(
randomizer, host_triplet, UnsupportedPortAction::Warn, UseHeadVersion::No, Editable::No);
auto action_plan = compute_full_plan(
paths, provider, var_provider, all_default_full_specs, packages_dir_assigner, create_install_plan_options);
auto action_plan = compute_full_plan(paths,
provider,
var_provider,
ci_requested_default_full_specs,
packages_dir_assigner,
create_install_plan_options);
BinaryCache binary_cache(fs);
if (!binary_cache.install_providers(args, paths, out_sink))
{
Expand All @@ -400,7 +439,19 @@ namespace vcpkg
LocalizedString not_supported_regressions;
{
std::string msg;
for (const auto& spec : all_default_full_specs)

// First, print ports that were excluded early (before dependency resolution)
for (const auto& excluded_spec : ports_to_exclude)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea I missed in my version of the same #1822 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #1822 (comment) in addition to fixing this problem I also fixed the "Summary" at the end to properly note that these were EXCLUDED

{
split_specs->known.emplace(excluded_spec, BuildResult::Excluded);
// Generate a simple hash for excluded ports (they don't get real ABIs)
std::string fake_abi = "0000000000000000000000000000000000000000000000000000000000000000";
split_specs->abi_map.emplace(excluded_spec, fake_abi);
split_specs->features.emplace(excluded_spec, std::vector<std::string>{"core"});
msg += fmt::format("{:>40}: {:>8}: {}\n", excluded_spec, "skip", fake_abi);
}

for (const auto& spec : ci_requested_default_full_specs)
{
if (!Util::Sets::contains(split_specs->abi_map, spec.package_spec))
{
Expand Down
Loading