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