source: trunk/debbuild@ 54

Last change on this file since 54 was 54, checked in by kdeugau, 18 years ago

/trunk

Add --rebuild support for our .sdebs.
Tweak .sdeb filename output for consistency between -ba and -bs

  • Property svn:executable set to *
  • Property svn:keywords set to Date Rev Author
File size: 35.3 KB
Line 
1#!/usr/bin/perl
2# debbuild script
3# Shamlessly steals intreface from rpm's "rpmbuild" to create
4# Debian packages. Please note that such packages are highly
5# unlikely to conform to "Debian Policy".
6###
7# SVN revision info
8# $Date: 2006-04-19 18:00:46 +0000 (Wed, 19 Apr 2006) $
9# SVN revision $Rev: 54 $
10# Last update by $Author: kdeugau $
11###
12
13use strict;
14use warnings;
15use Fcntl; # for sysopen flags
16use Cwd 'abs_path'; # for finding where files really are
17
18# regex debugger
19#use re "debug";
20
21# Program flow:
22# -> Parse/execute "system" config/macros (if any - should be rare)
23# -> Parse/execute "user" config/macros (if any - *my* requirement is %_topdir)
24# -> Parse command line for options, spec file/tarball/.src.deb (NB - also accept .src.rpm)
25
26sub expandmacros;
27
28# User's prefs for dirs, environment, etc,etc,etc.
29# config file ~/.debmacros
30# Default ordered search paths for config/macros:
31# /usr/lib/rpm/rpmrc /usr/lib/rpm/redhat/rpmrc /etc/rpmrc ~/.rpmrc
32# /usr/lib/rpm/macros /usr/lib/rpm/redhat/macros /etc/rpm/macros ~/.rpmmacros
33# **NOTE: May be possible to (ab)use bits of debhelper
34
35# Build tree
36# default is /usr/src/debian/{BUILD,SOURCES,SPECS,DEBS,SDEBS}
37
38# Globals
39my $specfile;
40my $tarball;
41my $srcpkg;
42my $cmdbuildroot;
43my $tarballdir; # This should really be initialized, but the coding makes it, um, ugly.
44my %specglobals; # For %define's in specfile, among other things.
45
46# Initialized globals
47my $verbosity = 0;
48my %cmdopts = (type => '',
49 stage => 'a',
50 short => 'n'
51 );
52my $topdir = "/usr/src/debian";
53my $buildroot = "%{_tmppath}/%{name}-%{version}-%{release}.root".int(rand(99998)+1);
54
55# "Constants"
56my %targets = ('p' => 'Prep',
57 'c' => 'Compile',
58 'i' => 'Install',
59 'l' => 'Verify %files',
60 'a' => 'Build binary and source',
61 'b' => 'Build binary',
62 's' => 'Build source'
63 );
64my $scriptletbase =
65q(#!/bin/sh
66
67 RPM_SOURCE_DIR="%{_topdir}/SOURCES"
68 RPM_BUILD_DIR="%{_topdir}/BUILD"
69 RPM_OPT_FLAGS="-O2 -g -march=i386 -mcpu=i686"
70 RPM_ARCH="i386"
71 RPM_OS="linux"
72 export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS
73 RPM_DOC_DIR="/usr/share/doc"
74 export RPM_DOC_DIR
75 RPM_PACKAGE_NAME="%{name}"
76 RPM_PACKAGE_VERSION="%{version}"
77 RPM_PACKAGE_RELEASE="%{release}"
78 export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE
79 RPM_BUILD_ROOT="%{buildroot}"
80 export RPM_BUILD_ROOT
81);
82foreach (`dpkg-architecture`) {
83 s/=(.+)/="$1"/;
84 $scriptletbase .= " $_";
85}
86$scriptletbase .=
87q(
88 set -x
89 umask 022
90 cd %{_topdir}/BUILD
91);
92
93# Package data
94# This is the form of $pkgdata{pkgname}{meta}
95# meta includes Summary, Name, Version, Release, Group, Copyright,
96# Source, URL, Packager, BuildRoot, Description, BuildReq(uires),
97# Requires, Provides
98# 10/31/2005 Maybe this should be flatter? -kgd
99my %pkgdata;
100my @pkglist = ('main'); #sigh
101# Files listing. Embedding this in %pkgdata would be, um, messy.
102my %filelist;
103my $buildreq = '';
104
105# Scriptlets
106my $prepscript = '';
107my $buildscript = '';
108# %install doesn't need the full treatment from %clean; just an empty place to install to.
109# NB - rpm doesn't do this; is it really necessary?
110my $installscript = '[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT'."\n";
111my $cleanscript = '';
112# pre/post (un)install scripts. Note that these will likely barf as-is. :/
113my $preinstscript = '';
114my $postinstscript = '';
115my $preuninstscript = '';
116my $postuninstscript = '';
117
118die "Not enough arguments\n" if #$argv == 0;
119
120# Snag some environment data
121my $tmpdir;
122if (defined $ENV{TMP} && $ENV{TMP} =~ /^(\/var)?\/tmp$/) {
123 $tmpdir = $ENV{TMP};
124} else {
125 $tmpdir = "/var/tmp";
126}
127
128##main
129
130load_userconfig();
131parse_cmd();
132
133if ($cmdopts{install}) {
134 install_sdeb();
135 exit 0;
136}
137
138# Stick --rebuild handling in here - basically install_sdeb()
139# followed by tweaking options to run with -ba
140if ($cmdopts{type} eq 's') {
141 install_sdeb();
142 my @srclist = qx { pax < $srcpkg };
143 $cmdopts{type} = 'b';
144 $cmdopts{stage} = 'a';
145 foreach (@srclist) {
146 if (/SPECS/) {
147 chomp;
148 $specfile = "$topdir/$_";
149 }
150 }
151}
152
153if ($cmdopts{type} eq 'b') {
154 # Need to read the spec file to find the tarball. Note that
155 # this also generates most of the shell script required.
156 parse_spec();
157 die "Can't build $pkgdata{main}{name}: build requirements not met.\n"
158 if !checkbuildreq();
159}
160
161# -> srcpkg if -.s
162if ($cmdopts{stage} eq 's') {
163 srcpackage();
164 exit 0;
165}
166
167# Hokay. Need to:
168# -> prep if -.p OR (-.[cilabs] AND !--short-circuit)
169if ($cmdopts{stage} eq 'p' || ($cmdopts{stage} =~ /[cilabs]/ && $cmdopts{short} ne 'y')) {
170 prep();
171}
172# -> build if -.c OR (-.[ilabs] AND !--short-circuit)
173if ($cmdopts{stage} eq 'c' || ($cmdopts{stage} =~ /[ilabs]/ && $cmdopts{short} ne 'y')) {
174 build();
175}
176# -> install if -.[ilabs]
177#if ($cmdopts{stage} eq 'i' || ($cmdopts{stage} =~ /[labs]/ && $cmdopts{short} ne 'y')) {
178if ($cmdopts{stage} =~ /[ilabs]/) {
179 install();
180#foreach my $pkg (@pkglist) {
181# print "files in $pkg:\n ".$filelist{$pkg}."\n";
182#}
183
184}
185# -> binpkg and srcpkg if -.a
186if ($cmdopts{stage} eq 'a') {
187 binpackage();
188 srcpackage();
189}
190# -> binpkg if -.b
191if ($cmdopts{stage} eq 'b') {
192 binpackage();
193}
194
195# Just in case.
196exit 0;
197
198
199## load_userconfig()
200# Loads user configuration (if any)
201# Currently only handles .debmacros
202# Needs to handle "other files"
203sub load_userconfig {
204 my (undef,undef,undef,undef,undef,undef,undef,$homedir,undef) = getpwuid($<);
205 if (-e "$homedir/.debmacros") {
206 open USERMACROS,"<$homedir/.debmacros";
207 while (<USERMACROS>) {
208 # And we also only handle a few macros at the moment.
209 if (/^\%_topdir/) {
210 my (undef,$tmp) = split /\s+/, $_;
211 $topdir = $tmp;
212 }
213 }
214 }
215} # end load_userconfig()
216
217
218## parse_cmd()
219# Parses command line into global hash %cmdopts, other globals
220# Options based on rpmbuild's options
221sub parse_cmd {
222 # Don't feel like coding my own option parser...
223 #use Getopt::Long;
224 # ... but I may have to: (OTOH, rpm uses popt, so maybe we can too.)
225 #use Getopt::Popt qw(:all);
226 # Or not. >:( Stupid Debian lack of findable Perl module names in packages.
227
228 # Stuff it.
229 my $prevopt = '';
230 foreach (@ARGV) {
231 chomp;
232
233 # Is it an option?
234 if (/^-/) {
235
236 # Is it a long option?
237 if (/^--/) {
238 if (/^--short-circuit/) {
239 $cmdopts{short} = 'y';
240 } elsif (/^--rebuild/) {
241 $cmdopts{type} = 's';
242 } else {
243 print "Long opt $_\n";
244 }
245 } else {
246 # Not a long option
247 if (/^-[bt]/) {
248 if ($cmdopts{stage} eq 's') {
249 # Mutually exclusive options.
250 die "Can't use $_ with --rebuild\n";
251 } else {
252 # Capture the type (from "bare" files or tarball) and the stage (prep, build, etc)
253 ($cmdopts{stage}) = (/^-[bt]([pcilabs])/);
254 ($cmdopts{type}) = (/^-([bt])[pcilabs]/);
255 }
256 } elsif (/^-v/) {
257 # bump verbosity. Not sure what I'll actually do here...
258 } elsif (/^-i/) {
259 $cmdopts{install} = 1;
260 $prevopt = '-i';
261 } else {
262 die "Bad option $_\n";
263 }
264 }
265
266 } else { # Not an option argument
267
268 # --buildroot is the only option that takes an argument
269 # Therefore, any *other* bare arguments are the spec file,
270 # tarball, or source package we're operating on - depending
271 # on which one we meet.
272 if ($prevopt eq '--buildroot') {
273 $cmdbuildroot = $_;
274 } elsif ($prevopt eq '-i') {
275 $srcpkg = $_;
276 } else {
277 if ($cmdopts{type} eq 's') {
278 # Source package
279 if (!/(sdeb|\.src\.rpm)$/) {
280 die "Can't --rebuild with $_\n";
281 }
282 $srcpkg = $_;
283 } elsif ($cmdopts{type} eq 'b') {
284 $specfile = $_;
285 # Spec file
286 } else {
287 # Tarball
288 }
289 }
290 }
291 $prevopt = $_;
292 } # foreach @ARGV
293
294 # Some cross-checks. rpmbuild limits --short-circuit to just
295 # the "compile" and "install" targets - with good reason IMO.
296 # Note that --short-circuit with -.p is not really an error, just redundant.
297 # NB - this is NOT fatal, just ignored!
298 if ($cmdopts{short} eq 'y' && $cmdopts{stage} =~ /[labs]/) {
299 warn "Can't use --short-circuit for $targets{$cmdopts{stage}} stage. Ignoring.\n";
300 $cmdopts{short} = 'n';
301 }
302
303 # Valid options, with example arguments (if any):
304# Build from .spec file; mutually exclusive:
305 # -bp
306 # -bc
307 # -bi
308 # -bl
309 # -ba
310 # -bb
311 # -bs
312# Build from tarball; mutually exclusive:
313 # -tp
314 # -tc
315 # -ti
316 # -ta
317 # -tb
318 # -ts
319# Build from .src.(deb|rpm)
320 # --rebuild
321 # --recompile
322
323# General options
324 # --buildroot=DIRECTORY
325 # --clean
326 # --nobuild
327 # --nodeps
328 # --nodirtokens
329 # --rmsource
330 # --rmspec
331 # --short-circuit
332 # --target=CPU-VENDOR-OS
333
334 #my $popt = new Getopt::Popt(argv => \@ARGV, options => \@optionsTable);
335
336} # end parse_cmd()
337
338
339## parse_spec()
340# Parse the .spec file.
341sub parse_spec {
342 open SPECFILE,"<$specfile";
343
344LINE: while (<SPECFILE>) {
345 next if /^#/; # Ignore comments...
346 next if /^\s+$/; # ... and blank lines.
347
348 if (/^\%/) {
349 # A macro that needs further processing.
350
351 if (/^\%define\s+([^\s]+)\s+([^\s]+)/) {
352 $specglobals{$1} = expandmacros($2,'g');
353 }
354
355 if (/^\%description(?:\s+(?:-n\s+)?([a-zA-Z0-9_.-]+))?/) {
356 my $subname = "main";
357 if ($1) {
358 my $tmp = $1;
359 if (/-n/) { $subname = $tmp; } else { $subname = "$pkgdata{main}{name}-$tmp"; }
360 }
361 while (<SPECFILE>) {
362 next if /^#/; # Messy. Should be possible to do better. :/
363 redo LINE if /^\%/;
364 $pkgdata{$subname}{desc} .= " $_";
365 }
366 }
367 if (/^\%package\s+(?:-n\s+)?([a-zA-Z0-9_.-]+)/) {
368 my $subname = $1;
369 if (! /-n/) { $subname = "$pkgdata{main}{name}-$1"; }
370 push @pkglist, $subname;
371 $pkgdata{$subname}{name} = $subname;
372 $pkgdata{$subname}{version} = $pkgdata{main}{version};
373 while (<SPECFILE>) {
374 redo LINE if /^\%/;
375 if (my ($dname,$dvalue) = (/^(Summary|Group|Version|Requires|Provides):\s+(.+)$/i)) {
376 $dname =~ tr/[A-Z]/[a-z]/;
377 $pkgdata{$subname}{$dname} = $dvalue;
378 }
379 }
380 }
381
382 if (/^\%prep/) {
383 # %prep section. May have %setup macro; may include %patch tags,
384 # may be just a bare shell script.
385
386 # This really should be local-ish, but we need just the filename for the source
387 $pkgdata{main}{source} =~ s|.+/([^/]+)$|$1|;
388
389 # Replace some core macros
390 $pkgdata{main}{source} = expandmacros($pkgdata{main}{source},'gp');
391
392PREPSCRIPT: while (<SPECFILE>) {
393 if (/^\%setup/) {
394 # Parse out the %setup macro. Note that we aren't supporting
395 # many of RPM's %setup features.
396 $prepscript .= "cd $topdir/BUILD\n";
397 if ( /\s+-n\s+([^\s]+)\s+/ ) {
398 $tarballdir = $1;
399 } else {
400 $tarballdir = "$pkgdata{main}{name}-$pkgdata{main}{version}";
401 }
402 $prepscript .= "rm -rf $tarballdir\ntar -".
403 ( $pkgdata{main}{source} =~ /\.tar\.gz$/ ? "z" : "" ).
404 ( $pkgdata{main}{source} =~ /\.tar\.bz2$/ ? "j" : "" ).
405 ( /\s+-q\s+/ ? '' : 'vv' )."xf ".
406 "$topdir/SOURCES/$pkgdata{main}{source}\n".
407 qq(STATUS=\$?\nif [ \$STATUS -ne 0 ]; then\n exit \$STATUS\nfi\n).
408 ( /\s+-n\s+([^\s]+)\s+/ ?
409 "cd $1\n" : "cd $pkgdata{main}{name}-$pkgdata{main}{version}\n" ).
410 qq([ `/usr/bin/id -u` = '0' ] && /bin/chown -Rhf root .\n).
411 qq([ `/usr/bin/id -u` = '0' ] && /bin/chgrp -Rhf root .\n).
412 qq(/bin/chmod -Rf a+rX,g-w,o-w .\n);
413 } elsif (/^\%patch([^:]+)\s+(.+)$/) {
414 $prepscript .= "patch $2 <$topdir/SOURCES/".$pkgdata{main}{"patch$1"}."\n";
415 } else {
416 last PREPSCRIPT if /^\%/;
417 $prepscript .= $_;
418 }
419 }
420 redo LINE;
421 }
422 if (/^\%build/) {
423 # %build. This is pretty much just a shell script. There
424 # *are* a few macros, but we're not going to deal with them yet.
425 $buildscript .= "cd $tarballdir\n";
426BUILDSCRIPT: while (<SPECFILE>) {
427 if (/^\%configure/) {
428 $buildscript .= expandmacros($_,'cgbp');
429 } elsif (/^\%\{__make\}/) {
430 $buildscript .= expandmacros($_,'mgbp');
431 } else {
432 last BUILDSCRIPT if /^\%[^{]/;
433 $buildscript .= $_;
434 }
435 }
436 redo LINE;
437 }
438 if (/^\%install/) {
439 $installscript .= "cd $tarballdir\n";
440INSTALLSCRIPT: while (<SPECFILE>) {
441 if (/^\%makeinstall/) {
442 $installscript .= expandmacros($_,'igbp');
443 } else {
444 last INSTALLSCRIPT if /^\%/;
445 $installscript .= $_;
446 }
447 }
448 redo LINE;
449 }
450 if (/^\%clean/) {
451 while (<SPECFILE>) {
452 redo LINE if /^\%/;
453 $cleanscript .= $_;
454 }
455 $cleanscript = expandmacros($cleanscript,'gp');
456 }
457
458 # pre/post (un)install scripts
459 if (/^\%pre\b/) {
460 while (<SPECFILE>) {
461 redo LINE if /^\%/;
462 $preinstscript .= $_;
463 }
464 }
465 if (/^\%post\b/) {
466 while (<SPECFILE>) {
467 redo LINE if /^\%/;
468 $postinstscript .= $_;
469 }
470 }
471 if (/^\%preun\b/) {
472 while (<SPECFILE>) {
473 redo LINE if /^\%/;
474 $preuninstscript .= $_;
475 }
476 }
477 if (/^\%postun\b/) {
478 while (<SPECFILE>) {
479 redo LINE if /^\%/;
480 $postuninstscript .= $_;
481 }
482 }
483 # done %pre/%post scripts
484
485 if (/^\%files(?:\s+(?:-n\s+)?([a-zA-z0-9]+))?/) {
486 my $pkgname = 'main';
487 if ($1) { # Magic to add entries to the right list of files
488 my $tmp = $1;
489 if (/-n/) { $pkgname = $tmp; } else { $pkgname = "$pkgdata{main}{name}-$tmp"; }
490 }
491
492 # Set this now, so it can be flipped a bit later, and used much later.
493 #$pkgdata{$pkgname}{conffiles} = 0;
494
495 while (<SPECFILE>) {
496 chomp;
497 next if /^#/;
498 # need to update this to deal (properly) with %dir, %attr, etc
499 next if /^\%dir/;
500 next if /^\%attr/;
501 next if /^\%defattr/;
502
503 # Debian dpkg doesn't speak "%docdir". Meh.
504 next if /^\%docdir/;
505
506 # Conffiles. Note that Debian and RH have similar, but not
507 # *quite* identical ideas of what constitutes a conffile. Nrgh.
508 if (/^\%config\s+(.+)$/) {
509 $pkgdata{$pkgname}{conffiles} = 1; # Flag it for later
510 my $tmp = $1; # Now we can mangleificationate it. And we probably need to. :/
511 $tmp = expandmacros($tmp, 'gp'); # Expand common macros
512 if ($tmp !~ /\s+/) {
513 # Simplest case, just a file. Whew.
514 push @{$pkgdata{$pkgname}{conflist}}, $tmp;
515 $filelist{$pkgname} .= " $tmp";
516 } else {
517 # Wot? Spaces? That means extra %-macros. Which, for the most part, can be ignored.
518 ($tmp) = ($tmp =~ /.+\s([^\s]+)/); # Strip everything before the last space
519 push @{$pkgdata{$pkgname}{conflist}}, $tmp;
520 $filelist{$pkgname} .= " $tmp";
521 }
522 next;
523 }
524
525 # and finally we can fall through %{_<FHS>}-prefixed locations...
526 if (/^\%\{_/) {
527 $filelist{$pkgname} .= " $_";
528 next;
529 }
530 # EW. Necessary to clear up %define expansions before we exit with redo.
531 $_ = expandmacros $_, 'g';
532
533 # ... unknown or "next section" % directives ...
534 redo LINE if /^\%/;
535
536 # ... and "normal" files
537 $filelist{$pkgname} .= " $_";
538 }
539 $filelist{$pkgname} = expandmacros($filelist{$pkgname}, 'gp');
540 } # done %file section
541
542 if (/^\%changelog/) {
543 $pkgdata{main}{changelog} = '';
544 while (<SPECFILE>) {
545 redo LINE if /^\%/;
546 $pkgdata{main}{changelog} .= $_;
547 }
548 }
549
550 } else { # Data from the spec file "header"
551
552 if (/^summary:\s+(.+)/i) {
553 $pkgdata{main}{summary} = $1;
554 } elsif (/^name:\s+(.+)/i) {
555 $pkgdata{main}{name} = expandmacros($1,'g');
556 } elsif (/^version:\s+(.+)/i) {
557 $pkgdata{main}{version} = expandmacros($1,'g');
558 } elsif (/^release:\s+(.+)/i) {
559 $pkgdata{main}{release} = expandmacros($1,'g');
560 } elsif (/^group:\s+(.+)/i) {
561 $pkgdata{main}{group} = $1;
562 } elsif (/^copyright:\s+(.+)/i) {
563 $pkgdata{main}{copyright} = $1;
564 } elsif (/^url:\s+(.+)/i) {
565 $pkgdata{main}{url} = $1;
566 } elsif (/^packager:\s+(.+)/i) {
567 $pkgdata{main}{packager} = $1;
568 } elsif (/^buildroot:\s+(.+)/i) {
569 $buildroot = $1;
570 } elsif (/^source:\s+(.+)/i) {
571 $pkgdata{main}{source} = $1;
572 die "Unknown tarball format $1\n" if $1 !~ /\.tar\.(?:gz|bz2)$/;
573 } elsif (/^source([0-9]+):\s+(.+)/i) {
574 $pkgdata{sources}{$1} = $2;
575 } elsif (/^patch([^:]+):\s+(.+)$/i) {
576 my $patchname = "patch$1";
577 $pkgdata{main}{$patchname} = $2;
578 if ($pkgdata{main}{$patchname} =~ /\//) {
579 # URL-style patch. Rare but not unheard-of.
580 my @patchbits = split '/', $pkgdata{main}{$patchname};
581 $pkgdata{main}{$patchname} = $patchbits[$#patchbits];
582 }
583 } elsif (/^buildreq(?:uires)?:\s+(.+)/i) {
584 $buildreq .= ", $1";
585 } elsif (/^requires:\s+(.+)/i) {
586 $pkgdata{main}{requires} .= ", $1";
587 } elsif (/^provides:\s+(.+)/i) {
588 $pkgdata{main}{provides} .= ", $1";
589 } elsif (/^conflicts:\s+(.+)/i) {
590 $pkgdata{main}{conflicts} .= ", $1";
591 }
592#Name: suwrap
593#Version: 0.04
594#Release: 3
595#Group: Applications/System
596#Copyright: WebHart internal ONLY. :(
597#BuildArchitectures: i386
598#BuildRoot: /tmp/%{name}-%{version}
599#Url: http://virtual.webhart.net
600#Packager: Kris Deugau <kdeugau@deepnet.cx>
601#Source: ftp://virtual.webhart.net/%{name}-%{version}.tar.gz
602
603 }
604 }
605
606 # Parse and replace some more macros. More will be replaced even later.
607
608 # Expand macros as necessary.
609 $scriptletbase = expandmacros($scriptletbase,'gp');
610
611 $buildroot = $cmdbuildroot if $cmdbuildroot;
612 $buildroot = expandmacros($buildroot,'gp');
613
614 close SPECFILE;
615} # end parse_spec()
616
617
618## prep()
619# Writes and executes the %prep script (mostly) built while reading the spec file.
620sub prep {
621 # Replace some things here just to make sure.
622 $prepscript = expandmacros($prepscript,'gp');
623
624#print $prepscript; exit 0;
625
626 # create script filename
627 my $prepscriptfile = "$tmpdir/deb-tmp.prep.".int(rand(99998)+1);
628 sysopen(PREPSCRIPT, $prepscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
629 or die $!;
630 print PREPSCRIPT $scriptletbase;
631 print PREPSCRIPT $prepscript;
632 close PREPSCRIPT;
633
634 # execute
635 print "Calling \%prep script $prepscriptfile...\n";
636 system("/bin/sh -e $prepscriptfile") == 0
637 or die "Can't exec: $!\n";
638
639 # and clean up
640 unlink $prepscriptfile;
641} # end prep()
642
643
644## build()
645# Writes and executes the %build script (mostly) built while reading the spec file.
646sub build {
647 # Expand the macros
648 $buildscript = expandmacros($buildscript,'cgbp');
649
650 # create script filename
651 my $buildscriptfile = "$tmpdir/deb-tmp.build.".int(rand(99998)+1);
652 sysopen(BUILDSCRIPT, $buildscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
653 or die $!;
654 print BUILDSCRIPT $scriptletbase;
655 print BUILDSCRIPT $buildscript;
656 close BUILDSCRIPT;
657
658 # execute
659 print "Calling \%build script $buildscriptfile...\n";
660 system("/bin/sh -e $buildscriptfile") == 0
661 or die "Can't exec: $!\n";
662
663 # and clean up
664 unlink $buildscriptfile;
665} # end build()
666
667
668## install()
669# Writes and executes the %install script (mostly) built while reading the spec file.
670sub install {
671 # Expand the macros
672 $installscript = expandmacros($installscript,'igbp');
673
674 # create script filename
675 my $installscriptfile = "$tmpdir/deb-tmp.inst.".int(rand(99998)+1);
676 sysopen(INSTSCRIPT, $installscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
677 or die $!;
678 print INSTSCRIPT $scriptletbase;
679# print INSTSCRIPT $cleanscript; # Clean up our install target before installing into it.
680 print INSTSCRIPT $installscript;
681 close INSTSCRIPT;
682
683 # execute
684 print "Calling \%install script $installscriptfile...\n";
685 system("/bin/sh -e $installscriptfile") == 0
686 or die "Can't exec: $!\n";
687
688 # and clean up
689 unlink $installscriptfile;
690} # end install()
691
692
693## binpackage()
694# Creates the binary .deb package from the installed tree in $buildroot.
695# Writes and executes a shell script to do so.
696# Creates miscellaneous files required by dpkg-deb to actually build the package file.
697# Should handle simple subpackages
698sub binpackage {
699 # Make sure we have somewhere to write the .deb file
700 if (!-e "$topdir/DEBS/i386") {
701 mkdir "$topdir/DEBS/i386";
702 }
703
704 foreach my $pkg (@pkglist) {
705
706 # Gotta do this first, otherwise we don't have a place to move files from %files
707 mkdir "$buildroot/$pkg";
708
709 # Eliminate any lingering % macros
710 $filelist{$pkg} = expandmacros $filelist{$pkg}, 'g';
711
712 my @pkgfilelist = split ' ', $filelist{$pkg};
713 foreach my $pkgfile (@pkgfilelist) {
714 $pkgfile = expandmacros($pkgfile, 'gp');
715 my ($fpath,$fname) = ($pkgfile =~ m|(.+?/?)?([^/]+)$|); # We don't need $fname now, but we might.
716 qx { mkdir -p $buildroot/$pkg$fpath }
717 if $fpath && $fpath ne '';
718 qx { mv $buildroot$pkgfile $buildroot/$pkg$fpath };
719 }
720
721 # Get the "Depends" (Requires) a la RPM. Ish. We strip the leading
722 # comma and space here (if needed) in case there were "Requires" specified
723 # in the spec file - those would precede these.
724 ($pkgdata{$pkg}{requires} .= getreqs("$buildroot/$pkg")) =~ s/^, //;
725
726 # Do this here since we're doing {depends}...
727 if (defined($pkgdata{$pkg}{provides})) {
728 $pkgdata{$pkg}{provides} =~ s/^, //;
729 $pkgdata{$pkg}{provides} = expandmacros($pkgdata{$pkg}{provides},'gp');
730 }
731 if (defined($pkgdata{$pkg}{conflicts})) {
732 $pkgdata{$pkg}{conflicts} =~ s/^, //;
733 $pkgdata{$pkg}{conflicts} = expandmacros($pkgdata{$pkg}{conflicts},'gp');
734 }
735
736 # Gotta do this next, otherwise the control file has nowhere to go. >:(
737 mkdir "$buildroot/$pkg/DEBIAN";
738
739 # Hack the filename for the package into a Debian-tool-compatible format. GRRRRRR!!!!!
740 # Have I mentioned I hate Debian Policy?
741 $pkgdata{$pkg}{name} =~ tr/_/-/;
742
743 # create script filename
744 my $debscriptfile = "$tmpdir/deb-tmp.pkg.".int(rand(99998)+1);
745 sysopen(DEBSCRIPT, $debscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
746 or die $!;
747 print DEBSCRIPT $scriptletbase;
748 print DEBSCRIPT "fakeroot dpkg-deb -b $buildroot/$pkg $topdir/DEBS/i386/".
749 "$pkgdata{$pkg}{name}_$pkgdata{$pkg}{version}-$pkgdata{main}{release}_i386.deb\n";
750 # %$&$%@#@@#%@@@ Debian and their horrible ugly package names. >:(
751 close DEBSCRIPT;
752
753 my $control = "Package: $pkgdata{$pkg}{name}\n".
754 "Version: $pkgdata{$pkg}{version}-$pkgdata{main}{release}\n".
755 "Section: $pkgdata{$pkg}{group}\n".
756 "Priority: optional\n".
757 "Architecture: i386\n".
758 "Maintainer: $pkgdata{main}{packager}\n".
759 ( $pkgdata{$pkg}{requires} ne '' ? "Depends: $pkgdata{$pkg}{requires}\n" : '' ).
760 ( defined($pkgdata{$pkg}{provides}) ? "Provides: $pkgdata{$pkg}{provides}\n" : '' ).
761 ( defined($pkgdata{$pkg}{conflicts}) ? "Conflicts: $pkgdata{$pkg}{conflicts}\n" : '' ).
762 "Description: $pkgdata{$pkg}{summary}\n";
763 $control .= "$pkgdata{$pkg}{desc}\n";
764
765 open CONTROL, ">$buildroot/$pkg/DEBIAN/control";
766 print CONTROL $control;
767 close CONTROL;
768
769 # Iff there are conffiles (as specified in the %files list(s), add'em
770 # in so dpkg-deb can tag them.
771 if ($pkgdata{$pkg}{conffiles}) {
772 open CONFLIST, ">$buildroot/$pkg/DEBIAN/conffiles";
773 foreach my $conffile (@{$pkgdata{$pkg}{conflist}}) {
774 print CONFLIST "$conffile\n";
775 }
776 close CONFLIST;
777 }
778
779 # Can't see much point in scripts on subpackages... although since
780 # it's *possible* I should support it at some point.
781 if ($pkg eq 'main') {
782 if ($preinstscript ne '') {
783 $preinstscript = expandmacros($preinstscript,'g');
784 open PREINST, ">$buildroot/$pkg/DEBIAN/preinst";
785 print PREINST "#!/bin/sh\nset -e\n\n";
786 print PREINST $preinstscript;
787 close PREINST;
788 `chmod 0755 $buildroot/$pkg/DEBIAN/preinst`;
789 }
790 if ($postinstscript ne '') {
791 $postinstscript = expandmacros($postinstscript,'g');
792 open POSTINST, ">$buildroot/$pkg/DEBIAN/postinst";
793 print POSTINST "#!/bin/sh\nset -e\n\n";
794 print POSTINST $postinstscript;
795 close POSTINST;
796 `chmod 0755 $buildroot/$pkg/DEBIAN/postinst`;
797 }
798 if ($preuninstscript ne '') {
799 $preuninstscript = expandmacros($preuninstscript,'g');
800 open PREUNINST, ">$buildroot/$pkg/DEBIAN/prerm";
801 print PREUNINST "#!/bin/sh\nset -e\n\n";
802 print PREUNINST $preuninstscript;
803 close PREUNINST;
804 `chmod 0755 $buildroot/$pkg/DEBIAN/prerm`;
805 }
806 if ($postuninstscript ne '') {
807 $postuninstscript = expandmacros($postuninstscript,'g');
808 open POSTUNINST, ">$buildroot/$pkg/DEBIAN/postrm";
809 print POSTUNINST "#!/bin/sh\nset -e\n\n";
810 print POSTUNINST $postuninstscript;
811 close POSTUNINST;
812 `chmod 0755 $buildroot/$pkg/DEBIAN/postrm`;
813 }
814 }
815
816 # execute
817 print "Calling package creation script $debscriptfile for $pkgdata{$pkg}{name}...\n";
818 system("/bin/sh -e $debscriptfile") == 0
819 or die "Can't exec: $!\n";
820
821 # and clean up
822 unlink $debscriptfile;
823
824 } # subpackage loop
825
826} # end binpackage()
827
828
829## srcpackage()
830# Builds a .src.deb source package. Note that Debian's idea of
831# a "source package" is seriously flawed IMO, because you can't
832# easily copy it as-is.
833# Not quite identical to RPM, but Good Enough (TM).
834sub srcpackage {
835 # In case we were called with -bs.
836 $pkgdata{main}{name} =~ tr/_/-/;
837 my $pkgsrcname = "$pkgdata{main}{name}-$pkgdata{main}{version}-$pkgdata{main}{release}.sdeb";
838
839 my $paxcmd;
840
841 # We'll definitely need this later, and *may* need it sooner.
842 (my $barespec = $specfile) =~ s|.+/([^/]+)$|$1|;
843
844 # Copy the specfile to the build tree, but only if it's not there already.
845##buglet: need to deal with silly case where silly user has put the spec
846# file in a subdir of %{_topdir}/SPECS. Ewww. Silly user!
847 if (abs_path($specfile) !~ /^$topdir\/SPECS/) {
848 $paxcmd .= "cp $specfile %{_topdir}/SPECS/; \n"
849 }
850
851 # use pax -w [file] [file] ... >outfile.sdeb
852 $paxcmd = "cd $topdir; pax -w ";
853
854# tweak source entry into usable form. Need it locally somewhere along the line.
855 (my $pkgsrc = $pkgdata{main}{source}) =~ s|.+/([^/]+)$|$1|;
856 $paxcmd .= "SOURCES/$pkgsrc ";
857
858 # create file list: Source[nn], Patch[nn]
859 foreach my $specbit (keys %{$pkgdata{main}} ) {
860 next if $specbit eq 'source';
861 $paxcmd .= "SOURCES/$pkgdata{main}{$specbit} " if $specbit =~ /^patch/;
862##buglet: need to deal with case where patches are listed as URLs?
863# or other extended pathnames? Silly !@$%^&!%%!%!! user!
864 }
865
866 foreach my $source (keys %{$pkgdata{sources}}) {
867 $paxcmd .= "SOURCES/$pkgdata{sources}{$source} ";
868 }
869
870 # add the spec file, source package destination, and cd back where we came from.
871 $paxcmd .= "SPECS/$barespec > $topdir/SDEBS/$pkgsrcname; cd -";
872
873 # In case of %-macros...
874 $paxcmd = expandmacros($paxcmd,'gp');
875
876 system "$paxcmd";
877 print "Wrote source package $pkgsrcname in $topdir/SDEBS.\n";
878}
879
880
881## checkbuildreq()
882# Checks the build requirements (if any)
883# Spits out a rude warning and returns a true-false error if any
884# requirements are not met.
885sub checkbuildreq {
886 return 1 if $buildreq eq ''; # No use doing extra work.
887
888 if ( ! -e "/usr/bin/dpkg-query" ) {
889 print "**WARNING** dpkg-query not found. Can't check build-deps.\n".
890 " Required for sucessful build:\n".$buildreq."\n".
891 " Continuing anyway.\n";
892 return 1;
893 }
894
895 my $reqflag = 1; # unset iff a buildreq is missing
896
897 $buildreq =~ s/^, //; # Strip the leading comma and space
898 my @reqlist = split /,\s+/, $buildreq;
899
900 foreach my $req (@reqlist) {
901 my ($pkg,$rel,$ver);
902
903 # We have two classes of requirements - versioned and unversioned.
904 if ($req =~ /[><=]/) {
905 # Pick up the details of versioned buildreqs
906 ($pkg,$rel,$ver) = ($req =~ /([a-z0-9._-]+)\s+([><=]+)\s+([a-z0-9._-]+)/);
907 } else {
908 # And the unversioned ones.
909 $pkg = $req;
910 $rel = '>=';
911 $ver = 0;
912 }
913
914 my @pkglist = qx { dpkg-query --showformat '\${status}\t\${version}\n' -W $pkg };
915# need to check if no lines returned - means a bad buildreq
916 my ($reqstat,undef,undef,$reqver) = split /\s+/, $pkglist[0];
917 if ($reqstat !~ /install/) {
918 print " * Missing build-dependency $pkg!\n";
919 $reqflag = 0;
920 } else {
921# gotta be a better way to do this... :/
922 if ($rel eq '>=' && !($reqver ge $ver)) {
923 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
924 $reqflag = 0;
925 }
926 if ($rel eq '>' && !($reqver gt $ver)) {
927 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
928 $reqflag = 0;
929 }
930 if ($rel eq '<=' && !($reqver le $ver)) {
931 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
932 $reqflag = 0;
933 }
934 if ($rel eq '<' && !($reqver lt $ver)) {
935 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
936 $reqflag = 0;
937 }
938 if ($rel eq '=' && !($reqver eq $ver)) {
939 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
940 $reqflag = 0;
941 }
942 } # end not installed/installed check
943 } # end req loop
944
945 return $reqflag;
946} # end checkbuildreq()
947
948
949## getreqs()
950# Find out which libraries/packages are required for any
951# executables and libs in a given file tree.
952# (Debian doesn't have soname-level deps; just package-level)
953# Returns an empty string if the tree contains no binaries.
954# Doesn't work well on shell scripts. but those *should* be
955# fine anyway. (Yeah, right...)
956sub getreqs() {
957 my $pkgtree = $_[0];
958
959 print "Checking library requirements...\n";
960 my @binlist = qx { find $pkgtree -type f -perm 755 };
961
962 if (scalar(@binlist) == 0) {
963 return '';
964 }
965
966 my @reqlist;
967 foreach (@binlist) {
968 push @reqlist, qx { ldd $_ };
969 }
970
971 # Get the list of libs provided by this package. Still doesn't
972 # handle the case where the lib gets stuffed into a subpackage. :/
973 my @intprovlist = qx { find $pkgtree -type f -name "*.so*" };
974 my $provlist = '';
975 foreach (@intprovlist) {
976 s/$pkgtree//;
977 $provlist .= "$_";
978 }
979
980 my %reqs;
981 my $reqlibs = '';
982
983 foreach (@reqlist) {
984 next if /^$pkgtree/;
985 next if /not a dynamic executable/;
986 next if m|/lib/ld-linux.so|; # Hack! Hack! PTHBTT! (libc suxx0rz)
987
988 my ($req) = (/^\s+([a-z0-9._-]+)/); # dig out the actual library (so)name
989
990 # Ignore libs provided by this package. Note that we don't match
991 # on word-boundary at the *end* of the lib we're looking for, as the
992 # looked-for lib may not have the full soname version. (ie, it may
993 # "just" point to one of the symlinks that get created somewhere.)
994 next if $provlist =~ /\b$req/;
995
996 $reqlibs .= " $req";
997 }
998
999 if ($reqlibs ne '') {
1000 foreach (qx { dpkg -S $reqlibs }) {
1001 my ($libpkg,undef) = split /:\s+/;
1002 $reqs{$libpkg} = 1;
1003 }
1004 }
1005
1006 my $deplist = '';
1007 foreach (keys %reqs) {
1008 $deplist .= ", $_";
1009 }
1010
1011# For now, we're done. We're not going to meddle with versions yet.
1012# Among other things, it's messier than handling "simple" yes/no "do
1013# we have this lib?" deps. >:(
1014
1015 return $deplist;
1016} # end getreqs()
1017
1018
1019## install_sdeb()
1020# Extracts .sdeb contents to %_topdir as appropriate
1021sub install_sdeb {
1022 my $paxcmd = "cd $topdir; pax -r <$srcpkg; cd -";
1023
1024 # In case of %-macros...
1025 $paxcmd = expandmacros($paxcmd,'gp');
1026
1027 system "$paxcmd";
1028 print "Extracted source package $srcpkg to $topdir.\n";
1029} # end install_sdeb()
1030
1031
1032## expandmacros()
1033# Expands all %{blah} macros in the passed string
1034# Split up a bit with some sections so we don't spend time trying to
1035# expand macros that are only used in a few specific places.
1036sub expandmacros {
1037 my $macrostring = shift;
1038 my $section = shift;
1039
1040 # To allow the FHS-ish %configure and %makeinstall to work The Right Way.
1041 # (Without clobbering the global $buildroot.)
1042 my $prefix = '';
1043
1044 if ($section =~ /c/) {
1045 # %configure macro
1046# Don't know what it's for, don't have a useful default replacement
1047# --program-prefix=%{_program_prefix} \
1048 $macrostring =~ s'%configure'./configure --host=$DEB_HOST_GNU_TYPE \
1049 --build=$DEB_BUILD_GNU_TYPE \
1050 --prefix=%{_prefix} \
1051 --exec-prefix=%{_exec_prefix} \
1052 --bindir=%{_bindir} \
1053 --sbindir=%{_sbindir} \
1054 --sysconfdir=%{_sysconfdir} \
1055 --datadir=%{_datadir} \
1056 --includedir=%{_includedir} \
1057 --libdir=%{_libdir} \
1058 --libexecdir=%{_libexecdir} \
1059 --localstatedir=%{_localstatedir} \
1060 --sharedstatedir=%{_sharedstatedir} \
1061 --mandir=%{_mandir} \
1062 --infodir=%{_infodir} ';
1063 } # done %configure
1064
1065 if ($section =~ /m/) {
1066 $macrostring =~ s'%{__make}'make ';
1067 } # done make
1068
1069 if ($section =~ /i/) {
1070 # This is where we need to mangle $prefix.
1071 $macrostring =~ s'%makeinstall'make %{fhs} install';
1072 $prefix = $buildroot;
1073 } # done %install and/or %makeinstall
1074
1075 # Build data
1076 # Note that these are processed in reverse order to get the substitution order right
1077 if ($section =~ /b/) {
1078# $macrostring =~ s'%{fhs}'host=$DEB_HOST_GNU_TYPE \
1079# build=$DEB_BUILD_GNU_TYPE \
1080 $macrostring =~ s'%{fhs}'prefix=%{_prefix} \
1081 exec-prefix=%{_exec_prefix} \
1082 bindir=%{_bindir} \
1083 sbindir=%{_sbindir} \
1084 sysconfdir=%{_sysconfdir} \
1085 datadir=%{_datadir} \
1086 includedir=%{_includedir} \
1087 libdir=%{_libdir} \
1088 libexecdir=%{_libexecdir} \
1089 localstatedir=%{_localstatedir} \
1090 sharedstatedir=%{_sharedstatedir} \
1091 mandir=%{_mandir} \
1092 infodir=%{_infodir} \
1093';
1094
1095 # Note that the above regex terminates with the extra space
1096 # "Just In Case" of user additions, which will then get neatly
1097 # tagged on the end where they take precedence (supposedly)
1098 # over the "default" ones.
1099
1100 # Now we cascade the macros introduced above. >_<
1101 # Wot ot to go theah:
1102 $macrostring =~ s|%{_mandir}|%{_datadir}/man|g; #/usr/share/man
1103 $macrostring =~ s|%{_infodir}|%{_datadir}/info|g; #/usr/share/info
1104 $macrostring =~ s|%{_oldincludedir}|/usr/include|g; #/usr/include
1105 $macrostring =~ s|%{_includedir}|%{_prefix\}/include|g; #/usr/include
1106 $macrostring =~ s|%{_libdir}|%{_exec_prefix}/%{_lib}|g; #/usr/lib
1107 $macrostring =~ s|%{_lib}|lib|g; #?
1108 $macrostring =~ s|%{_localstatedir}|/var|g; #/var
1109 $macrostring =~ s|%{_sharedstatedir}|%{_prefix}/com|g; #/usr/com WTF?
1110 $macrostring =~ s|%{_sysconfdir}|/etc|g; #/etc
1111 $macrostring =~ s|%{_datadir}|%{_prefix}/share|g; #/usr/share
1112 $macrostring =~ s|%{_libexecdir}|%{_exec_prefix}/libexec|g; #/usr/libexec
1113 $macrostring =~ s|%{_sbindir}|%{_exec_prefix}/sbin|g; #/usr/sbin
1114 $macrostring =~ s|%{_bindir}|%{_exec_prefix}/bin|g; #/usr/bin
1115 $macrostring =~ s|%{_exec_prefix}|%{_prefix}|g; #/usr
1116 $macrostring =~ s|%{_prefix}|/usr|g; #/usr
1117 } # done with config section
1118
1119 # Package data
1120 if ($section =~ /p/) {
1121 $macrostring =~ s/\%\{buildroot\}/$buildroot/gi;
1122 foreach my $source (keys %{$pkgdata{sources}}) {
1123 $macrostring =~ s/\%\{source$source\}/$topdir\/SOURCES\/$pkgdata{sources}{$source}/gi;
1124 }
1125 $macrostring =~ s/\%\{name\}/$pkgdata{main}{name}/gi;
1126 $macrostring =~ s/\%\{version\}/$pkgdata{main}{version}/gi;
1127 $macrostring =~ s/\%\{release\}/$pkgdata{main}{release}/gi;
1128 }
1129
1130 # Globals, and not-so-globals
1131 if ($section =~ /g/) {
1132 $macrostring =~ s|%{_builddir}|%{_topdir}/BUILD|g;
1133 $macrostring =~ s|%{_topdir}|$topdir|g;
1134 $macrostring =~ s|%{_tmppath}|$tmpdir|g;
1135 $macrostring =~ s'%{_docdir}'/usr/share/doc'g;
1136
1137 # Standard FHS locations. More or less.
1138 $macrostring =~ s'%{_bindir}'/usr/bin'g;
1139 $macrostring =~ s'%{_sbindir}'/usr/sbin'g;
1140 $macrostring =~ s'%{_mandir}'/usr/share/man'g;
1141 $macrostring =~ s'%{_includedir}'/usr/include'g;
1142 $macrostring =~ s'%{_libdir}'/usr/lib'g;
1143 $macrostring =~ s'%{_sysconfdir}'/etc'g;
1144 $macrostring =~ s'%{_localstatedir}'/var'g;
1145
1146 # %define's
1147 foreach my $key (keys %specglobals) {
1148 $macrostring =~ s|%{$key}|$specglobals{$key}|g;
1149 }
1150
1151 # system programs. RPM uses a global config file for these; we'll just
1152 # ASS-U-ME and make life a little simpler.
1153 if ($macrostring =~ /\%\{\_\_([a-z0-9_-]+)\}/) {
1154 $macrostring =~ s|%{__([a-z0-9_-]+)}|$1|g;
1155 }
1156 } # done with globals section
1157
1158 return $macrostring;
1159} # end expandmacros()
1160
1161
1162
1163__END__
1164
1165
1166
1167=head1 NAME
1168
1169debbuild - Build Debian-compatible packages from RPM spec files
1170
1171=head1 SYNOPSIS
1172
1173 debbuild {-ba|-bb|-bp|-bc|-bi|-bl|-bs} [build-options] file.spec
1174
1175 debbuild {-ta|-tb|-tp|-tc|-ti|-tl|-ts} [build-options] file.tar.{gz|bz2}
1176
1177 debbuild --rebuild file.src.{rpm|deb}
1178
1179=head1 DESCRIPTION
1180
1181This script attempts to build Debian-friendly semi-native packages
1182from RPM spec files, RPM-friendly tarballs, and RPM source packages
1183(.src.rpm). It accepts I<most> of the options rpmbuild does, and
1184should be able to interpret most spec files usefully. Perl modules
1185should be handled via CPAN+dh-make-perl instead; Debian's conventions
1186for such things do not lend themselves to automated conversion.
1187
1188As far as possible, the command-line options are identical to those
1189from rpmbuild, although several rpmbuild options are not supported.
1190
1191=cut
Note: See TracBrowser for help on using the repository browser.