2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 08:33:20 +00:00

test(xcat-core): capture the Debian genesis dependency ignoring the architecture

The ppc64el and riscv64 xcat debs depend on xcat-genesis-scripts-amd64, and
xcat-genesis-scripts-ppc64 depends on xcat-genesis-base-ppc64, a package no
repository publishes. Nothing reports either one: the amd64 scripts package is
Architecture: all, so it installs on any architecture, and the broken ppc64
dependency is never reached because nothing pulls that package.

Extend debian_control_arch_coverage.t. It now reads the Depends field of
xCAT/debian/control and xCATsn/debian/control, applies each architecture
restriction the way dpkg-gencontrol does, and asserts that the genesis scripts
a given architecture receives are that architecture's own. It also asserts that
xCAT-genesis-scripts/debian/control-<arch> builds xcat-genesis-scripts-<arch>
and depends on xcat-genesis-base-<arch>.

Eight of the eighteen assertions fail on this tree.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-04 18:33:56 -03:00
parent 1db2c7cde0
commit 76d6967ed7
@@ -37,4 +37,102 @@ for my $ctl (@controls) {
}
}
# The genesis dependency must follow the architecture. xCAT and xCATsn are built once per
# architecture from one control file, so an unrestricted "Depends: xcat-genesis-scripts-amd64"
# reaches the ppc64el and riscv64 debs too. That package is Architecture: all, so it installs and
# apt reports no error -- it lays down the x86_64 Genesis tree and pulls the 128 MB amd64
# genesis-base, and the management node gets no Genesis for its own architecture. The rpm side
# already selects per architecture through %{?genesistarch:Requires: xCAT-genesis-scripts-...}.
#
# xCAT-genesis-scripts keeps one control file per Debian architecture, and the file name is the
# Debian architecture. Its package name and its genesis-base dependency must carry that same
# architecture: xcat-genesis-base-ppc64 is a name no repository publishes, while the base deb
# that builddeb-genesis-base builds for ppc64el is xcat-genesis-base-ppc64el.
# Return the folded value of a control field, or undef.
sub control_field {
my ($text, $name) = @_;
return $1 if $text =~ /^\Q$name\E:[ \t]*(.*(?:\n[ \t]+.*)*)/m;
return;
}
# Split a dependency field into [package name, architecture restriction] pairs. Alternatives
# separated by "|" are returned one by one, because a restriction binds to one alternative.
sub dependency_terms {
my ($field) = @_;
my @terms;
return @terms unless defined $field;
$field =~ s/\n/ /g;
for my $dep (split /,/, $field) {
for my $alt (split /\|/, $dep) {
next unless $alt =~ /^\s*([A-Za-z0-9][A-Za-z0-9+.-]*)\s*(?:\([^)]*\))?\s*(?:\[([^\]]*)\])?/;
push @terms, [ $1, $2 ];
}
}
return @terms;
}
# dpkg-gencontrol drops a dependency whose architecture restriction excludes the build
# architecture. No restriction means the dependency reaches every architecture.
sub term_applies {
my ($restriction, $arch) = @_;
return 1 unless defined $restriction;
my @tokens = grep { length } split /\s+/, $restriction;
return 1 unless @tokens;
my $negated = ($tokens[0] =~ /^!/) ? 1 : 0;
my %named = map { my $t = $_; $t =~ s/^!//; $t =~ s/^any-//; ($t => 1) } @tokens;
return $negated ? (exists $named{$arch} ? 0 : 1) : (exists $named{$arch} ? 1 : 0);
}
# The architectures xCAT-genesis-scripts is packaged for, taken from its per-architecture control
# files. riscv64 has none on purpose: its Genesis is the OpenEmbedded image.
my $scripts_debian = "$root/xCAT-genesis-scripts/debian";
my @scripts_arches = sort map { m{/control-(.+)$} ? $1 : () } glob("$scripts_debian/control-*");
SKIP: {
skip 'xCAT-genesis-scripts has no per-architecture control files', 1 unless @scripts_arches;
for my $arch (@scripts_arches) {
my $ctl = "$scripts_debian/control-$arch";
open my $fh, '<', $ctl or die "read $ctl: $!";
local $/; my $text = <$fh>; close $fh;
my ($package) = ($text =~ /^Package:\s*(\S+)/m);
is($package, "xcat-genesis-scripts-$arch",
"control-$arch builds xcat-genesis-scripts-$arch");
my @bases = grep { /^xcat-genesis-base-/ }
map { $_->[0] } dependency_terms(control_field($text, 'Depends'));
is_deeply(\@bases, ["xcat-genesis-base-$arch"],
"xcat-genesis-scripts-$arch depends on xcat-genesis-base-$arch");
}
for my $ctl (@controls) {
open my $fh, '<', $ctl or die "read $ctl: $!";
local $/; my $text = <$fh>; close $fh;
(my $short = $ctl) =~ s{^\Q$root\E/}{};
my ($arch_line) = ($text =~ /^Architecture:\s*(.+)$/m);
next unless defined $arch_line;
my @built = grep { !/^(?:any|all)$/ } split /\s+/, $arch_line;
my @genesis = grep { $_->[0] =~ /^xcat-genesis-scripts-/ }
dependency_terms(control_field($text, 'Depends'));
for my $arch (@built) {
my @reaching = map { $_->[0] }
grep { term_applies($_->[1], $arch) } @genesis;
my @foreign = grep { $_ ne "xcat-genesis-scripts-$arch" } @reaching;
is_deeply(\@foreign, [],
"$short on $arch depends on no other architecture's genesis scripts");
my %packaged = map { $_ => 1 } @scripts_arches;
next unless $packaged{$arch};
ok(scalar(grep { $_ eq "xcat-genesis-scripts-$arch" } @reaching),
"$short on $arch depends on xcat-genesis-scripts-$arch");
}
}
}
done_testing();