chiark / gitweb /
devscripts (2.10.69+squeeze4) stable-security; urgency=high
[devscripts.git] / scripts / debi.pl
1 #! /usr/bin/perl -w
2
3 # debi:  Install current version of deb package
4 # debc:  List contents of current version of deb package
5 #
6 # debi and debc originally by Christoph Lameter <clameter@debian.org>
7 # Copyright Christoph Lameter <clameter@debian.org>
8 # The now defunct debit originally by Jim Van Zandt <jrv@vanzandt.mv.com>
9 # Copyright 1999 Jim Van Zandt <jrv@vanzandt.mv.com>
10 # Modifications by Julian Gilbey <jdg@debian.org>, 1999-2003
11 # Copyright 1999-2003, Julian Gilbey <jdg@debian.org>
12 #
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26 use 5.008;
27 use strict;
28 use Getopt::Long;
29 use File::Basename;
30 use filetest 'access';
31 use Cwd;
32 use Dpkg::Control;
33 use Dpkg::Changelog::Parse;
34
35 my $progname = basename($0,'.pl');  # the '.pl' is for when we're debugging
36 my $modified_conf_msg;
37
38 sub usage_i {
39     print <<"EOF";
40 Usage: $progname [options] [.changes file] [package ...]
41   Install the .deb file(s) just created, as listed in the generated
42   .changes file or the .changes file specified.  If packages are listed,
43   only install those specified packages from the .changes file.
44   Options:
45     --no-conf or      Don\'t read devscripts config files;
46       --noconf          must be the first option given
47     -a<arch>          Search for .changes file made for Debian build <arch>
48     -t<target>        Search for .changes file made for GNU <target> arch
49     --debs-dir DIR    Look for the changes and debs files in DIR instead of
50                       the parent of the current package directory
51     --multi           Search for multiarch .changes file made by dpkg-cross
52     --upgrade         Only upgrade packages; don't install new ones.
53     --check-dirname-level N
54                       How much to check directory names:
55                       N=0   never
56                       N=1   only if program changes directory (default)
57                       N=2   always
58     --check-dirname-regex REGEX
59                       What constitutes a matching directory name; REGEX is
60                       a Perl regular expression; the string \`PACKAGE\' will
61                       be replaced by the package name; see manpage for details
62                       (default: 'PACKAGE(-.+)?')
63     --with-depends    Install packages with their depends.
64     --tool TOOL       Use the specified tool for installing the dependencies
65                       of the package(s) to be installed.
66                       (default: apt-get)
67     --help            Show this message
68     --version         Show version and copyright information
69
70 Default settings modified by devscripts configuration files:
71 $modified_conf_msg
72 EOF
73 }
74
75 sub usage_c {
76     print <<"EOF";
77 Usage: $progname [options] [.changes file] [package ...]
78   Display the contents of the .deb or .udeb file(s) just created, as listed
79   in the generated .changes file or the .changes file specified.
80   If packages are listed, only display those specified packages
81   from the .changes file.  Options:
82     --no-conf or      Don\'t read devscripts config files;
83       --noconf          must be the first option given
84     -a<arch>          Search for changes file made for Debian build <arch>
85     -t<target>        Search for changes file made for GNU <target> arch
86     --debs-dir DIR    Look for the changes and debs files in DIR instead of
87                       the parent of the current package directory
88     --multi           Search for multiarch .changes file made by dpkg-cross
89     --check-dirname-level N
90                       How much to check directory names:
91                       N=0   never
92                       N=1   only if program changes directory (default)
93                       N=2   always
94     --check-dirname-regex REGEX
95                       What constitutes a matching directory name; REGEX is
96                       a Perl regular expression; the string \`PACKAGE\' will
97                       be replaced by the package name; see manpage for details
98                       (default: 'PACKAGE(-.+)?')
99     --help            Show this message
100     --version         Show version and copyright information
101
102 Default settings modified by devscripts configuration files:
103 $modified_conf_msg
104 EOF
105 }
106
107 if ($progname eq 'debi') { *usage = \&usage_i; }
108 elsif ($progname eq 'debc') { *usage = \&usage_c; }
109 else { die "Unrecognised invocation name: $progname\n"; }
110
111 my $version = <<"EOF";
112 This is $progname, from the Debian devscripts package, version ###VERSION###
113 This code is copyright 1999-2003, Julian Gilbey <jdg\@debian.org>,
114 all rights reserved.
115 Based on original code by Christoph Lameter and James R. Van Zandt.
116 This program comes with ABSOLUTELY NO WARRANTY.
117 You are free to redistribute this code under the terms of
118 the GNU General Public License, version 2 or later.
119 EOF
120
121 # Start by setting default values
122 my $debsdir = '..';
123 my $debsdir_warning;
124 my $check_dirname_level = 1;
125 my $check_dirname_regex = 'PACKAGE(-.+)?';
126 my $install_tool = 'apt-get';
127
128 # Next, read configuration files and then command line
129 # The next stuff is boilerplate
130
131 if (@ARGV and $ARGV[0] =~ /^--no-?conf$/) {
132     $modified_conf_msg = "  (no configuration files read)";
133     shift;
134 } else {
135     my @config_files = ('/etc/devscripts.conf', '~/.devscripts');
136     my %config_vars = (
137                        'DEBRELEASE_DEBS_DIR' => '..',
138                        'DEVSCRIPTS_CHECK_DIRNAME_LEVEL' => 1,
139                        'DEVSCRIPTS_CHECK_DIRNAME_REGEX' => 'PACKAGE(-.+)?',
140                        );
141     my %config_default = %config_vars;
142
143     my $shell_cmd;
144     # Set defaults
145     foreach my $var (keys %config_vars) {
146         $shell_cmd .= qq[$var="$config_vars{$var}";\n];
147     }
148     $shell_cmd .= 'for file in ' . join(" ",@config_files) . "; do\n";
149     $shell_cmd .= '[ -f $file ] && . $file; done;' . "\n";
150     # Read back values
151     foreach my $var (keys %config_vars) { $shell_cmd .= "echo \$$var;\n" }
152     my $shell_out = `/bin/bash -c '$shell_cmd'`;
153     @config_vars{keys %config_vars} = split /\n/, $shell_out, -1;
154
155     # Check validity
156     $config_vars{'DEVSCRIPTS_CHECK_DIRNAME_LEVEL'} =~ /^[012]$/
157         or $config_vars{'DEVSCRIPTS_CHECK_DIRNAME_LEVEL'}=1;
158     # We do not replace this with a default directory to avoid accidentally
159     # installing a broken package
160     $config_vars{'DEBRELEASE_DEBS_DIR'} =~ s%/+%/%;
161     $config_vars{'DEBRELEASE_DEBS_DIR'} =~ s%(.)/$%$1%;
162     if (! -d $config_vars{'DEBRELEASE_DEBS_DIR'}) {
163         $debsdir_warning = "config file specified DEBRELEASE_DEBS_DIR directory $config_vars{'DEBRELEASE_DEBS_DIR'} does not exist!";
164     }
165
166     foreach my $var (sort keys %config_vars) {
167         if ($config_vars{$var} ne $config_default{$var}) {
168             $modified_conf_msg .= "  $var=$config_vars{$var}\n";
169         }
170     }
171     $modified_conf_msg ||= "  (none)\n";
172     chomp $modified_conf_msg;
173
174     $debsdir = $config_vars{'DEBRELEASE_DEBS_DIR'};
175     $check_dirname_level = $config_vars{'DEVSCRIPTS_CHECK_DIRNAME_LEVEL'};
176     $check_dirname_regex = $config_vars{'DEVSCRIPTS_CHECK_DIRNAME_REGEX'};
177 }
178
179 # Command line options next
180 my ($opt_help, $opt_version, $opt_a, $opt_t, $opt_debsdir, $opt_multi);
181 my $opt_upgrade;
182 my ($opt_level, $opt_regex, $opt_noconf);
183 my ($opt_tool, $opt_with_depends);
184 GetOptions("help" => \$opt_help,
185            "version" => \$opt_version,
186            "a=s" => \$opt_a,
187            "t=s" => \$opt_t,
188            "debs-dir=s" => \$opt_debsdir,
189            "multi" => \$opt_multi,
190            "upgrade" => \$opt_upgrade,
191            "check-dirname-level=s" => \$opt_level,
192            "check-dirname-regex=s" => \$opt_regex,
193            "with-depends" => \$opt_with_depends,
194            "tool=s" => \$opt_tool,
195            "noconf" => \$opt_noconf,
196            "no-conf" => \$opt_noconf,
197            )
198     or die "Usage: $progname [options] [.changes file] [package ...]\nRun $progname --help for more details\n";
199
200 if ($opt_help) { usage(); exit 0; }
201 if ($opt_version) { print $version; exit 0; }
202 if ($opt_noconf) {
203     die "$progname: --no-conf is only acceptable as the first command-line option!\n";
204 }
205
206 my ($targetarch, $targetgnusystem);
207 $targetarch = $opt_a ? "-a$opt_a" : "";
208 $targetgnusystem = $opt_t ? "-t$opt_t" : "";
209
210 if ($opt_debsdir) {
211     $opt_debsdir =~ s%/+%/%;
212     $opt_debsdir =~ s%(.)/$%$1%;
213     if (! -d $opt_debsdir) {
214         $debsdir_warning = "--debs-dir directory $opt_debsdir does not exist!";
215     }
216     $debsdir = $opt_debsdir;
217 }
218
219 if ($debsdir_warning) {
220     die "$progname: $debsdir_warning\n";
221 }
222
223 if (defined $opt_level) {
224     if ($opt_level =~ /^[012]$/) { $check_dirname_level = $opt_level; }
225     else {
226         die "$progname: unrecognised --check-dirname-level value (allowed are 0,1,2)\n";
227     }
228 }
229
230 if (defined $opt_regex) { $check_dirname_regex = $opt_regex; }
231
232 if ($opt_tool) {
233     $install_tool = $opt_tool;
234 }
235
236 # Is a .changes file listed on the command line?
237 my ($changes, $mchanges, $arch);
238 if (@ARGV and $ARGV[0] =~ /\.changes$/) {
239     $changes = shift;
240 }
241
242 # Need to determine $arch in any event
243 $arch = `dpkg-architecture $targetarch $targetgnusystem -qDEB_HOST_ARCH`;
244 if ($? != 0 or ! $arch) {
245     die "$progname: unable to determine target architecture.\n";
246 }
247 chomp $arch;
248
249 my $chdir = 0;
250
251 if (! defined $changes) {
252     # Look for .changes file via debian/changelog
253     until (-r 'debian/changelog') {
254         $chdir = 1;
255         chdir '..' or die "$progname: can't chdir ..: $!\n";
256         if (cwd() eq '/') {
257             die "$progname: cannot find readable debian/changelog anywhere!\nAre you in the source code tree?\n";
258         }
259     }
260
261     if (-e ".svn/deb-layout") {
262         # Cope with format of svn-buildpackage tree
263         my $fh;
264         open($fh, "<", ".svn/deb-layout") || die "Can't open .svn/deb-layout: $!\n";
265         my($build_area) = grep /^buildArea=/, <$fh>;
266         close($fh);
267         if (defined($build_area) and not $opt_debsdir) {
268             chomp($build_area);
269             $build_area =~ s/^buildArea=//;
270             $debsdir = $build_area if -d $build_area;
271         }
272     }
273
274     # Find the source package name and version number
275     my $changelog = changelog_parse();
276
277     die "$progname: no package name in changelog!\n"
278         unless exists $changelog->{'Source'};
279     die "$progname: no package version in changelog!\n"
280         unless exists $changelog->{'Version'};
281
282     # Is the directory name acceptable?
283     if ($check_dirname_level ==  2 or
284             ($check_dirname_level == 1 and $chdir)) {
285         my $re = $check_dirname_regex;
286         $re =~ s/PACKAGE/\\Q$changelog->{'Source'}\\E/g;
287         my $gooddir;
288         if ($re =~ m%/%) { $gooddir = eval "cwd() =~ /^$re\$/;"; }
289         else { $gooddir = eval "basename(cwd()) =~ /^$re\$/;"; }
290
291         if (! $gooddir) {
292             my $pwd = cwd();
293             die <<"EOF";
294 $progname: found debian/changelog for package $changelog->{'Source'} in the directory
295   $pwd
296 but this directory name does not match the package name according to the
297 regex  $check_dirname_regex.
298
299 To run $progname on this package, see the --check-dirname-level and
300 --check-dirname-regex options; run $progname --help for more info.
301 EOF
302         }
303     }
304
305     my $sversion = $changelog->{'Version'};
306     $sversion =~ s/^\d+://;
307     my $package = $changelog->{'Source'};
308     my $pva="${package}_${sversion}_${arch}";
309     $changes="$debsdir/$pva.changes";
310
311     if (! -e $changes and -d ".svn" and -d "../build-area") {
312         # Try out default svn-buildpackage structure in case
313         # we were going to fail anyway...
314         $changes = "../build-area/$pva.changes";
315     }
316
317     if ($opt_multi) {
318         my @mchanges = glob("$debsdir/${package}_${sversion}_*+*.changes");
319         @mchanges = grep { /[_+]$arch[\.+]/ } @mchanges;
320         $mchanges = $mchanges[0] || '';
321         $mchanges ||= "$debsdir/${package}_${sversion}_multi.changes"
322             if -f "$debsdir/${package}_${sversion}_multi.changes";
323     }
324 }
325
326 chdir dirname($changes)
327     or die "$progname: can't chdir to $changes directory: $!\n";
328 $changes = basename($changes);
329 $mchanges = basename($mchanges) if $opt_multi;
330
331 if (! -r $changes or $opt_multi and $mchanges and ! -r $mchanges) {
332     die "$progname: can't read $changes" .
333         (($opt_multi and $mchanges) ? " or $mchanges" : "") . "!\n";
334 }
335
336 if (! -r $changes and $opt_multi) {
337     $changes = $mchanges;
338 } else {
339     $opt_multi = 0;
340 }
341 # $opt_multi now tells us whether we're actually using a multi-arch .changes
342 # file
343
344 my @debs = ();
345 my %pkgs = map { $_ => 0 } @ARGV;
346 my $ctrl = Dpkg::Control->new(name => $changes, type => CTRL_FILE_CHANGES);
347 $ctrl->load($changes);
348 for (split(/\n/, $ctrl->{Files})) {
349     # udebs are only supported for debc
350     if ((($progname eq 'debi') && (/ (\S*\.deb)$/)) ||
351         (($progname eq 'debc') && (/ (\S*\.u?deb)$/))) {
352         my $deb = $1;
353         $deb =~ /^([a-z0-9+\.-]+)_/ or warn "unrecognised .deb name: $deb\n";
354         # don't want other archs' .debs:
355         next unless $deb =~ /[_+]($arch|all)[\.+]/;
356         my $pkg = $deb;
357         $pkg =~ s/_.*$//;
358
359         if (@ARGV) {
360             if (exists $pkgs{$pkg}) {
361                 push @debs, $deb;
362                 $pkgs{$pkg}++;
363             } elsif (exists $pkgs{$deb}) {
364                 push @debs, $deb;
365                 $pkgs{$deb}++;
366             }
367         } else {
368             push @debs, $deb;
369         }
370     }
371 }
372
373 if (! @debs) {
374     die "$progname: no appropriate .debs found in the changes file $changes!\n";
375 }
376
377 if ($progname eq 'debi') {
378     my @upgrade = $opt_upgrade ? ('-O') : ();
379     if ($opt_with_depends) {
380         system('debpkg', @upgrade, '--unpack', @debs) == 0
381             or die "$progname: debpkg --unpack failed \n";
382         system($install_tool, '-f', 'install') == 0
383             or die "$progname: " . $install_tool . ' -f install failed\n';
384     } else {
385         system('debpkg', @upgrade, '-i', @debs) == 0
386             or die "$progname: debpkg -i failed\n";
387     }
388 } else {
389     # $progname eq 'debc'
390     foreach my $deb (@debs) {
391         print "$deb\n";
392         print '-' x length($deb), "\n";
393         system('dpkg-deb', '-I', $deb) == 0
394             or die "$progname: dpkg-deb -I $deb failed\n";
395         system('dpkg-deb', '-c', $deb) == 0
396             or die "$progname: dpkg-deb -c $deb failed\n";
397         print "\n";
398     }
399 }
400
401 # Now do a sanity check
402 if (@ARGV) {
403     foreach my $pkg (keys %pkgs) {
404         if ($pkgs{$pkg} == 0) {
405             warn "$progname: package $pkg not found in $changes, ignoring\n";
406         } elsif ($pkgs{$pkg} > 1) {
407             warn "$progname: package $pkg found more than once in $changes, installing all\n";
408         }
409     }
410 }
411
412 exit 0;