# dgit
# Integration between git and Debian-style archives
#
-# Copyright (C)2013-2017 Ian Jackson
-# Copyright (C)2017 Sean Whitton
+# Copyright (C)2013-2018 Ian Jackson
+# Copyright (C)2017-2018 Sean Whitton
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
our (@gbp_build) = ('');
our (@gbp_pq) = ('gbp pq');
our (@changesopts) = ('');
+our (@pbuilder) = ("sudo -E pbuilder");
+our (@cowbuilder) = ("sudo -E cowbuilder");
our %opts_opt_map = ('dget' => \@dget, # accept for compatibility
'curl' => \@curl,
'gbp-build' => \@gbp_build,
'gbp-pq' => \@gbp_pq,
'ch' => \@changesopts,
- 'mergechanges' => \@mergechanges);
+ 'mergechanges' => \@mergechanges,
+ 'pbuilder' => \@pbuilder,
+ 'cowbuilder' => \@cowbuilder);
our %opts_opt_cmdonly = ('gpg' => 1, 'git' => 1);
our %opts_cfg_insertpos = map {
autoflush STDOUT 1;
our $supplementary_message = '';
-our $need_split_build_invocation = 1;
our $split_brain = 0;
END {
}
sub opts_opt_multi_cmd {
+ my $extra = shift;
my @cmd;
push @cmd, split /\s+/, shift @_;
+ push @cmd, @$extra;
push @cmd, @_;
@cmd;
}
sub gbp_pq {
- return opts_opt_multi_cmd @gbp_pq;
+ return opts_opt_multi_cmd [], @gbp_pq;
}
sub dgit_privdir () {
dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
dgit [dgit-opts] build [dpkg-buildpackage-opts]
dgit [dgit-opts] sbuild [sbuild-opts]
+ dgit [dgit-opts] pbuilder|cowbuilder [debbuildopts]
dgit [dgit-opts] push [dgit-opts] [suite]
dgit [dgit-opts] push-source [dgit-opts] [suite]
dgit [dgit-opts] rpush build-host:build-dir ...
dopush();
}
-our $sourcechanges;
-
-sub cmd_push_source {
- prep_push();
- fail "dgit push-source: --include-dirty/--ignore-dirty does not make".
- "sense with push-source!" if $includedirty;
- clean_tree();
- build_maybe_quilt_fixup();
- if ($changesfile) {
- my $changes = parsecontrol("$buildproductsdir/$changesfile",
- "source changes file");
- unless (test_source_only_changes($changes)) {
- fail "user-specified changes file is not source-only";
- }
- } else {
- # Building a source package is very fast, so just do it
- build_source();
- die "er, patches are applied dirtily but shouldn't be.."
- if $patches_applied_dirtily;
- $changesfile = $sourcechanges;
- }
- dopush();
-}
-
#---------- remote commands' implementation ----------
sub pre_remote_push_build_host {
#---------- building etc. ----------
our $version;
+our $sourcechanges;
our $dscfn;
#----- `3.0 (quilt)' handling -----
quilt_fixup_multipatch($clogp, $headref, $upstreamversion);
}
- die 'bug' if $split_brain && !$need_split_build_invocation;
-
changedir $maindir;
runcmd_ordryrun_local
@git, qw(pull --ff-only -q), "$playground/work", qw(master);
$isuite = getfield $clogp, 'Distribution';
$package = getfield $clogp, 'Source';
$version = getfield $clogp, 'Version';
+ $dscfn = dscfn($version);
}
sub build_prep_early () {
sub build_prep ($) {
my ($wantsrc) = @_;
build_prep_early();
- clean_tree();
+ # clean the tree if we're trying to include dirty changes in the
+ # source package, or we are running the builder in $maindir
+ clean_tree() if $includedirty || ($wantsrc & WANTSRC_BUILDER);
build_maybe_quilt_fixup();
if ($rmchanges) {
my $pat = changespat $version;
sub massage_dbp_args ($;$) {
my ($cmd,$xargs) = @_;
- # We need to:
- #
- # - if we're going to split the source build out so we can
- # do strange things to it, massage the arguments to dpkg-buildpackage
- # so that the main build doessn't build source (or add an argument
- # to stop it building source by default).
- #
- # - add -nc to stop dpkg-source cleaning the source tree,
- # unless we're not doing a split build and want dpkg-source
- # as cleanmode, in which case we can do nothing
- #
+ # Since we split the source build out so we can do strange things
+ # to it, massage the arguments to dpkg-buildpackage so that the
+ # main build doessn't build source (or add an argument to stop it
+ # building source by default).
debugcmd '#massaging#', @$cmd if $debuglevel>1;
-#print STDERR "MASS0 ",Dumper($cmd, $xargs, $need_split_build_invocation);
- if ($cleanmode eq 'dpkg-source' && !$need_split_build_invocation) {
- $clean_using_builder = 1;
- return WANTSRC_BUILDER;
- }
# -nc has the side effect of specifying -b if nothing else specified
# and some combinations of -S, -b, et al, are errors, rather than
# later simply overriding earlie. So we need to:
push @$cmd, '-nc';
#print STDERR "MASS1 ",Dumper($cmd, $xargs, $dmode);
my $r = WANTSRC_BUILDER;
- if ($need_split_build_invocation) {
- printdebug "massage split $dmode.\n";
- $r = $dmode =~ m/[S]/ ? WANTSRC_SOURCE :
- $dmode =~ y/gGF/ABb/ ? WANTSRC_SOURCE | WANTSRC_BUILDER :
- $dmode =~ m/[ABb]/ ? WANTSRC_BUILDER :
- die "$dmode ?";
- }
+ printdebug "massage split $dmode.\n";
+ $r = $dmode =~ m/[S]/ ? WANTSRC_SOURCE :
+ $dmode =~ y/gGF/ABb/ ? WANTSRC_SOURCE | WANTSRC_BUILDER :
+ $dmode =~ m/[ABb]/ ? WANTSRC_BUILDER :
+ die "$dmode ?";
printdebug "massage done $r $dmode.\n";
push @$cmd, $dmode;
#print STDERR "MASS2 ",Dumper($cmd, $xargs, $r);
if ($gbp_make_orig) {
clean_tree();
$cleanmode = 'none'; # don't do it again
- $need_split_build_invocation = 1;
}
my @dbp = @dpkgbuildpackage;
$gbp_build[0] = 'gbp buildpackage';
}
}
- my @cmd = opts_opt_multi_cmd @gbp_build;
+ my @cmd = opts_opt_multi_cmd [], @gbp_build;
push @cmd, (qw(-us -uc --git-no-sign-tags),
"--git-builder=".(shellquote @dbp));
}
sub cmd_git_build { cmd_gbp_build(); } # compatibility with <= 1.0
+sub building_source_in_playtree {
+ # If $includedirty, we have to build the source package from the
+ # working tree, not a playtree, so that uncommitted changes are
+ # included (copying or hardlinking them into the playtree could
+ # cause trouble).
+ #
+ # Note that if we are building a source package in split brain
+ # mode we do not support including uncommitted changes, because
+ # that makes quilt fixup too hard. I.e. ($split_brain && (dgit is
+ # building a source package)) => !$includedirty
+ return !$includedirty;
+}
+
sub build_source {
$sourcechanges = changespat $version,'source';
if (act_local()) {
unlink "$buildproductsdir/$sourcechanges" or $!==ENOENT
or fail "remove $sourcechanges: $!";
}
- $dscfn = dscfn($version);
my @cmd = (@dpkgsource, qw(-b --));
- if ($split_brain) {
+ my $leafdir;
+ if (building_source_in_playtree()) {
+ $leafdir = 'work';
+ my $headref = git_rev_parse('HEAD');
+ # If we are in split brain, there is already a playtree with
+ # the thing we should package into a .dsc (thanks to quilt
+ # fixup). If not, make a playtree
+ prep_ud() unless $split_brain;
changedir $playground;
- runcmd_ordryrun_local @cmd, "work";
- my @udfiles = <${package}_*>;
- changedir $maindir;
- foreach my $f (@udfiles) {
- printdebug "source copy, found $f\n";
- next unless
- $f eq $dscfn or
- ($f =~ m/\.debian\.tar(?:\.\w+)$/ &&
- $f eq srcfn($version, $&));
- printdebug "source copy, found $f - renaming\n";
- rename "$playground/$f", "$buildproductsdir/$f" or $!==ENOENT
- or fail "put in place new source file ($f): $!";
+ unless ($split_brain) {
+ my $upstreamversion = upstreamversion $version;
+ unpack_playtree_linkorigs($upstreamversion, sub { });
+ unpack_playtree_mkwork($headref);
+ changedir '..';
}
} else {
- my $pwd = must_getcwd();
- my $leafdir = basename $pwd;
- changedir "..";
- runcmd_ordryrun_local @cmd, $leafdir;
- changedir $pwd;
+ $leafdir = basename $maindir;
+ changedir '..';
}
+ runcmd_ordryrun_local @cmd, $leafdir;
+
+ changedir $leafdir;
runcmd_ordryrun_local qw(sh -ec),
- 'exec >$1; shift; exec "$@"','x',
- "$buildproductsdir/$sourcechanges",
+ 'exec >../$1; shift; exec "$@"','x', $sourcechanges,
@dpkggenchanges, qw(-S), changesopts();
+ changedir '..';
+
+ printdebug "moving $dscfn, $sourcechanges, etc. to ".bpd_abs()."\n";
+ $dsc = parsecontrol($dscfn, "source package");
+
+ my $mv = sub {
+ my ($why, $l) = @_;
+ printdebug " renaming ($why) $l\n";
+ rename "$l", bpd_abs()."/$l"
+ or fail "put in place new built file ($l): $!";
+ };
+ foreach my $l (split /\n/, getfield $dsc, 'Files') {
+ $l =~ m/\S+$/ or next;
+ $mv->('Files', $&);
+ }
+ $mv->('dsc', $dscfn);
+ $mv->('changes', $sourcechanges);
+
+ changedir $maindir;
}
sub cmd_build_source {
printdone "source built, results in $dscfn and $sourcechanges";
}
-sub cmd_sbuild {
- build_prep(WANTSRC_SOURCE); # not BUILDER because sbuild uses the .dsc
+sub cmd_push_source {
+ prep_push();
+ fail "dgit push-source: --include-dirty/--ignore-dirty does not make".
+ "sense with push-source!" if $includedirty;
+ build_maybe_quilt_fixup();
+ if ($changesfile) {
+ my $changes = parsecontrol("$buildproductsdir/$changesfile",
+ "source changes file");
+ unless (test_source_only_changes($changes)) {
+ fail "user-specified changes file is not source-only";
+ }
+ } else {
+ # Building a source package is very fast, so just do it
+ build_source();
+ die "er, patches are applied dirtily but shouldn't be.."
+ if $patches_applied_dirtily;
+ $changesfile = $sourcechanges;
+ }
+ dopush();
+}
+
+sub binary_builder {
+ my ($bbuilder, $pbmc_msg, @args) = @_;
+ build_prep(WANTSRC_SOURCE);
build_source();
midbuild_checkchanges();
in_bpd {
if (act_local()) {
- stat_exists $dscfn or fail "$dscfn (in parent directory): $!";
+ stat_exists $dscfn or fail "$dscfn (in build products dir): $!";
stat_exists $sourcechanges
- or fail "$sourcechanges (in parent directory): $!";
+ or fail "$sourcechanges (in build products dir): $!";
}
- runcmd_ordryrun_local @sbuild, qw(-d), $isuite, @ARGV, $dscfn;
+ runcmd_ordryrun_local @$bbuilder, @args;
};
maybe_unapply_patches_again();
in_bpd {
- postbuild_mergechanges(<<END);
+ postbuild_mergechanges($pbmc_msg);
+ };
+}
+
+sub cmd_sbuild {
+ build_prep_early();
+ binary_builder(\@sbuild, <<END, qw(-d), $isuite, @ARGV, $dscfn);
perhaps you need to pass -A ? (sbuild's default is to build only
arch-specific binaries; dgit 1.4 used to override that.)
END
- };
-}
+}
+
+sub pbuilder ($) {
+ my ($pbuilder) = @_;
+ build_prep_early();
+ # @ARGV is allowed to contain only things that should be passed to
+ # pbuilder under debbuildopts; just massage those
+ my $wantsrc = massage_dbp_args \@ARGV;
+ fail "you asked for a builder but your debbuildopts didn't ask for".
+ " any binaries -- is this really what you meant?"
+ unless $wantsrc & WANTSRC_BUILDER;
+ fail "we must build a .dsc to pass to the builder but your debbuiltopts".
+ " forbids the building of a source package; cannot continue"
+ unless $wantsrc & WANTSRC_SOURCE;
+ # We do not want to include the verb "build" in @pbuilder because
+ # the user can customise @pbuilder and they shouldn't be required
+ # to include "build" in their customised value. However, if the
+ # user passes any additional args to pbuilder using the dgit
+ # option --pbuilder:foo, such args need to come after the "build"
+ # verb. opts_opt_multi_cmd does all of that.
+ binary_builder([opts_opt_multi_cmd ["build"], @$pbuilder], undef,
+ qw(--debbuildopts), "@ARGV", qw(--distribution), $isuite,
+ $dscfn);
+}
+
+sub cmd_pbuilder {
+ pbuilder(\@pbuilder);
+}
+
+sub cmd_cowbuilder {
+ pbuilder(\@cowbuilder);
+}
sub cmd_quilt_fixup {
badusage "incorrect arguments to dgit quilt-fixup" if @ARGV;
push @ropts, $_;
$tagformat_want = [ $1, 'command line', 1 ];
# 1 menas overrides distro configuration
- } elsif (m/^--always-split-source-build$/s) {
- # undocumented, was once for testing, now a no-op
- push @ropts, $_;
- $need_split_build_invocation = 1;
} elsif (m/^--config-lookup-explode=(.+)$/s) {
# undocumented, for testing
push @ropts, $_;
$$vr = $v;
}
- $need_split_build_invocation ||= quiltmode_splitbrain();
-
fail "dgit: --include-dirty is not supported in split view quilt mode"
if $split_brain && $includedirty;