chiark / gitweb /
build changes handling: Introduce postbuild_mergechanges
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 33306dade1b87c45c507964eb7199333946cb342..356b48c6ca1dc6dcdfc51613c9bdd02f987ee486 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -41,6 +41,7 @@ use Carp;
 use Debian::Dgit;
 
 our $our_version = 'UNRELEASED'; ###substituted###
+our $absurdity = undef; ###substituted###
 
 our @rpushprotovsn_support = qw(4 3 2); # 4 is new tag format
 our $protovsn;
@@ -73,7 +74,11 @@ our $tagformat_want;
 our $tagformat;
 our $tagformatfn;
 
-our %forceopts = map { $_=>0 } ();
+our %forceopts = map { $_=>0 }
+    qw(unrepresentable unsupported-source-format
+       dsc-changes-mismatch
+       import-gitapply-absurd
+       import-gitapply-no-absurd);
 
 our %format_ok = map { $_=>1 } ("1.0","3.0 (native)","3.0 (quilt)");
 
@@ -146,6 +151,11 @@ our @ourdscfield = qw(Dgit Vcs-Dgit-Master);
 our $csuite;
 our $instead_distro;
 
+if (!defined $absurdity) {
+    $absurdity = $0;
+    $absurdity =~ s{/[^/]+$}{/absurd} or die;
+}
+
 sub debiantag ($$) {
     my ($v,$distro) = @_;
     return $tagformatfn->($v, $distro);
@@ -230,6 +240,14 @@ sub forceable_fail ($$) {
     print STDERR "warning: overriding problem due to --force:\n". $msg;
 }
 
+sub forceing ($) {
+    my ($forceoptsl) = @_;
+    my @got = grep { $forceopts{$_} } @$forceoptsl;
+    return 0 unless @got;
+    print STDERR
+ "warning: skipping checks or functionality due to --force-$got[0]\n";
+}
+
 sub no_such_package () {
     print STDERR "$us: package $package does not exist in suite $isuite\n";
     exit 4;
@@ -1302,7 +1320,9 @@ sub get_archive_dsc () {
        $dsc = parsecontrolfh($dscfh,$dscurl,1);
        printdebug Dumper($dsc) if $debuglevel>1;
        my $fmt = getfield $dsc, 'Format';
-       fail "unsupported source format $fmt, sorry" unless $format_ok{$fmt};
+       $format_ok{$fmt} or forceable_fail [qw(unsupported-source-format)],
+           "unsupported source format $fmt, sorry";
+           
        $dsc_checked = !!$digester;
        printdebug "get_archive_dsc: Version ".(getfield $dsc, 'Version')."\n";
        return;
@@ -1948,25 +1968,54 @@ END
        local $ENV{GIT_AUTHOR_EMAIL} = $authline[1];
        local $ENV{GIT_AUTHOR_DATE} =  $authline[2];
 
-       eval {
-           runcmd shell_cmd 'exec >/dev/null 2>../../gbp-pq-output',
-               gbp_pq, qw(import);
-       };
-       if ($@) {
-           { local $@; eval { runcmd qw(cat ../../gbp-pq-output); }; }
-           die $@;
-       }
+       my $path = $ENV{PATH} or die;
+
+       foreach my $use_absurd (qw(0 1)) {
+           local $ENV{PATH} = $path;
+           if ($use_absurd) {
+               chomp $@;
+               progress "warning: $@";
+               $path = "$absurdity:$path";
+               progress "$us: trying slow absurd-git-apply...";
+               rename "../../gbp-pq-output","../../gbp-pq-output.0"
+                   or $!==ENOENT
+                   or die $!;
+           }
+           eval {
+               die "forbid absurd git-apply\n" if $use_absurd
+                   && forceing [qw(import-gitapply-no-absurd)];
+               die "only absurd git-apply!\n" if !$use_absurd
+                   && forceing [qw(import-gitapply-absurd)];
+
+               local $ENV{PATH} = $path if $use_absurd;
+
+               my @showcmd = (gbp_pq, qw(import));
+               my @realcmd = shell_cmd
+                   'exec >/dev/null 2>../../gbp-pq-output', @showcmd;
+               debugcmd "+",@realcmd;
+               if (system @realcmd) {
+                   die +(shellquote @showcmd).
+                       " failed: ".
+                       failedcmd_waitstatus()."\n";
+               }
 
-       my $gapplied = git_rev_parse('HEAD');
-       my $gappliedtree = cmdoutput @git, qw(rev-parse HEAD:);
-       $gappliedtree eq $dappliedtree or
-           fail <<END;
+               my $gapplied = git_rev_parse('HEAD');
+               my $gappliedtree = cmdoutput @git, qw(rev-parse HEAD:);
+               $gappliedtree eq $dappliedtree or
+                   fail <<END;
 gbp-pq import and dpkg-source disagree!
  gbp-pq import gave commit $gapplied
  gbp-pq import gave tree $gappliedtree
  dpkg-source --before-build gave tree $dappliedtree
 END
-       $rawimport_hash = $gapplied;
+               $rawimport_hash = $gapplied;
+           };
+           last unless $@;
+       }
+       if ($@) {
+           { local $@; eval { runcmd qw(cat ../../gbp-pq-output); }; }
+           die $@;
+       }
     }
 
     progress "synthesised git commit from .dsc $cversion";
@@ -3259,7 +3308,8 @@ END
 
     # Check that changes and .dsc agree enough
     $changesfile =~ m{[^/]*$};
-    files_compare_inputs($dsc, parsecontrol($changesfile,$&));
+    files_compare_inputs($dsc, parsecontrol($changesfile,$&))
+       unless forceing [qw(dsc-changes-mismatch)];
 
     # Checks complete, we're going to try and go ahead:
 
@@ -4613,7 +4663,7 @@ END
     if (@unrepres) {
        print STDERR "dgit:  cannot represent change: $_->[1]: $_->[0]\n"
            foreach @unrepres;
-       fail <<END;
+       forceable_fail [qw(unrepresentable)], <<END;
 HEAD has changes to .orig[s] which are not representable by `3.0 (quilt)'
 END
     }
@@ -4841,6 +4891,53 @@ sub massage_dbp_args ($;$) {
     return $r;
 }
 
+sub in_parent (&) {
+    my ($fn) = @_;
+    my $wasdir = must_getcwd();
+    changedir "..";
+    $fn->();
+    changedir $wasdir;
+}    
+
+sub postbuild_mergechanges ($) { # must run with CWD=.. (eg in in_parent)
+    my ($msg_if_onlyone) = @_;
+    # If there is only one .changes file, fail with $msg_if_onlyone,
+    # or if that is undef, be a no-op.
+    # Returns the changes file to report to the user.
+    my $pat = changespat $version;
+    my @changesfiles = glob $pat;
+    @changesfiles = sort {
+       ($b =~ m/_source\.changes$/ <=> $a =~ m/_source\.changes$/)
+           or $a cmp $b
+    } @changesfiles;
+    my $result;
+    if (@changesfiles==1) {
+       fail <<END.$msg_if_onlyone if defined $msg_if_onlyone;
+only one changes file from build (@changesfiles)
+END
+       $result = $changesfiles[0];
+    } elsif (@changesfiles==2) {
+       my $binchanges = parsecontrol($changesfiles[1], "binary changes file");
+       foreach my $l (split /\n/, getfield $binchanges, 'Files') {
+           fail "$l found in binaries changes file $binchanges"
+               if $l =~ m/\.dsc$/;
+       }
+       runcmd_ordryrun_local @mergechanges, @changesfiles;
+       my $multichanges = changespat $version,'multi';
+       if (act_local()) {
+           stat_exists $multichanges or fail "$multichanges: $!";
+           foreach my $cf (glob $pat) {
+               next if $cf eq $multichanges;
+               rename "$cf", "$cf.inmulti" or fail "$cf\{,.inmulti}: $!";
+           }
+       }
+       $result = $multichanges;
+    } else {
+       fail "wrong number of different changes files (@changesfiles)";
+    }
+    printdone "build successful, results in $result\n" or die $!;
+}
+
 sub cmd_build {
     my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts_initial(), @ARGV);
     my $wantsrc = massage_dbp_args \@dbp;
@@ -4974,43 +5071,21 @@ Suggest you delete @unwanted.
 END
            if @unwanted;
     }
-    my $wasdir = must_getcwd();
-    changedir "..";
-    if (act_local()) {
-       stat_exists $dscfn or fail "$dscfn (in parent directory): $!";
-       stat_exists $sourcechanges
-           or fail "$sourcechanges (in parent directory): $!";
-    }
-    runcmd_ordryrun_local @sbuild, qw(-d), $isuite, @ARGV, $dscfn;
-    my @changesfiles = glob $pat;
-    @changesfiles = sort {
-       ($b =~ m/_source\.changes$/ <=> $a =~ m/_source\.changes$/)
-           or $a cmp $b
-    } @changesfiles;
-    fail <<END if @changesfiles==1;
-only one changes file from sbuild (@changesfiles)
+    in_parent {
+       if (act_local()) {
+           stat_exists $dscfn or fail "$dscfn (in parent directory): $!";
+           stat_exists $sourcechanges
+               or fail "$sourcechanges (in parent directory): $!";
+       }
+       runcmd_ordryrun_local @sbuild, qw(-d), $isuite, @ARGV, $dscfn;
+    };
+    maybe_unapply_patches_again();
+    in_parent {
+       postbuild_mergechanges(<<END);
 perhaps you need to pass -A ?  (sbuild's default is to build only
 arch-specific binaries; dgit 1.4 used to override that.)
 END
-    fail "wrong number of different changes files (@changesfiles)"
-       unless @changesfiles==2;
-    my $binchanges = parsecontrol($changesfiles[1], "binary changes file");
-    foreach my $l (split /\n/, getfield $binchanges, 'Files') {
-       fail "$l found in binaries changes file $binchanges"
-           if $l =~ m/\.dsc$/;
-    }
-    runcmd_ordryrun_local @mergechanges, @changesfiles;
-    my $multichanges = changespat $version,'multi';
-    if (act_local()) {
-       stat_exists $multichanges or fail "$multichanges: $!";
-       foreach my $cf (glob $pat) {
-           next if $cf eq $multichanges;
-           rename "$cf", "$cf.inmulti" or fail "$cf\{,.inmulti}: $!";
-       }
-    }
-    changedir $wasdir;
-    maybe_unapply_patches_again();
-    printdone "build successful, results in $multichanges\n" or die $!;
+    };
 }    
 
 sub cmd_quilt_fixup {
@@ -5188,6 +5263,10 @@ sub parseopts () {
                push @ropts, $&;
                $forceopts{$1} = 1;
                $_='';
+           } elsif (m/^--force-/) {
+               print STDERR
+                   "$us: warning: ignoring unknown force option $_\n";
+               $_='';
            } elsif (m/^--dgit-tag-format=(old|new)$/s) {
                # undocumented, for testing
                push @ropts, $_;