]> git.donarmstrong.com Git - debhelper.git/blob - dh
don't pass --no-act on
[debhelper.git] / dh
1 #!/usr/bin/perl -w
2
3 =head1 NAME
4
5 dh - debhelper command sequencer
6
7 =cut
8
9 use strict;
10 use Debian::Debhelper::Dh_Lib;
11
12 =head1 SYNOPSIS
13
14 B<dh> sequence [B<--until> I<cmd>] [B<--before> I<cmd>] [B<--after> I<cmd>] [B<--remaining> [S<I<debhelper options>>]
15
16 =head1 DESCRIPTION
17
18 dh runs a sequence of debhelper commands. The supported sequences
19 correspond to the targets of a debian/rules file: "build", "clean",
20 "install", "binary-arch", "binary-indep", and "binary".
21
22 Commands in the binary-indep sequence are passed the "-i" option to ensure
23 they only work on binary independent packages, and commands in the
24 binary-arch sequences are passed the "-a" option to ensure they only work
25 on architecture dependent packages.
26
27 Each debhelper command will record when it's successfully run in
28 debian/package.debhelper.log. (Which dh_clean deletes.) So dh can tell
29 which commands have already been run, for which packages, and skip running
30 those commands again.
31
32 Each time dh is run, it examines the log, and finds the last logged command
33 that is in the specified sequence. It then continues with the next command
34 in the sequence. The B<--until>, B<--before>, B<--after>, and B<--remaining>
35 options can override this behavior.
36
37 =head1 OPTIONS
38
39 =over 4
40
41 =item B<--until> I<cmd>
42
43 Run commands in the sequence until and including I<cmd>, then stop.
44
45 =item B<--before> I<cmd>
46
47 Run commands in the sequence before I<cmd>, then stop.
48
49 =item B<--after> I<cmd>
50
51 Run commands in the sequence that come after I<cmd>.
52
53 =item B<--remaining>
54
55 Run all commands in the sequence that have yet to be run.
56
57 =back
58
59 All other options passed to dh are passed on to each command it runs. This
60 can be used to set an option like "-v" or "-X" or "-N", as well as for more
61 specialised options.
62
63 =head1 COMMAND SPECIFICATION
64
65 I<cmd> can be a full name of a debhelper command, or a substring. It'll first
66 search for a command in the sequence exactly matching the name, to avoid any
67 ambiguity. If there are multiple substring matches, the last one in the
68 sequence will be used.
69
70 =cut
71
72 sub command_pos {
73         my $command=shift;
74         my @sequence=@_;
75
76         foreach my $i (0..$#sequence) {
77                 if ($command eq $sequence[$i]) {
78                         return $i;
79                 }
80         }
81
82         my @matches;
83         foreach my $i (0..$#sequence) {
84                 if ($sequence[$i] =~ /\Q$command\E/) {
85                         push @matches, $i;
86                 }
87         }
88         if (! @matches) {
89                 error "command specification \"$command\" does not match any command in the sequence"
90         }
91         else {
92                 return pop @matches;
93         }
94 }
95
96 =head1 EXAMPLES
97
98 To see what commands are included in a sequence, without actually doing
99 anything:
100
101         dh binary-arch --no-act
102
103 This is a very simple rules file, for packages where the default seqences of
104 commands work with no additional options.
105
106         #!/usr/bin/make -f
107         %:
108                 dh %@
109
110 This is a simple rules file that is a good starting place for customisation.
111 (It's also available in F</usr/share/doc/debhelper/examples/rules.simple>
112
113         #!/usr/bin/make -f
114
115         build:
116                 dh build
117
118         clean:
119                 dh clean
120
121         install: build
122                 dh install
123
124         binary-arch: install
125                 dh binary-arch
126
127         binary-indep: install
128                 dh binary-indep
129
130         binary: binary-arch binary-indep
131
132 Often you'll want to pass an option to ./configure. This uses dh to run all
133 commands before L<dh_auto_configure(1)>, then runs that command by hand,
134 and then finished up by running the rest of the sequence. You could also
135 run ./configure by hand, instead of bothering with using dh_auto_configure.
136 And if necessary, you can add commands to run automake, etc here too.
137
138         build:
139                 dh build --before configure
140                 dh_auto_configure --kitchen-sink=yes
141                 dh build --after configure
142
143 Here's how to skip two automated in a row (configure and build), and
144 instead run the commands by hand.
145
146         build:
147                 dh build --before configure
148                 ./mondoconfig
149                 make universe-explode-in-delight
150                 dh build --after build
151
152 Another common case is wanting to run some code manually after a particular
153 debhelper command is run.
154
155         install: build
156                 dh install --until dh_fixperms
157                 # dh_fixperms has run, now override it for one program
158                 chmod 4755 debian/foo/usr/bin/foo
159                 # and continue
160                 dh install --after dh_fixperms
161
162 It's also fine to run debhelper commands before starting a dh sequence.
163 Just be sure to use the B<--remaining> option to ensure that commands
164 that normally come before those in the sequence are still run.
165
166         install:
167                 dh_installdocs README TODO
168                 dh_installchangelogs Changes
169                 dh install --remaining
170
171         binary-arch: install
172                 dh_strip -X foo
173                 dh binary-arch --remaining
174
175 =cut
176
177 # Stash this away before init modifies it.
178 my @ARGV_orig=@ARGV;
179
180 init();
181 inhibit_log();
182
183 # Definitions of sequences.
184 my %sequences;
185 $sequences{build} = [qw{
186         dh_testdir
187         dh_auto_configure
188         dh_auto_build
189         dh_auto_test
190 }],
191 $sequences{clean} = [qw{
192         dh_testdir
193         dh_auto_clean
194         dh_clean
195 }];
196 $sequences{install} = [@{$sequences{build}}, qw{
197         dh_testroot
198         dh_prep
199         dh_installdirs
200         dh_auto_install
201
202         dh_install
203         dh_installdocs
204         dh_installchangelogs
205         dh_installexamples
206         dh_installman
207
208         dh_installcatalogs
209         dh_installcron
210         dh_installdebconf
211         dh_installcatalogs
212         dh_installemacsen
213         dh_installifupdown
214         dh_installinfo
215         dh_installinit
216         dh_installmenu
217         dh_installmime
218         dh_installmodules
219         dh_installlogcheck
220         dh_installlogrotate
221         dh_installpam
222         dh_installppp
223         dh_installudev
224         dh_installwm
225         dh_installxfonts
226         dh_lintian
227         dh_desktop
228         dh_gconf
229         dh_icons
230         dh_perl
231         dh_pysupport
232         dh_scrollkeeper
233         dh_usrlocal
234
235         dh_link
236         dh_compress
237         dh_fixperms
238 }];
239 my @b=qw{
240         dh_installdeb
241         dh_gencontrol
242         dh_md5sums
243         dh_builddeb
244 };
245 $sequences{'binary-indep'} = [@{$sequences{install}}, @b];
246 $sequences{binary} = [@{$sequences{install}}, qw{
247         dh_strip
248         dh_makeshlibs
249         dh_shlibdeps
250 }, @b];
251 $sequences{'binary-arch'} = [@{$sequences{binary}}];
252
253 # Third-party commands can be listed in the sequences, but should be
254 # listed here as well. They will not be run if not present.
255 my %thirdparty=(
256         dh_pycompat => 1,
257         dh_pysupport => 1,
258 );
259
260 # Get the sequence of commands to run.
261 if (! @ARGV) {
262         error "specify a sequence to run";
263 }
264 my $sequence=shift;
265 if (! exists $sequences{$sequence}) {
266         error "Unknown sequence $sequence (chose from: ".
267                 join(" ", sort keys %sequences).")";
268 }
269 my @sequence=@{$sequences{$sequence}};
270
271 # Get the options to pass to commands in the sequence.
272 # Filter out options intended only for this program.
273 my @options;
274 if ($sequence eq 'binary-arch') {
275         push @options, "-a";
276 }
277 elsif ($sequence eq 'binary-indep') {
278         push @options, "-i";
279 }
280 while (@ARGV_orig) {
281         my $opt=shift @ARGV_orig;
282         next if $opt eq $sequence;
283         if ($opt =~ /^--?(after|until|before)$/) {
284                 shift @ARGV_orig;
285                 next;
286         }
287         elsif ($opt =~ /^--?(no-act|remaining|(after|until|before)=)/) {
288                 next;
289         }
290         push @options, $opt;
291 }
292
293 # Figure out at what point in the sequence to start for each package.
294 my %logged;
295 my %startpoint;
296 foreach my $package (@{$dh{DOPACKAGES}}) {
297         my @log=loadlog($package);
298         if ($dh{AFTER}) {
299                 # Run commands in the sequence that come after the
300                 # specified command.
301                 $startpoint{$package}=command_pos($dh{AFTER}, @sequence) + 1;
302                 # Write a dummy log entry indicating that the specified
303                 # command was, in fact, run. This handles the case where
304                 # no commands remain to run after it, communicating to
305                 # future dh instances that the specified command should not
306                 # be run again.
307                 writelog($package, $sequence[$startpoint{$package}-1]);
308         }
309         elsif ($dh{REMAINING}) {
310                 # Start at the beginning so all remaining commands will get
311                 # run.
312                 $startpoint{$package}=0;
313         }
314         else {
315                 # Find the last logged command that is in the sequence, and
316                 # continue with the next command after it. If no logged
317                 # command is in the sequence, we're starting at the beginning..                         
318                 $startpoint{$package}=0;
319 COMMAND:        foreach my $command (reverse @log) {
320                         foreach my $i (0..$#sequence) {
321                                 if ($command eq $sequence[$i]) {
322                                         $startpoint{$package}=$i+1;
323                                         last COMMAND;
324                                 }
325                         }
326                 }
327         }
328 }
329
330 # Figure out what point in the sequence to go to.
331 my $stoppoint=$#sequence;
332 if ($dh{UNTIL}) {
333         $stoppoint=command_pos($dh{UNTIL}, @sequence);
334 }
335 elsif ($dh{BEFORE}) {
336         $stoppoint=command_pos($dh{BEFORE}, @sequence) - 1;
337 }
338
339 # Now run the commands in the sequence.
340 foreach my $i (0..$stoppoint) {
341         # Figure out which packages need to run this command.
342         my @exclude;
343         foreach my $package (@{$dh{DOPACKAGES}}) {
344                 if ($startpoint{$package} > $i ||
345                     $logged{$package}{$sequence[$i]}) {
346                         push @exclude, $package;
347                 }
348         }
349         
350         if (@exclude eq @{$dh{DOPACKAGES}}) {
351                 # Command already done for all packages.
352                 next;
353         }
354         elsif (! @exclude) {
355                 # Run command for all packages.
356                 run($sequence[$i], @options);
357         }
358         else {
359                 # Run command for only a subset of packages.
360                 run($sequence[$i], @options,
361                         map { "-N$_" } @exclude);
362         }
363 }
364
365 sub run {
366         my $command=shift;
367         my @options=@_;
368         
369         # If a third party command is not in /usr/bin, don't try to run it.
370         if ($thirdparty{$command} && ! -x "/usr/bin/$command") {
371                 return;
372         }
373
374         # 3 space indent lines the command being run up under the 
375         # sequence name after "dh ".
376         print "   ".escape_shell($command, @options)."\n";
377
378         if (! $dh{NO_ACT}) {
379                 my $ret=system($command, @options);
380                 if ($ret >> 8 != 0) {
381                         exit $ret >> 8;
382                 }
383                 elsif ($ret) {
384                         exit 1;
385                 }
386         }
387 }
388
389 sub loadlog {
390         my $package=shift;
391         my $ext=pkgext($package);
392         
393         my @log;
394         open(LOG, "<", "debian/${ext}debhelper.log") || return;
395         while (<LOG>) {
396                 chomp;
397                 push @log, $_;
398                 $logged{$package}{$_}=1;
399         }
400         close LOG;
401         return @log;
402 }
403                 
404 sub writelog {
405         my $package=shift;
406         my $cmd=shift;
407         my $ext=pkgext($package);
408         
409         open(LOG, ">>", "debian/${ext}debhelper.log") || error("failed to write to log");
410         print LOG $cmd."\n";
411         close LOG;
412 }
413
414 =head1 SEE ALSO
415
416 L<debhelper(7)>
417
418 This program is a part of debhelper.
419
420 =head1 AUTHOR
421
422 Joey Hess <joeyh@debian.org>
423
424 =cut