Skip to content

Commit f888736

Browse files
Add support for .abi-compliance-history file for having custom baselines AND update docs
1 parent c692b14 commit f888736

1 file changed

Lines changed: 121 additions & 57 deletions

File tree

PGBuild/Modules/ABICompCheck.pm

Lines changed: 121 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ See accompanying License file for license details
1010
=head1 PGBuild::Modules::ABICompCheck
1111
1212
This module is used for ABI compliance checking of PostgreSQL builds by
13-
comparing the latest commit on a stable branch with the most recent tag on that
14-
branch. This helps detect unintended changes that could break compatibility for
15-
extensions or client applications.
13+
comparing the latest commit on a stable branch with a baseline reference (most
14+
recent tag, first commit of the branch, or a custom commit specified in
15+
C<.abi-compliance-history>). This helps detect unintended changes that could
16+
break compatibility for extensions or client applications.
1617
1718
=head2 EXECUTION FLOW
1819
@@ -32,35 +33,49 @@ is constructed dynamically by scanning all the .so files directly under the C<in
3233
3334
=item 3.
3435
35-
The module identifies the most recent tag for a particular branch (e.g.,
36-
REL_16_1) to use as a baseline for comparison against the most recent commit of
37-
the branch.
36+
The module identifies a baseline reference for comparison. This can be:
37+
38+
=over
39+
40+
=item *
41+
42+
The most recent tag on the branch (e.g., REL_16_1), if found and not older than the branch point.
43+
44+
=item *
45+
46+
The first commit of the current branch, if no suitable tag is found or if the latest tag predates the branch.
47+
48+
=item *
49+
50+
A custom commit/tag specified in C<pgsql/.abi-compliance-history> file (this overrides the automatic selection).
51+
52+
=back
3853
3954
=item 4.
4055
41-
It checks if a pre-existing, complete build and ABI dump for this baseline tag
42-
exists in its working directory (C<buildroot/abicheck.$animal_name>).
56+
It checks if a pre-existing, complete build and ABI dump for this baseline
57+
reference exists in its working directory (C<buildroot/abicheck.$animal_name>).
4358
4459
=item 5.
4560
46-
If the baseline tag's build is missing or incomplete, the module performs a
47-
fresh build of that tag:
61+
If the baseline reference's build is missing or has changed, the module performs
62+
a fresh build:
4863
4964
=over
5065
5166
=item *
5267
53-
It checks out the source code for the tag.
68+
It checks out the source code for the baseline reference.
5469
5570
=item *
56-
It runs C<configure>, C<make>, and C<make install> for the tag in an isolated
71+
It runs C<configure>, C<make>, and C<make install> for the baseline reference in an isolated
5772
directory.
5873
5974
=item *
6075
6176
It uses C<abidw> to generate XML representations of the ABI for key binaries
6277
(the C<postgres> executable and all shared libraries found directly under the
63-
installation's C<lib> directory) from this tag build. These are stored for
78+
installation's C<lib> directory) from this baseline build. These are stored for
6479
future runs.
6580
6681
=back
@@ -73,18 +88,20 @@ the main build (the latest commit).
7388
=item 7.
7489
7590
Using C<abidiff>, it compares the ABI XML file of each binary from the latest
76-
commit against the corresponding file from the baseline tag.
91+
commit against the corresponding file from the baseline reference.
7792
7893
=item 8.
7994
80-
Any differences detected by C<abidiff> are collected into a log report. If no
95+
Any differences detected by C<abidiff> are collected into a log report. A list
96+
of successfully compared binaries is also included in the output. If no
8197
differences are found, a success message is logged.
8298
8399
=item 9.
84100
85-
The final report, containing either the ABI differences or "no abi diffs found
86-
in this run", is sent to the build farm server as part of the overall build
87-
status.
101+
The final report, containing the list of compared binaries, either the ABI
102+
differences or "no abi diffs found in this run", and optionally build logs if
103+
the baseline was rebuilt, is sent to the build farm server as part of the
104+
overall build status.
88105
89106
=back
90107
@@ -111,8 +128,10 @@ An array reference containing flags to pass to C<abidw>. Defaults to:
111128
112129
=item C<tag_for_branch>
113130
114-
A hash reference mapping branch names to their corresponding tags for ABI
115-
comparison. Defaults to empty hash which means latest tags for all branches.
131+
A hash reference mapping branch names to their corresponding tags or tag
132+
patterns for ABI comparison. Supports exact tag names or patterns (e.g.,
133+
'REL_17_*'). If a pattern matches multiple tags, the first one is used. Defaults
134+
to empty hash, which means automatic tag selection for all branches.
116135
117136
=back
118137
@@ -141,6 +160,12 @@ Before using this module, ensure that you have the
141160
L<libabigail|https://github.com/libabigail/libabigail> tools (e.g., the
142161
C<abigail-tools> Apt package) installed on your animal.
143162
163+
=item *
164+
165+
You can override the automatic baseline selection by creating a
166+
C<pgsql/.abi-compliance-history> file containing the commit SHA. This
167+
is useful for comparing against a specific commit when needed.
168+
144169
=back
145170
146171
=head2 EXAMPLE LOG OUTPUT
@@ -152,9 +177,13 @@ Example output will be similar to:
152177
Git HEAD: 61c37630774002fb36a5fa17f57caa3a9c2165d9
153178
Changes since: REL_17_6
154179
155-
baseline_tag updated from REL_17_5 to REL_17_6
180+
baseline updated from REL_17_5 to REL_17_6
181+
Binaries compared:
182+
bin/postgres
183+
lib/libpq.so
184+
156185
no abi diffs found in this run - Or ABI diff if any
157-
....other build logs for recent tag if any
186+
....other build logs for baseline if rebuilt
158187
159188
=cut
160189

@@ -313,7 +342,7 @@ sub installcheck
313342
my $abi_compare_loc = "$self->{abi_compare_root}/$pgbranch";
314343
mkdir $abi_compare_loc unless -d $abi_compare_loc;
315344
my $tag_for_branch = $self->{tag_for_branch} || {};
316-
my $baseline_tag;
345+
my $comparison_ref = '';
317346

318347
# find the tag to compare with for the branch
319348
if (exists $tag_for_branch->{$pgbranch})
@@ -333,46 +362,81 @@ sub installcheck
333362
# use the first tag from the list in case of regex
334363
emit
335364
"Using $tags[0] as the baseline tag for branch $pgbranch based on pattern '$tag_pattern'";
336-
$baseline_tag = $tags[0];
365+
$comparison_ref = $tags[0];
337366
}
338367
else
339368
{
369+
# If no specific tag is configured, find the most recent tag on the branch.
340370
emit "Finding latest tag for branch $pgbranch";
341-
$baseline_tag =
342-
run_log(qq{git -C ./pgsql describe --tags --abbrev=0 2>/dev/null})
343-
; # Find the latest tag
344-
}
345-
chomp $baseline_tag;
346-
my $comparison_ref = '';
347-
$comparison_ref = run_log(qq{git -C ./pgsql merge-base master bf_$pgbranch})
348-
; # Find the very first commit for current branch
349-
die "git merge-base failed: $?" if $?;
350-
chomp $comparison_ref;
351-
352-
if ($baseline_tag)
353-
{
354-
# if some baseline tag is found, then get the commit SHA for the latest tagged commit
355-
# and compare with the first commit for current branch
356-
# using `git merge-base --is-ancestor A B` to
357-
my $tag_commit =
358-
run_log(qq{git -C ./pgsql rev-list -n 1 $baseline_tag});
359-
die "git rev-list failed: $?" if $?;
360-
chomp $tag_commit;
361-
362-
my $is_ancestor = system(
363-
qq{git -C ./pgsql merge-base --is-ancestor $tag_commit $comparison_ref 2>/dev/null}
364-
);
365-
if ($is_ancestor)
371+
my $baseline = run_log(qq{git -C ./pgsql describe --tags --abbrev=0 2>/dev/null});
372+
chomp $baseline;
373+
374+
# Default to the latest tag if found.
375+
if ($baseline)
376+
{
377+
# Find the first commit of the current branch.
378+
my $first_commit =
379+
run_log(qq{git -C ./pgsql merge-base master bf_$pgbranch});
380+
die "git merge-base failed: $?" if $?;
381+
chomp $first_commit;
382+
383+
# Check if the latest tag is an ancestor of the first commit of the branch.
384+
# If it is, it's likely a tag from a previous branch, so we should
385+
# use the first commit of the current branch as the baseline instead.
386+
my $is_ancestor = system(qq{git -C ./pgsql merge-base --is-ancestor $baseline $first_commit 2>/dev/null});
387+
388+
if ($is_ancestor == 0)
389+
{
390+
# The tag is an ancestor of the branch point, so it's too old.
391+
# Use the first commit of the branch as the baseline.
392+
$comparison_ref = $first_commit;
393+
emit "Latest tag '$baseline' is older than branch point. Using first branch commit '$comparison_ref' as baseline.";
394+
}
395+
else
396+
{
397+
# The tag is on the current branch, so use it.
398+
$comparison_ref = $baseline;
399+
emit "Using latest tag '$comparison_ref' as baseline.";
400+
}
401+
}
402+
else
403+
{
404+
# As a fallback, find the first commit of the current branch.
405+
# This is not ideal as it might be too old for a meaningful ABI comparison.
406+
$comparison_ref =
407+
run_log(qq{git -C ./pgsql merge-base master bf_$pgbranch});
408+
die "git merge-base failed: $?" if $?;
409+
chomp $comparison_ref;
410+
emit "No tags found. Using first branch commit '$comparison_ref' as baseline.";
411+
}
412+
413+
# Allow overriding the baseline via .abi-compliance-history file.
414+
if (-f "pgsql/.abi-compliance-history")
366415
{
367-
# If the latest tag is not an ancestor of the first branch commit
368-
# we need to use the latest tag as the comparison reference
369-
# else we use the first commit of the branch instead of tag
370-
$comparison_ref = $baseline_tag;
371-
emit "Baseline tag: $baseline_tag";
416+
open my $fh, '<', "pgsql/.abi-compliance-history"
417+
or die "Cannot open pgsql/.abi-compliance-history: $!";
418+
while (my $line = <$fh>)
419+
{
420+
next if $line =~ /^\s*#/ || $line =~ /^\s*$/;
421+
$line =~ s/\s*#.*//;
422+
$line =~ s/^\s+|\s+$//g;
423+
# Check that the commit/tag actually exists.
424+
my $exit_status = system(qq{git -C ./pgsql cat-file -e $line^{commit} 2>/dev/null});
425+
if ($exit_status != 0)
426+
{
427+
die
428+
"Wrong or non-existent commit/tag '$line' found in .abi-compliance-history";
429+
}
430+
$comparison_ref = $line;
431+
emit
432+
"Overriding baseline with '$comparison_ref' from .abi-compliance-history";
433+
last;
434+
}
435+
close $fh;
372436
}
373437
}
374438

375-
# get the previous tag from the latest_tag file for current branch if exists
439+
# Get the previous tag from the latest_tag file for current branch if it exists.
376440
my $baseline_tag_file = "$abi_compare_loc/latest_tag";
377441
my $previous_tag = '';
378442
if (-e $baseline_tag_file)
@@ -384,7 +448,7 @@ sub installcheck
384448
chomp $previous_tag if $previous_tag;
385449
}
386450

387-
# Initialise output log with basic information
451+
# Initialise output log with basic information.
388452
my $latest_commit_sha = run_log(qq{git -C ./pgsql rev-parse HEAD});
389453
die "git rev-parse HEAD failed: $?" if $?;
390454
chomp $latest_commit_sha;
@@ -399,7 +463,7 @@ sub installcheck
399463
if ($previous_tag ne $comparison_ref)
400464
{
401465
push(@saveout,
402-
"baseline_tag updated from $previous_tag to $comparison_ref\n");
466+
"baseline updated from $previous_tag to $comparison_ref\n");
403467
$rebuild_tag = 1;
404468
}
405469

0 commit comments

Comments
 (0)