]> git.donarmstrong.com Git - debhelper.git/blob - dh
wording
[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>] [B<--with> I<addon>] [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 =item B<--with> I<addon>
58
59 Add the debhelper commands specified by the given addon to appropriate places
60 in the sequence of commands that is run. This option can be repeated more
61 than once, and is used when there is a third-party package that provides
62 debhelper commands. See "SEQUENCE ADDONS" below for documentation about what
63 such packages should do to be supported by --with.
64
65 =back
66
67 All other options passed to dh are passed on to each command it runs. This
68 can be used to set an option like "-v" or "-X" or "-N", as well as for more
69 specialised options.
70
71 =head1 COMMAND SPECIFICATION
72
73 I<cmd> can be a full name of a debhelper command, or a substring. It'll first
74 search for a command in the sequence exactly matching the name, to avoid any
75 ambiguity. If there are multiple substring matches, the last one in the
76 sequence will be used.
77
78 =head1 SEQUENCE ADDONS
79
80 When B<--with> I<addon> is used, dh loads the perl module
81 Debian::Debhelper::Sequence::I<addon>. Two functions are provided to let
82 the module add its commands to sequences:
83
84 =over 4
85
86 =item Debian::Debhelper::Dh_Lib::insert_before(existing_command, new_command)
87
88 Insert I<new_command> in sequences before I<existing_command>.
89
90 =item Debian::Debhelper::Dh_Lib::insert_after(existing_command, new_command)
91
92 Insert I<new_command> in sequences after I<existing_command>.
93
94 =item Debian::Debhelper::Dh_Lib::remove_command(existing_command)
95
96 Remove I<existing_command> from the list of commands to run.
97
98 =back
99
100 =cut
101
102 sub command_pos {
103         my $command=shift;
104         my @sequence=@_;
105
106         foreach my $i (0..$#sequence) {
107                 if ($command eq $sequence[$i]) {
108                         return $i;
109                 }
110         }
111
112         my @matches;
113         foreach my $i (0..$#sequence) {
114                 if ($sequence[$i] =~ /\Q$command\E/) {
115                         push @matches, $i;
116                 }
117         }
118         if (! @matches) {
119                 error "command specification \"$command\" does not match any command in the sequence"
120         }
121         else {
122                 return pop @matches;
123         }
124 }
125
126 =head1 EXAMPLES
127
128 To see what commands are included in a sequence, without actually doing
129 anything:
130
131         dh binary-arch --no-act
132
133 This is a very simple rules file, for packages where the default sequences of
134 commands work with no additional options.
135
136         #!/usr/bin/make -f
137         %:
138                 dh $@
139
140 This is a simple rules file that is a good starting place for customisation.
141 (It's also available in F</usr/share/doc/debhelper/examples/rules.simple>
142
143         #!/usr/bin/make -f
144
145         build: build-stamp
146         build-stamp:
147                 dh build
148                 touch build-stamp
149
150         clean:
151                 dh clean
152
153         install: build install-stamp
154         install-stamp:
155                 dh install
156                 touch install-stamp
157
158         binary-arch: install
159                 dh binary-arch
160
161         binary-indep: install
162                 dh binary-indep
163
164         binary: binary-arch binary-indep
165
166 Often you'll want to pass an option to ./configure. This uses dh to run all
167 commands before L<dh_auto_configure(1)>, then runs that command by hand,
168 and then finishes up by running the rest of the sequence. You could also
169 run ./configure by hand, instead of bothering with using dh_auto_configure.
170 And if necessary, you can add commands to run automake, etc here too.
171
172         build: build-stamp
173         build-stamp:
174                 dh build --before configure
175                 dh_auto_configure -- --kitchen-sink=yes
176                 dh build --after configure
177                 touch build-stamp
178
179 Here's how to skip two automated steps in a row (configure and build), and
180 instead run the commands by hand.
181
182         build: build-stamp
183         build-stamp:
184                 dh build --before configure
185                 ./mondoconfig
186                 make universe-explode-in-delight
187                 dh build --after build
188                 touch build-stamp
189
190 Another common case is wanting to run some code manually after a particular
191 debhelper command is run.
192
193         install: build install-stamp
194         install-stamp:
195                 dh install --until dh_fixperms
196                 # dh_fixperms has run, now override it for one program
197                 chmod 4755 debian/foo/usr/bin/foo
198                 # and continue
199                 dh install --after dh_fixperms
200                 touch install-stamp
201
202 It's also fine to run debhelper commands early. Just make sure that at
203 least dh_prep is run from the sequence first, and be sure to use the
204 B<--remaining> option to ensure that commands that normally come before
205 those in the sequence are still run.
206
207         install: build install-stamp
208         install-stamp:
209                 dh install --until dh_prep
210                 dh_installdocs README TODO
211                 dh_installchangelogs Changes
212                 dh install --remaining
213                 touch install-stamp
214
215         binary-arch: install
216                 dh_strip -X foo
217                 dh binary-arch --remaining
218
219 =cut
220
221 # Stash this away before init modifies it.
222 my @ARGV_orig=@ARGV;
223
224 init(options => {
225         "until=s" => \$dh{UNTIL},
226         "after=s" => \$dh{AFTER},
227         "before=s" => \$dh{BEFORE},
228         "remaining" => \$dh{REMAINING},
229         "with=s" => sub {
230                 my ($option,$value)=@_;
231                 push @{$dh{WITH}},$value;
232         },
233 });
234 inhibit_log();
235
236 # Definitions of sequences.
237 my %sequences;
238 $sequences{build} = [qw{
239         dh_testdir
240         dh_auto_configure
241         dh_auto_build
242         dh_auto_test
243 }],
244 $sequences{clean} = [qw{
245         dh_testdir
246         dh_auto_clean
247         dh_clean
248 }];
249 $sequences{install} = [@{$sequences{build}}, qw{
250         dh_testroot
251         dh_prep
252         dh_installdirs
253         dh_auto_install
254
255         dh_install
256         dh_installdocs
257         dh_installchangelogs
258         dh_installexamples
259         dh_installman
260
261         dh_installcatalogs
262         dh_installcron
263         dh_installdebconf
264         dh_installcatalogs
265         dh_installemacsen
266         dh_installifupdown
267         dh_installinfo
268         dh_installinit
269         dh_installmenu
270         dh_installmime
271         dh_installmodules
272         dh_installlogcheck
273         dh_installlogrotate
274         dh_installpam
275         dh_installppp
276         dh_installudev
277         dh_installwm
278         dh_installxfonts
279         dh_lintian
280         dh_desktop
281         dh_gconf
282         dh_icons
283         dh_perl
284         dh_scrollkeeper
285         dh_usrlocal
286
287         dh_link
288         dh_compress
289         dh_fixperms
290 }];
291 my @b=qw{
292         dh_installdeb
293         dh_gencontrol
294         dh_md5sums
295         dh_builddeb
296 };
297 $sequences{'binary-indep'} = [@{$sequences{install}}, @b];
298 $sequences{binary} = [@{$sequences{install}}, qw{
299         dh_strip
300         dh_makeshlibs
301         dh_shlibdeps
302 }, @b];
303 $sequences{'binary-arch'} = [@{$sequences{binary}}];
304
305 # --with python-support is enabled by default, at least for now
306 unshift @{$dh{WITH}}, "python-support";
307
308 # sequence addon interface
309 sub _insert {
310         my $offset=shift;
311         my $existing=shift;
312         my $new=shift;
313         foreach my $sequence (keys %sequences) {
314                 my @list=@{$sequences{$sequence}};
315                 next unless grep $existing, @list;
316                 my @new;
317                 foreach my $command (@list) {
318                         if ($command eq $existing) {
319                                 push @new, $new if $offset < 0;
320                                 push @new, $command;
321                                 push @new, $new if $offset > 0;
322                         }
323                         else {
324                                 push @new, $command;
325                         }
326                 }
327                 $sequences{$sequence}=\@new;
328         }
329 }
330 sub insert_before {
331         _insert(-1, @_);
332 }
333 sub insert_after {
334         _insert(1, @_);
335 }
336 sub remove_command {
337         my $command=shift;
338         foreach my $sequence (keys %sequences) {
339                 $sequences{$sequence}=[grep { $_ ne $command } @{$sequences{$sequence}}];
340         }
341         
342 }
343 foreach my $addon (@{$dh{WITH}}) {
344         my $mod="Debian::Debhelper::Sequence::$addon";
345         $mod=~s/-/_/g;
346         eval "use $mod";
347         if ($@) {
348                 error("--with $addon not supported or failed to load module $mod");
349         }
350 }
351
352 # Get the sequence of commands to run.
353 if (! @ARGV) {
354         error "specify a sequence to run";
355 }
356 my $sequence=shift;
357 if (! exists $sequences{$sequence}) {
358         error "Unknown sequence $sequence (choose from: ".
359                 join(" ", sort keys %sequences).")";
360 }
361 my @sequence=@{$sequences{$sequence}};
362
363 # The list of all packages that can be acted on.
364 my @packages=@{$dh{DOPACKAGES}};
365
366 # Get the options to pass to commands in the sequence.
367 # Filter out options intended only for this program.
368 my @options;
369 if ($sequence eq 'binary-arch') {
370         push @options, "-a";
371         # as an optimisation, remove from the list any packages
372         # that are not arch dependent
373         my %arch_packages = map { $_ => 1 } getpackages("arch");
374         @packages = grep { $arch_packages{$_} } @packages;
375 }
376 elsif ($sequence eq 'binary-indep') {
377         push @options, "-i";
378         # ditto optimisation for arch indep
379         my %indep_packages = map { $_ => 1 } getpackages("indep");
380         @packages = grep { $indep_packages{$_} } @packages;
381 }
382 while (@ARGV_orig) {
383         my $opt=shift @ARGV_orig;
384         next if $opt eq $sequence;
385         if ($opt =~ /^--?(after|until|before|with)$/) {
386                 shift @ARGV_orig;
387                 next;
388         }
389         elsif ($opt =~ /^--?(no-act|remaining|(after|until|before|with)=)/) {
390                 next;
391         }
392         push @options, $opt;
393 }
394
395 # Figure out at what point in the sequence to start for each package.
396 my %logged;
397 my %startpoint;
398 foreach my $package (@packages) {
399         my @log=loadlog($package);
400         if ($dh{AFTER}) {
401                 # Run commands in the sequence that come after the
402                 # specified command.
403                 $startpoint{$package}=command_pos($dh{AFTER}, @sequence) + 1;
404                 # Write a dummy log entry indicating that the specified
405                 # command was, in fact, run. This handles the case where
406                 # no commands remain to run after it, communicating to
407                 # future dh instances that the specified command should not
408                 # be run again.
409                 writelog($package, $sequence[$startpoint{$package}-1]);
410         }
411         elsif ($dh{REMAINING}) {
412                 # Start at the beginning so all remaining commands will get
413                 # run.
414                 $startpoint{$package}=0;
415         }
416         else {
417                 # Find the last logged command that is in the sequence, and
418                 # continue with the next command after it. If no logged
419                 # command is in the sequence, we're starting at the beginning..                         
420                 $startpoint{$package}=0;
421 COMMAND:        foreach my $command (reverse @log) {
422                         foreach my $i (0..$#sequence) {
423                                 if ($command eq $sequence[$i]) {
424                                         $startpoint{$package}=$i+1;
425                                         last COMMAND;
426                                 }
427                         }
428                 }
429         }
430 }
431
432 # Figure out what point in the sequence to go to.
433 my $stoppoint=$#sequence;
434 if ($dh{UNTIL}) {
435         $stoppoint=command_pos($dh{UNTIL}, @sequence);
436 }
437 elsif ($dh{BEFORE}) {
438         $stoppoint=command_pos($dh{BEFORE}, @sequence) - 1;
439 }
440
441 # Now run the commands in the sequence.
442 foreach my $i (0..$stoppoint) {
443         # Figure out which packages need to run this command.
444         my @exclude;
445         foreach my $package (@packages) {
446                 if ($startpoint{$package} > $i ||
447                     $logged{$package}{$sequence[$i]}) {
448                         push @exclude, $package;
449                 }
450         }
451         
452         if (@exclude eq @packages) {
453                 # Command already done for all packages.
454                 next;
455         }
456         elsif (! @exclude) {
457                 # Run command for all packages.
458                 run($sequence[$i], @options);
459         }
460         else {
461                 # Run command for only a subset of packages.
462                 run($sequence[$i], @options,
463                         map { "-N$_" } @exclude);
464         }
465 }
466
467 sub run {
468         my $command=shift;
469         my @options=@_;
470         
471         # 3 space indent lines the command being run up under the 
472         # sequence name after "dh ".
473         print "   ".escape_shell($command, @options)."\n";
474
475         if (! $dh{NO_ACT}) {
476                 my $ret=system($command, @options);
477                 if ($ret >> 8 != 0) {
478                         exit $ret >> 8;
479                 }
480                 elsif ($ret) {
481                         exit 1;
482                 }
483         }
484 }
485
486 sub loadlog {
487         my $package=shift;
488         my $ext=pkgext($package);
489         
490         my @log;
491         open(LOG, "<", "debian/${ext}debhelper.log") || return;
492         while (<LOG>) {
493                 chomp;
494                 push @log, $_;
495                 $logged{$package}{$_}=1;
496         }
497         close LOG;
498         return @log;
499 }
500                 
501 sub writelog {
502         my $package=shift;
503         my $cmd=shift;
504         my $ext=pkgext($package);
505         
506         open(LOG, ">>", "debian/${ext}debhelper.log") || error("failed to write to log");
507         print LOG $cmd."\n";
508         close LOG;
509 }
510
511 =head1 SEE ALSO
512
513 L<debhelper(7)>
514
515 This program is a part of debhelper.
516
517 =head1 AUTHOR
518
519 Joey Hess <joeyh@debian.org>
520
521 =cut