source: trunk/debbuild@ 96

Last change on this file since 96 was 96, checked in by kdeugau, 17 years ago

/trunk

Bugfixery and add a "fixme" note re: %if constructs outside of preamble

  • Cosmetic cleanup: swap (undef,undef...) = (function) for $var=(function)[n]
  • Expand macro defs from ~/.debmacros
  • Move expansion of %(...) constructs lower in expandmacros so that %{macro} expansions get done before the shell call
  • Property svn:executable set to *
  • Property svn:keywords set to Date Rev Author
File size: 48.9 KB
Line 
1#!/usr/bin/perl -w
2# debbuild script
3# Shamelessly steals interface 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: 2007-05-03 19:45:34 +0000 (Thu, 03 May 2007) $
9# SVN revision $Rev: 96 $
10# Last update by $Author: kdeugau $
11###
12# Copyright 2005-2007 Kris Deugau <kdeugau@deepnet.cx>
13#
14# This program is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License as published by
16# the Free Software Foundation; either version 2 of the License, or
17# (at your option) any later version.
18#
19# This program is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program; if not, write to the Free Software
26# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
28use strict;
29use warnings;
30use Fcntl; # for sysopen flags
31use Cwd 'abs_path'; # for finding where files really are
32
33# regex debugger
34#use re "debug";
35
36# Program flow:
37# -> Parse/execute "system" config/macros (if any - should be rare)
38# -> Parse/execute "user" config/macros (if any - *my* requirement is %_topdir)
39# -> Parse command line for options, spec file/tarball/.src.deb (NB - also accept .src.rpm)
40
41sub expandmacros;
42
43# User's prefs for dirs, environment, etc,etc,etc.
44# config file ~/.debmacros
45# Default ordered search paths for config/macros:
46# /usr/lib/rpm/rpmrc /usr/lib/rpm/redhat/rpmrc /etc/rpmrc ~/.rpmrc
47# /usr/lib/rpm/macros /usr/lib/rpm/redhat/macros /etc/rpm/macros ~/.rpmmacros
48# **NOTE: May be possible to (ab)use bits of debhelper
49
50# Build tree
51# default is /usr/src/debian/{BUILD,SOURCES,SPECS,DEBS,SDEBS}
52
53# Globals
54my $finalmessages = ''; # A place to stuff messages that I want printed at the *very* end of any processing.
55my $specfile;
56my $tarball;
57my $srcpkg;
58my $cmdbuildroot;
59my $tarballdir = '%{name}-%{version}'; # We do this in case of a spec file not using %setup...
60my %specglobals; # For %define's in specfile, among other things.
61
62# Initialized globals
63my $verbosity = 0;
64my %cmdopts = (type => '',
65 stage => 'a',
66 short => 'n'
67 );
68my $topdir = "/usr/src/debian";
69my $buildroot = "%{_tmppath}/%{name}-%{version}-%{release}.root".int(rand(99998)+1);
70
71# "Constants"
72my %targets = ('p' => 'Prep',
73 'c' => 'Compile',
74 'i' => 'Install',
75 'l' => 'Verify %files',
76 'a' => 'Build binary and source',
77 'b' => 'Build binary',
78 's' => 'Build source'
79 );
80# Ah, the joys of multiple architectures. :( Feh.
81# As copied from rpm
82my %optflags = ( 'i386' => '-O2 -g -march=i386 -mcpu=i686',
83 'amd64' => '-O2 -g'
84 );
85my $hostarch; # we set this later...
86my $scriptletbase =
87q(#!/bin/sh
88
89 RPM_SOURCE_DIR="%{_topdir}/SOURCES"
90 RPM_BUILD_DIR="%{_topdir}/BUILD"
91 RPM_OPT_FLAGS="%{optflags}"
92 RPM_ARCH="%{_arch}"
93 RPM_OS="linux"
94 export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS
95 RPM_DOC_DIR="/usr/share/doc"
96 export RPM_DOC_DIR
97 RPM_PACKAGE_NAME="%{name}"
98 RPM_PACKAGE_VERSION="%{version}"
99 RPM_PACKAGE_RELEASE="%{release}"
100 export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE
101 RPM_BUILD_ROOT="%{buildroot}"
102 export RPM_BUILD_ROOT
103);
104foreach (`dpkg-architecture`) {
105 s/=(.+)/="$1"/;
106 $scriptletbase .= " $_";
107 ($hostarch) = (/^DEB_HOST_ARCH="(.+)"$/) if /DEB_HOST_ARCH=/;
108}
109$scriptletbase .=
110q(
111 set -x
112 umask 022
113 cd %{_topdir}/BUILD
114);
115
116# Package data
117# This is the form of $pkgdata{pkgname}{meta}
118# meta includes Summary, Name, Version, Release, Group, Copyright,
119# Source, URL, Packager, BuildRoot, Description, BuildReq(uires),
120# Requires, Provides
121# 10/31/2005 Maybe this should be flatter? -kgd
122my %pkgdata;
123my @pkglist = ('main'); #sigh
124# Files listing. Embedding this in %pkgdata would be, um, messy.
125my %filelist;
126my $buildreq = '';
127
128# Scriptlets
129my $prepscript = '';
130my $buildscript = '';
131# %install doesn't need the full treatment from %clean; just an empty place to install to.
132# NB - rpm doesn't do this; is it really necessary?
133my $installscript = '[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT'."\n";
134my $cleanscript = '';
135
136die "Not enough arguments\n" if #$argv == 0;
137
138# Snag some environment data
139my $tmpdir;
140if (defined $ENV{TMP} && $ENV{TMP} =~ /^(\/var)?\/tmp$/) {
141 $tmpdir = $ENV{TMP};
142} else {
143 $tmpdir = "/var/tmp";
144}
145
146##main
147
148load_userconfig();
149parse_cmd();
150
151if ($cmdopts{install}) {
152 install_sdeb();
153 exit 0;
154}
155
156# output stage of --showpkgs
157if ($cmdopts{type} eq 'd') {
158 parse_spec();
159 foreach my $pkg (@pkglist) {
160 $pkgdata{$pkg}{name} =~ tr/_/-/;
161
162 my $pkgfullname = "$pkgdata{$pkg}{name}_$pkgdata{$pkg}{version}-$pkgdata{main}{release}_$pkgdata{$pkg}{arch}.deb";
163
164 print "$pkgfullname\n" if $filelist{$pkg};
165
166 }
167 # Source package
168 print "$pkgdata{main}{name}-$pkgdata{main}{version}-$pkgdata{main}{release}.sdeb\n";
169 exit 0;
170}
171
172# Stick --rebuild handling in here - basically install_sdeb()
173# followed by tweaking options to run with -ba
174if ($cmdopts{type} eq 's') {
175 if ($srcpkg =~ /\.src\.rpm$/) {
176 my @srclist = qx { rpm -qlp $srcpkg };
177 foreach (@srclist) {
178 chomp;
179 $specfile = "$topdir/SPECS/$_" if /\.spec$/;
180 }
181 qx { rpm -i $srcpkg };
182 } else {
183 install_sdeb();
184 my @srclist = qx { pax < $srcpkg };
185 foreach (@srclist) {
186 chomp;
187 $specfile = "$topdir/$_" if /SPECS/;
188 }
189 }
190 $cmdopts{type} = 'b';
191 $cmdopts{stage} = 'a';
192}
193
194if ($cmdopts{type} eq 'b') {
195 # Need to read the spec file to find the tarball. Note that
196 # this also generates most of the shell script required.
197 parse_spec();
198 die "Can't build $pkgdata{main}{name}: build requirements not met.\n"
199 if !checkbuildreq();
200}
201
202if ($cmdopts{type} eq 't') {
203 # Need to unpack the tarball to find the spec file. Sort of the inverse of -b above.
204 # zcat $tarball |tar -t |grep .spec
205 # collect some info about the tarball
206 $specfile = "$topdir/BUILD/". qx { zcat $tarball |tar -t |grep .spec\$ };
207 chomp $specfile;
208 my ($fileonly, $dirname) = ($tarball =~ /(([a-zA-Z0-9._-]+)\.tar\.(?:gz|bz2))$/);
209
210 $tarball = abs_path($tarball);
211 my $unpackcmd = "cd $topdir/BUILD; tar -".
212 ( $tarball =~ /\.tar\.gz$/ ? "z" : "" ).
213 ( $tarball =~ /\.tar\.bz2$/ ? "j" : "" ). "xf $tarball";
214 system "$unpackcmd";
215 system "cp $tarball $topdir/SOURCES/$fileonly";
216 system "cp $specfile $topdir/SPECS/";
217 parse_spec();
218 die "Can't build $pkgdata{main}{name}: build requirements not met.\n"
219 if !checkbuildreq();
220}
221
222# -> srcpkg if -.s
223if ($cmdopts{stage} eq 's') {
224 srcpackage();
225 exit 0;
226}
227
228# Hokay. Need to:
229# -> prep if -.p OR (-.[cilabs] AND !--short-circuit)
230if ($cmdopts{stage} eq 'p' || ($cmdopts{stage} =~ /[cilabs]/ && $cmdopts{short} ne 'y')) {
231 prep();
232}
233# -> build if -.c OR (-.[ilabs] AND !--short-circuit)
234if ($cmdopts{stage} eq 'c' || ($cmdopts{stage} =~ /[ilabs]/ && $cmdopts{short} ne 'y')) {
235 build();
236}
237# -> install if -.[ilabs]
238#if ($cmdopts{stage} eq 'i' || ($cmdopts{stage} =~ /[labs]/ && $cmdopts{short} ne 'y')) {
239if ($cmdopts{stage} =~ /[ilabs]/) {
240 install();
241#foreach my $pkg (@pkglist) {
242# print "files in $pkg:\n ".$filelist{$pkg}."\n";
243#}
244
245}
246# -> binpkg and srcpkg if -.a
247if ($cmdopts{stage} eq 'a') {
248 binpackage();
249 srcpackage();
250}
251# -> binpkg if -.b
252if ($cmdopts{stage} eq 'b') {
253 binpackage();
254}
255
256# Spit out any closing remarks
257print $finalmessages;
258
259# Just in case.
260exit 0;
261
262
263## load_userconfig()
264# Loads user configuration (if any)
265# Currently only handles .debmacros
266# Needs to handle "other files"
267sub load_userconfig {
268 my $homedir = (getpwuid($<))[7];
269 if (-e "$homedir/.debmacros") {
270 open USERMACROS,"<$homedir/.debmacros";
271 while (<USERMACROS>) {
272 # And we also only handle a few macros at the moment.
273 if (/^\%_topdir/) {
274 my (undef,$tmp) = split /\s+/, $_;
275 $topdir = $tmp;
276 }
277 next if /^\%_/;
278 # Allow arbitrary definitions. Note that we're only doing simple defs here for now.
279 if (/^\%([a-z0-9]+)\s+(.+)$/) {
280 $specglobals{$1} = $2;
281 }
282 }
283 }
284} # end load_userconfig()
285
286
287## parse_cmd()
288# Parses command line into global hash %cmdopts, other globals
289# Options based on rpmbuild's options
290sub parse_cmd {
291 # Don't feel like coding my own option parser...
292 #use Getopt::Long;
293 # ... but I may have to: (OTOH, rpm uses popt, so maybe we can too.)
294 #use Getopt::Popt qw(:all);
295 # Or not. >:( Stupid Debian lack of findable Perl module names in packages.
296
297 # Stuff it.
298 my $prevopt = '';
299 foreach (@ARGV) {
300 chomp;
301
302 # Is it an option?
303 if (/^-/) {
304
305 # Is it a long option?
306 if (/^--/) {
307 if (/^--short-circuit/) {
308 $cmdopts{short} = 'y';
309 } elsif (/^--rebuild/) {
310 $cmdopts{type} = 's';
311 } elsif (/^--showpkgs/) {
312 $cmdopts{type} = 'd'; # d for 'diagnostic' or 'debug' or 'dump'
313 } elsif (/^--define/) {
314 # nothing to do? Can't see anything needed, we handle the actual definition later.
315 } else {
316 print "Long option $_ not handled\n";
317 }
318 } else {
319 # Not a long option
320 if (/^-[bt]/) {
321 if ($cmdopts{stage} eq 's') {
322 # Mutually exclusive options.
323 die "Can't use $_ with --rebuild\n";
324 } else {
325 # Capture the type (from "bare" files or tarball) and the stage (prep, build, etc)
326 ($cmdopts{stage}) = (/^-[bt]([pcilabs])/);
327 ($cmdopts{type}) = (/^-([bt])[pcilabs]/);
328 }
329 } elsif (/^-v/) {
330 # bump verbosity. Not sure what I'll actually do here...
331 } elsif (/^-i/) {
332 $cmdopts{install} = 1;
333 $prevopt = '-i';
334 } else {
335 die "Bad option $_\n";
336 }
337 }
338
339 } else { # Not an option argument
340
341 # --buildroot is the only option that takes an argument
342 # Therefore, any *other* bare arguments are the spec file,
343 # tarball, or source package we're operating on - depending
344 # on which one we meet.
345 if ($prevopt eq '--buildroot') {
346 $cmdbuildroot = $_;
347 } elsif ($prevopt eq '--define') {
348 my ($macro,$value) = (/([a-z0-9_.-]+)(?:\s+(.+))?/);
349 if ($value) {
350 $specglobals{$macro} = $value;
351 } else {
352 warn "WARNING: missing value for macro $macro in --define! Ignoring.\n";
353 }
354 } elsif ($prevopt eq '-i') {
355 $srcpkg = $_;
356 } else {
357 if ($cmdopts{type} eq 's') {
358 # Source package
359 if (!/(sdeb|\.src\.rpm)$/) {
360 die "Can't --rebuild with $_\n";
361 }
362 $srcpkg = $_;
363 } elsif ($cmdopts{type} eq 'b' || $cmdopts{type} eq 'd') {
364 # Spec file
365 $specfile = $_;
366 } else {
367 # Tarball build. Need to extract tarball to find spec file. Whee.
368 $tarball = $_;
369 }
370 }
371 }
372 $prevopt = $_;
373 } # foreach @ARGV
374
375 # Some cross-checks. rpmbuild limits --short-circuit to just
376 # the "compile" and "install" targets - with good reason IMO.
377 # Note that --short-circuit with -.p is not really an error, just redundant.
378 # NB - this is NOT fatal, just ignored!
379 if ($cmdopts{short} eq 'y' && $cmdopts{stage} =~ /[labs]/) {
380 warn "Can't use --short-circuit for $targets{$cmdopts{stage}} stage. Ignoring.\n";
381 $cmdopts{short} = 'n';
382 }
383
384 # Valid options, with example arguments (if any):
385# Build from .spec file; mutually exclusive:
386 # -bp
387 # -bc
388 # -bi
389 # -bl
390 # -ba
391 # -bb
392 # -bs
393# Build from tarball; mutually exclusive:
394 # -tp
395 # -tc
396 # -ti
397 # -ta
398 # -tb
399 # -ts
400# Build from .src.(deb|rpm)
401 # --rebuild
402 # --recompile
403
404# General options
405 # --buildroot=DIRECTORY
406 # --clean
407 # --nobuild
408 # --nodeps
409 # --nodirtokens
410 # --rmsource
411 # --rmspec
412 # --short-circuit
413 # --target=CPU-VENDOR-OS
414
415 #my $popt = new Getopt::Popt(argv => \@ARGV, options => \@optionsTable);
416
417} # end parse_cmd()
418
419
420## parse_spec()
421# Parse the .spec file.
422sub parse_spec {
423 open SPECFILE,"<$specfile" or die "specfile ($specfile) barfed: $!";
424
425 my $iflevel = 0;
426 my $buildarch = $hostarch;
427 $pkgdata{main}{arch} = $hostarch;
428
429LINE: while (<SPECFILE>) {
430 next if /^#/; # Ignore comments...
431 next if /^\s*$/; # ... and blank lines.
432
433 if (/^\%/) {
434 # A macro that needs further processing.
435
436 if (my ($key, $def) = (/^\%define\s+([^\s]+)\s+(.+)$/) ) {
437 $specglobals{$key} = expandmacros($def,'g');
438 }
439
440 if (/^\%if/) {
441 s/^\%if//;
442 my $expr = expandmacros($_, 'g');
443 $iflevel++;
444 next LINE if $expr != 0; # This appears to be the only case we call false.
445 while (<SPECFILE>) {
446 if (/^\%endif/) {
447 $iflevel--;
448 next LINE;
449 } elsif (/^\%else/) {
450 next LINE;
451 }
452 }
453 }
454 if (/^\%else/) {
455 while (<SPECFILE>) {
456 if (/^\%endif/) {
457 $iflevel--;
458 next LINE;
459 }
460 }
461 }
462 if (/^\%endif/) {
463 $iflevel--;
464 next LINE;
465 }
466
467 if (/^\%description(?:\s+(?:-n\s+)?(.+))?/) {
468 my $subname = "main";
469 if ($1) {
470 my $tmp = expandmacros("$1", 'g');
471 if (/-n/) { $subname = $tmp; } else { $subname = "$pkgdata{main}{name}-$tmp"; }
472 }
473 while (<SPECFILE>) {
474 next if /^#/; # Messy. Should be possible to do better. :/
475 redo LINE if /^\%/;
476 $pkgdata{$subname}{desc} .= " $_";
477 }
478 }
479 if (/^\%package\s+(?:-n\s+)?(.+)/) {
480 # gotta expand %defines here. Whee.
481 my $subname = expandmacros("$1", 'g');
482 if (! /-n/) { $subname = "$pkgdata{main}{name}-$1"; }
483 push @pkglist, $subname;
484 $pkgdata{$subname}{name} = $subname;
485 $pkgdata{$subname}{version} = $pkgdata{main}{version};
486 # Build "same arch as previous package found" by default. Where rpm just picks the
487 # *very* last one, we want to allow arch<native>+arch-all
488 # (eg, Apache is i386, but apache-manual is all)
489 $pkgdata{$subname}{arch} = $buildarch; # Since it's likely subpackages will NOT have a BuildArch line...
490 while (<SPECFILE>) {
491 redo LINE if /^\%/;
492 if (my ($dname,$dvalue) = (/^(Summary|Group|Version|Requires|Provides|BuildArch(?:itecture)?):\s+(.+)$/i)) {
493 $dname =~ tr/[A-Z]/[a-z]/;
494 if ($dname =~ /^BuildArch/i) {
495 $dvalue =~ s/^noarch/all/ig;
496 $buildarch = $dvalue; # Emulate rpm's behaviour to a degree
497 $dname = 'arch';
498 }
499 $pkgdata{$subname}{$dname} = expandmacros($dvalue, 'gp');
500 }
501 }
502 }
503
504##fixme: need to allow %if contructs to work in subsections like %prep
505 if (/^\%prep/) {
506 # %prep section. May have %setup macro; may include %patch tags,
507 # may be just a bare shell script.
508
509 # This really should be local-ish, but we need just the filename for the source
510 $pkgdata{main}{source} =~ s|.+/([^/]+)$|$1|;
511
512 # Replace some core macros
513 $pkgdata{main}{source} = expandmacros($pkgdata{main}{source},'gp');
514
515PREPSCRIPT: while (<SPECFILE>) {
516 if (/^\%setup/) {
517 # Parse out the %setup macro. Note that we aren't supporting
518 # many of RPM's %setup features.
519 $prepscript .= "cd $topdir/BUILD\n";
520 if ( /\s+-n\s+([^\s]+)\s+/ ) {
521 $tarballdir = $1;
522 }
523 $tarballdir = expandmacros($tarballdir,'gp');
524 $prepscript .= "rm -rf $tarballdir\n";
525 if (/\s+-c\s+/) {
526 $prepscript .= "mkdir $tarballdir\ncd $tarballdir\n";
527 }
528 $prepscript .= "tar -".
529 ( $pkgdata{main}{source} =~ /\.tar\.gz$/ ? "z" : "" ).
530 ( $pkgdata{main}{source} =~ /\.tar\.bz2$/ ? "j" : "" ).
531 ( /\s+-q\s+/ ? '' : 'vv' )."xf ".
532 "$topdir/SOURCES/$pkgdata{main}{source}\n".
533 qq(STATUS=\$?\nif [ \$STATUS -ne 0 ]; then\n exit \$STATUS\nfi\n).
534 "cd $topdir/BUILD/$tarballdir\n".
535 qq([ `/usr/bin/id -u` = '0' ] && /bin/chown -Rhf root .\n).
536 qq([ `/usr/bin/id -u` = '0' ] && /bin/chgrp -Rhf root .\n).
537 qq(/bin/chmod -Rf a+rX,g-w,o-w .\n);
538 } elsif ( my ($patchnum,$patchopts) = (/^\%patch([^\s]+)(\s+.+)?$/) ) {
539 chomp $patchnum;
540 $prepscript .= qq(echo "Patch #$patchnum ($pkgdata{main}{"patch$patchnum"}):"\n).
541 "patch ";
542 # If there are options passed, use'em.
543 # Otherwise, catch a bare %patch and ASS-U-ME it's '-p0'-able.
544 # Will break on options that don't provide -pnn, but what the hell.
545 $prepscript .= $patchopts if $patchopts;
546 $prepscript .= "-p0" if !$patchopts;
547 $prepscript .= " -s <$topdir/SOURCES/".$pkgdata{main}{"patch$patchnum"}."\n";
548 } else {
549 last PREPSCRIPT if /^\%/;
550 $prepscript .= $_;
551 }
552 }
553 redo LINE;
554 }
555 if (/^\%build/) {
556 # %build. This is pretty much just a shell script. There
557 # *are* a few macros, but we're not going to deal with them yet.
558 $buildscript .= "cd $tarballdir\n";
559BUILDSCRIPT: while (<SPECFILE>) {
560 if (/^\%configure/) {
561 $buildscript .= expandmacros($_,'cgbp');
562 } elsif (/^\%\{__make\}/) {
563 $buildscript .= expandmacros($_,'mgbp');
564 } else {
565 last BUILDSCRIPT if /^\%[^{]/;
566 $buildscript .= $_;
567 }
568 }
569 redo LINE;
570 }
571 if (/^\%install/) {
572 $installscript .= "cd $tarballdir\n";
573INSTALLSCRIPT: while (<SPECFILE>) {
574 if (/^\%makeinstall/) {
575 $installscript .= expandmacros($_,'igbp');
576 } else {
577 last INSTALLSCRIPT if /^\%/;
578 $installscript .= $_;
579 }
580 }
581 redo LINE;
582 }
583 if (/^\%clean/) {
584 while (<SPECFILE>) {
585 redo LINE if /^\%/;
586 $cleanscript .= $_;
587 }
588 $cleanscript = expandmacros($cleanscript,'gp');
589 }
590
591 # pre/post (un)install scripts. Note that we expand macros later anyway, so we'll leave them unexpanded here.
592 if (/^\%(pre|post|preun|postun)\b(?:\s+(?:-n\s+)?(.+))?/i) {
593 my $scriptlet = lc $1;
594 my $pkgname = 'main';
595 if ($2) { # Magic to add entries to the right list of files
596 my $tmp = expandmacros("$2", 'g');
597 if (/-n/) { $pkgname = $tmp; } else { $pkgname = "$pkgdata{main}{name}-$tmp"; }
598 }
599 while (<SPECFILE>) {
600 redo LINE if /^\%/;
601 $pkgdata{$pkgname}{$scriptlet} .= $_;
602 }
603 }
604 # done %pre/%post scripts
605
606 if (/^\%files(?:\s+(?:-n\s+)?(.+))?/) {
607 my $pkgname = 'main';
608 if ($1) { # Magic to add entries to the right list of files
609 my $tmp = expandmacros("$1", 'g');
610 if (/-n/) { $pkgname = $tmp; } else { $pkgname = "$pkgdata{main}{name}-$tmp"; }
611 }
612
613 # Set this now, so it can be flipped a bit later, and used much later.
614 #$pkgdata{$pkgname}{conffiles} = 0;
615
616 while (<SPECFILE>) {
617 chomp;
618 next if /^#/;
619 # need to update this to deal (properly) with %dir, %attr, etc
620 next if /^\%dir/;
621 next if /^\%defattr/;
622
623 # Debian dpkg doesn't speak "%docdir". Meh.
624 next if /^\%docdir/;
625
626##fixme
627# Note that big chunks of this section don't match rpm's behaviour; among other things,
628# rpm accepts more than one %-directive on one line for a file or set of files.
629 # make sure files get suitable permissions and so on
630 if (/^\%attr/) {
631 # We're going to collapse whitespace before processing. PTHBT.
632 # While this breaks pathnames with spaces, anyone expecting command-line
633 # tools with spaces to work (never mind work *properly* or *well*) under
634 # any *nix has their head so far up their ass they can see out their mouth.
635 my ($args,$filelist) = split /\)/;
636 $filelist{$pkgname} .= " $filelist";
637 $args =~ s/\s+//g;
638 $args =~ s/"//g; # don't think quotes are ever necessary, but they're *allowed*
639 my ($perms,$owner,$group) = ($args =~ /\(([\d-]+),([a-zA-Z0-9-]+),([a-zA-Z0-9-]+)/);
640# due to Debian's total lack of real permissions-processing in its actual package
641# handling component (dpkg-deb), this can't really be done "properly". We'll have
642# to add chown/chmod commands to the postinst instead. Feh.
643 $pkgdata{$pkgname}{'post'} .= "chown $owner $filelist\n" if $owner ne '-';
644 $pkgdata{$pkgname}{'post'} .= "chgrp $group $filelist\n" if $group ne '-';
645 $pkgdata{$pkgname}{'post'} .= "chmod $perms $filelist\n" if $perms ne '-';
646 next;
647 }
648
649 # %doc needs extra processing, because it can be a space-separated list.
650 if (/^\%doc/) {
651 s/^\%doc\s+//;
652 foreach (split()) {
653 $filelist{$pkgname} .= " %{_docdir}/$_";
654 }
655 next;
656 }
657
658 # Conffiles. Note that Debian and RH have similar, but not
659 # *quite* identical ideas of what constitutes a conffile. Nrgh.
660 if (/^\%config\s+(.+)$/) {
661 $pkgdata{$pkgname}{conffiles} = 1; # Flag it for later
662 my $tmp = $1; # Now we can mangleificationate it. And we probably need to. :/
663 $tmp = expandmacros($tmp, 'gp'); # Expand common macros
664 if ($tmp !~ /\s+/) {
665 # Simplest case, just a file. Whew.
666 push @{$pkgdata{$pkgname}{conflist}}, $tmp;
667 $filelist{$pkgname} .= " $tmp";
668 } else {
669 # Wot? Spaces? That means extra %-macros. Which, for the most part, can be ignored.
670 ($tmp) = ($tmp =~ /.+\s([^\s]+)/); # Strip everything before the last space
671 push @{$pkgdata{$pkgname}{conflist}}, $tmp;
672 $filelist{$pkgname} .= " $tmp";
673 }
674 next;
675 }
676
677 # and finally we can fall through %{_<FHS>}-prefixed locations...
678 if (/^\%\{_/) {
679 $filelist{$pkgname} .= " $_";
680 next;
681 }
682 # EW. Necessary to clear up %define expansions before we exit with redo.
683 $_ = expandmacros $_, 'g';
684
685 # ... unknown or "next section" % directives ...
686 redo LINE if /^\%/;
687
688 # ... and "normal" files
689 $filelist{$pkgname} .= " $_";
690 }
691 } # done %file section
692
693 if (/^\%changelog/) {
694 $pkgdata{main}{changelog} = '';
695 while (<SPECFILE>) {
696 redo LINE if /^\%/;
697 $pkgdata{main}{changelog} .= $_;
698 }
699 }
700# don't add any segments after this (%changelog), because something in the (Perl) parser gets
701# thoroughly confuzzled if it tries to manipulate file data after EOL. Feh.
702
703 } else { # Data from the spec file "header"
704
705 if (/^summary:\s+(.+)/i) {
706 $pkgdata{main}{summary} = $1;
707 } elsif (/^name:\s+(.+)/i) {
708 $pkgdata{main}{name} = expandmacros($1,'g');
709 } elsif (/^version:\s+(.+)/i) {
710 $pkgdata{main}{version} = expandmacros($1,'g');
711 } elsif (/^release:\s+(.+)/i) {
712 $pkgdata{main}{release} = expandmacros($1,'g');
713 } elsif (/^group:\s+(.+)/i) {
714 $pkgdata{main}{group} = $1;
715 } elsif (/^copyright:\s+(.+)/i) {
716 $pkgdata{main}{copyright} = $1;
717 } elsif (/^url:\s+(.+)/i) {
718 $pkgdata{main}{url} = $1;
719 } elsif (/^packager:\s+(.+)/i) {
720 $pkgdata{main}{packager} = $1;
721 } elsif (/^buildroot:\s+(.+)/i) {
722 $buildroot = $1;
723 } elsif (/^source0?:\s+(.+)/i) {
724 $pkgdata{main}{source} = $1;
725 die "Unknown tarball format $1\n" if $1 !~ /\.tar\.(?:gz|bz2)$/;
726 } elsif (/^source([0-9]+):\s+(.+)/i) {
727 $pkgdata{sources}{$1} = $2;
728 } elsif (/^patch([^:]+):\s+(.+)$/i) {
729 my $patchname = "patch$1";
730 $pkgdata{main}{$patchname} = $2;
731 if ($pkgdata{main}{$patchname} =~ /\//) {
732 # URL-style patch. Rare but not unheard-of.
733 my @patchbits = split '/', $pkgdata{main}{$patchname};
734 $pkgdata{main}{$patchname} = $patchbits[$#patchbits];
735 }
736 chomp $pkgdata{main}{$patchname};
737 } elsif (/^buildarch(?:itecture)?:\s+(.+)/i) {
738 $pkgdata{main}{arch} = $1;
739 $pkgdata{main}{arch} =~ s/^noarch$/all/;
740 $buildarch = $pkgdata{main}{arch};
741 } elsif (/^buildreq(?:uires)?:\s+(.+)/i) {
742 $buildreq .= ", $1";
743 } elsif (/^requires:\s+(.+)/i) {
744 $pkgdata{main}{requires} .= ", ".expandmacros("$1", 'gp');
745 } elsif (/^provides:\s+(.+)/i) {
746 $pkgdata{main}{provides} .= ", $1";
747 } elsif (/^conflicts:\s+(.+)/i) {
748 $pkgdata{main}{conflicts} .= ", $1";
749 }
750#Name: suwrap
751#Version: 0.04
752#Release: 3
753#Group: Applications/System
754#Copyright: WebHart internal ONLY. :(
755#BuildArchitectures: i386
756#BuildRoot: /tmp/%{name}-%{version}
757#Url: http://virtual.webhart.net
758#Packager: Kris Deugau <kdeugau@deepnet.cx>
759#Source: ftp://virtual.webhart.net/%{name}-%{version}.tar.gz
760
761 }
762 }
763
764 # Parse and replace some more macros. More will be replaced even later.
765
766 # Expand macros as necessary.
767 $scriptletbase = expandmacros($scriptletbase,'gp');
768
769 $buildroot = $cmdbuildroot if $cmdbuildroot;
770 $buildroot = expandmacros($buildroot,'gp');
771
772 close SPECFILE;
773} # end parse_spec()
774
775
776## prep()
777# Writes and executes the %prep script (mostly) built while reading the spec file.
778sub prep {
779 # Replace some things here just to make sure.
780 $prepscript = expandmacros($prepscript,'gp');
781
782 # create script filename
783 my $prepscriptfile = "$tmpdir/deb-tmp.prep.".int(rand(99998)+1);
784 sysopen(PREPSCRIPT, $prepscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
785 or die $!;
786 print PREPSCRIPT $scriptletbase;
787 print PREPSCRIPT $prepscript;
788 close PREPSCRIPT;
789
790 # execute
791 print "Calling \%prep script $prepscriptfile...\n";
792 system("/bin/sh -e $prepscriptfile") == 0
793 or die "Can't exec: $!\n";
794
795 # and clean up
796 unlink $prepscriptfile;
797} # end prep()
798
799
800## build()
801# Writes and executes the %build script (mostly) built while reading the spec file.
802sub build {
803 # Expand the macros
804 $buildscript = expandmacros($buildscript,'cgbp');
805
806 # create script filename
807 my $buildscriptfile = "$tmpdir/deb-tmp.build.".int(rand(99998)+1);
808 sysopen(BUILDSCRIPT, $buildscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
809 or die $!;
810 print BUILDSCRIPT $scriptletbase;
811 print BUILDSCRIPT $buildscript;
812 close BUILDSCRIPT;
813
814 # execute
815 print "Calling \%build script $buildscriptfile...\n";
816 system("/bin/sh -e $buildscriptfile") == 0
817 or die "Can't exec: $!\n";
818
819 # and clean up
820 unlink $buildscriptfile;
821} # end build()
822
823
824## install()
825# Writes and executes the %install script (mostly) built while reading the spec file.
826sub install {
827 # Expand the macros
828 $installscript = expandmacros($installscript,'igbp');
829
830 # create script filename
831 my $installscriptfile = "$tmpdir/deb-tmp.inst.".int(rand(99998)+1);
832 sysopen(INSTSCRIPT, $installscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
833 or die $!;
834 print INSTSCRIPT $scriptletbase;
835 print INSTSCRIPT $installscript;
836 close INSTSCRIPT;
837
838 # execute
839 print "Calling \%install script $installscriptfile...\n";
840 system("/bin/sh -e $installscriptfile") == 0
841 or die "Can't exec: $!\n";
842
843 # and clean up
844 unlink $installscriptfile;
845} # end install()
846
847
848## binpackage()
849# Creates the binary .deb package from the installed tree in $buildroot.
850# Writes and executes a shell script to do so.
851# Creates miscellaneous files required by dpkg-deb to actually build the package file.
852# Should handle simple subpackages
853sub binpackage {
854
855 foreach my $pkg (@pkglist) {
856
857 $pkgdata{$pkg}{arch} = $hostarch if !$pkgdata{$pkg}{arch}; # Just In Case.
858
859 # Make sure we have somewhere to write the .deb file
860 if (!-e "$topdir/DEBS/$pkgdata{$pkg}{arch}") {
861 mkdir "$topdir/DEBS/$pkgdata{$pkg}{arch}";
862 }
863
864 # Skip building a package if it doesn't actually have any files. NB: This
865 # differs slightly from rpm's behaviour where a package *will* be built -
866 # even without any files - if %files is specified anywhere. I can think
867 # of odd corner cases where that *may* be desireable.
868 next if (!$filelist{$pkg} or $filelist{$pkg} =~ /^\s*$/);
869
870 # Gotta do this first, otherwise we don't have a place to move files from %files
871 mkdir "$buildroot/$pkg";
872
873 # Eliminate any lingering % macros
874 $filelist{$pkg} = expandmacros $filelist{$pkg}, 'g';
875
876 my @pkgfilelist = split ' ', $filelist{$pkg};
877 foreach my $pkgfile (@pkgfilelist) {
878 $pkgfile = expandmacros($pkgfile, 'gp');
879 my ($fpath,$fname) = ($pkgfile =~ m|(.+?/?)?([^/]+)$|); # We don't need $fname now, but we might.
880 qx { mkdir -p $buildroot/$pkg$fpath }
881 if $fpath && $fpath ne '';
882 qx { mv $buildroot$pkgfile $buildroot/$pkg$fpath };
883 }
884
885 # Get the "Depends" (Requires) a la RPM. Ish. We strip the leading
886 # comma and space here (if needed) in case there were "Requires" specified
887 # in the spec file - those would precede these.
888 $pkgdata{$pkg}{requires} .= getreqs("$buildroot/$pkg");
889
890 # magic needed to properly version dependencies...
891 # only provided deps will really be included
892 $pkgdata{$pkg}{requires} =~ s/^, //; # Still have to do this here.
893 $pkgdata{$pkg}{requires} =~ s/\s+//g;
894 my @deps = split /,/, $pkgdata{$pkg}{requires};
895 my $tmp = '';
896 foreach my $dep (@deps) {
897 # Hack up the perl(Class::SubClass) deps into something dpkg can understand.
898 # May or may not be versioned.
899 # We do this first so the version rewriter can do its magic next.
900 if (my ($mod,$ver) = ($dep =~ /^perl\(([A-Za-z0-9\:\-]+)\)([><=]+.+)?/) ) {
901 $mod =~ s/^perl\(//;
902 $mod =~ s/\)$//;
903 $mod =~ s/::/-/g;
904 $mod =~ tr/A-Z/a-z/;
905 $mod = "lib$mod-perl";
906 $mod .= $ver if $ver;
907 $dep = $mod;
908 }
909 if (my ($name,$rel,$value) = ($dep =~ /^([a-zA-Z0-9._-]+)([><=]+)([a-zA-Z0-9._-]+)$/)) {
910 $tmp .= ", $name ($rel $value)";
911 } else {
912 $tmp .= ", $dep";
913 }
914 }
915 ($pkgdata{$pkg}{requires} = $tmp) =~ s/^, //;
916
917 # Do this here since we're doing {depends}...
918 if (defined($pkgdata{$pkg}{provides})) {
919 $pkgdata{$pkg}{provides} =~ s/^, //;
920 $pkgdata{$pkg}{provides} = expandmacros($pkgdata{$pkg}{provides},'gp');
921 }
922 if (defined($pkgdata{$pkg}{conflicts})) {
923 $pkgdata{$pkg}{conflicts} =~ s/^, //;
924 $pkgdata{$pkg}{conflicts} = expandmacros($pkgdata{$pkg}{conflicts},'gp');
925 }
926
927 # Gotta do this next, otherwise the control file has nowhere to go. >:(
928 mkdir "$buildroot/$pkg/DEBIAN";
929
930 # Hack the filename for the package into a Debian-tool-compatible format. GRRRRRR!!!!!
931 # Have I mentioned I hate Debian Policy?
932 $pkgdata{$pkg}{name} =~ tr/_/-/;
933
934 # create script filename
935 my $debscriptfile = "$tmpdir/deb-tmp.pkg.".int(rand(99998)+1);
936 sysopen(DEBSCRIPT, $debscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
937 or die $!;
938 print DEBSCRIPT $scriptletbase;
939 print DEBSCRIPT "fakeroot dpkg-deb -b $buildroot/$pkg $topdir/DEBS/$pkgdata{$pkg}{arch}/".
940 "$pkgdata{$pkg}{name}_$pkgdata{$pkg}{version}-$pkgdata{main}{release}_$pkgdata{$pkg}{arch}.deb\n";
941 # %$&$%@#@@#%@@@ Debian and their horrible ugly package names. >:(
942 close DEBSCRIPT;
943
944 my $control = "Package: $pkgdata{$pkg}{name}\n".
945 "Version: $pkgdata{$pkg}{version}-$pkgdata{main}{release}\n".
946 "Section: $pkgdata{$pkg}{group}\n".
947 "Priority: optional\n".
948 "Architecture: $pkgdata{$pkg}{arch}\n".
949 "Maintainer: $pkgdata{main}{packager}\n".
950 ( $pkgdata{$pkg}{requires} ne '' ? "Depends: $pkgdata{$pkg}{requires}\n" : '' ).
951 ( defined($pkgdata{$pkg}{provides}) ? "Provides: $pkgdata{$pkg}{provides}\n" : '' ).
952 ( defined($pkgdata{$pkg}{conflicts}) ? "Conflicts: $pkgdata{$pkg}{conflicts}\n" : '' ).
953 "Description: $pkgdata{$pkg}{summary}\n";
954 $control .= "$pkgdata{$pkg}{desc}\n";
955
956 open CONTROL, ">$buildroot/$pkg/DEBIAN/control";
957 print CONTROL $control;
958 close CONTROL;
959
960 # Iff there are conffiles (as specified in the %files list(s), add'em
961 # in so dpkg-deb can tag them.
962 if ($pkgdata{$pkg}{conffiles}) {
963 open CONFLIST, ">$buildroot/$pkg/DEBIAN/conffiles";
964 foreach my $conffile (@{$pkgdata{$pkg}{conflist}}) {
965 print CONFLIST "$conffile\n";
966 }
967 close CONFLIST;
968 }
969
970 # found the point of scripts on subpackages.
971 if ($pkgdata{$pkg}{'pre'}) {
972 $pkgdata{$pkg}{'pre'} = expandmacros($pkgdata{$pkg}{'pre'},'gp');
973 open PREINST, ">$buildroot/$pkg/DEBIAN/preinst";
974 print PREINST "#!/bin/sh\nset -e\n\n";
975 print PREINST $pkgdata{$pkg}{'pre'};
976 close PREINST;
977 `chmod 0755 $buildroot/$pkg/DEBIAN/preinst`;
978 }
979 if ($pkgdata{$pkg}{'post'}) {
980 $pkgdata{$pkg}{'post'} = expandmacros($pkgdata{$pkg}{'post'},'gp');
981 open PREINST, ">$buildroot/$pkg/DEBIAN/postinst";
982 print PREINST "#!/bin/sh\nset -e\n\n";
983 print PREINST $pkgdata{$pkg}{'post'};
984 close PREINST;
985 `chmod 0755 $buildroot/$pkg/DEBIAN/postinst`;
986 }
987 if ($pkgdata{$pkg}{'preun'}) {
988 $pkgdata{$pkg}{'pre'} = expandmacros($pkgdata{$pkg}{'preun'},'gp');
989 open PREINST, ">$buildroot/$pkg/DEBIAN/prerm";
990 print PREINST "#!/bin/sh\nset -e\n\n";
991 print PREINST $pkgdata{$pkg}{'preun'};
992 close PREINST;
993 `chmod 0755 $buildroot/$pkg/DEBIAN/prerm`;
994 }
995 if ($pkgdata{$pkg}{'postun'}) {
996 $pkgdata{$pkg}{'postun'} = expandmacros($pkgdata{$pkg}{'postun'},'gp');
997 open PREINST, ">$buildroot/$pkg/DEBIAN/postrm";
998 print PREINST "#!/bin/sh\nset -e\n\n";
999 print PREINST $pkgdata{$pkg}{'postun'};
1000 close PREINST;
1001 `chmod 0755 $buildroot/$pkg/DEBIAN/postrm`;
1002 }
1003
1004 # execute
1005 print "Calling package creation script $debscriptfile for $pkgdata{$pkg}{name}...\n";
1006 system("/bin/sh -e $debscriptfile") == 0
1007 or die "Can't exec: $!\n";
1008
1009 $finalmessages .= "Wrote binary package ".
1010 "$pkgdata{$pkg}{name}_$pkgdata{$pkg}{version}-$pkgdata{main}{release}_$pkgdata{$pkg}{arch}.deb".
1011 " in $topdir/DEBS/$pkgdata{$pkg}{arch}\n";
1012 # and clean up
1013 unlink $debscriptfile;
1014
1015 } # subpackage loop
1016
1017} # end binpackage()
1018
1019
1020## srcpackage()
1021# Builds a .src.deb source package. Note that Debian's idea of
1022# a "source package" is seriously flawed IMO, because you can't
1023# easily copy it as-is.
1024# Not quite identical to RPM, but Good Enough (TM).
1025sub srcpackage {
1026 # In case we were called with -bs.
1027 $pkgdata{main}{name} =~ tr/_/-/;
1028 my $pkgsrcname = "$pkgdata{main}{name}-$pkgdata{main}{version}-$pkgdata{main}{release}.sdeb";
1029
1030 my $paxcmd;
1031
1032 # We'll definitely need this later, and *may* need it sooner.
1033 (my $barespec = $specfile) =~ s|.+/([^/]+)$|$1|;
1034
1035 # Copy the specfile to the build tree, but only if it's not there already.
1036##buglet: need to deal with silly case where silly user has put the spec
1037# file in a subdir of %{_topdir}/SPECS. Ewww. Silly user!
1038 if (abs_path($specfile) !~ /^$topdir\/SPECS/) {
1039 $paxcmd .= "cp $specfile %{_topdir}/SPECS/; \n"
1040 }
1041
1042 # use pax -w [file] [file] ... >outfile.sdeb
1043 $paxcmd = "cd $topdir; pax -w ";
1044
1045# tweak source entry into usable form. Need it locally somewhere along the line.
1046 (my $pkgsrc = $pkgdata{main}{source}) =~ s|.+/([^/]+)$|$1|;
1047 $paxcmd .= "SOURCES/$pkgsrc ";
1048
1049 # create file list: Source[nn], Patch[nn]
1050 foreach my $specbit (keys %{$pkgdata{main}} ) {
1051 next if $specbit eq 'source';
1052 $paxcmd .= "SOURCES/$pkgdata{main}{$specbit} " if $specbit =~ /^patch/;
1053##buglet: need to deal with case where patches are listed as URLs?
1054# or other extended pathnames? Silly !@$%^&!%%!%!! user!
1055 }
1056
1057 foreach my $source (keys %{$pkgdata{sources}}) {
1058 $paxcmd .= "SOURCES/$pkgdata{sources}{$source} ";
1059 }
1060
1061 # add the spec file, source package destination, and cd back where we came from.
1062 $paxcmd .= "SPECS/$barespec > $topdir/SDEBS/$pkgsrcname; cd -";
1063
1064 # In case of %-macros...
1065 $paxcmd = expandmacros($paxcmd,'gp');
1066
1067 system "$paxcmd";
1068 $finalmessages .= "Wrote source package $pkgsrcname in $topdir/SDEBS.\n";
1069}
1070
1071
1072## checkbuildreq()
1073# Checks the build requirements (if any)
1074# Spits out a rude warning and returns a true-false error if any
1075# requirements are not met.
1076sub checkbuildreq {
1077 return 1 if $buildreq eq ''; # No use doing extra work.
1078
1079 if ( ! -e "/usr/bin/dpkg-query" ) {
1080 print "**WARNING** dpkg-query not found. Can't check build-deps.\n".
1081 " Required for sucessful build:\n".$buildreq."\n".
1082 " Continuing anyway.\n";
1083 return 1;
1084 }
1085
1086 my $reqflag = 1; # unset iff a buildreq is missing
1087
1088 $buildreq =~ s/^, //; # Strip the leading comma and space
1089 my @reqlist = split /,\s+/, $buildreq;
1090
1091 foreach my $req (@reqlist) {
1092 my ($pkg,$rel,$ver);
1093
1094 # We have two classes of requirements - versioned and unversioned.
1095 if ($req =~ /[><=]/) {
1096 # Pick up the details of versioned buildreqs
1097 ($pkg,$rel,$ver) = ($req =~ /([a-z0-9._-]+)\s+([><=]+)\s+([a-z0-9._-]+)/);
1098 } else {
1099 # And the unversioned ones.
1100 $pkg = $req;
1101 $rel = '>=';
1102 $ver = 0;
1103 }
1104
1105 my @pkglist = qx { dpkg-query --showformat '\${status}\t\${version}\n' -W $pkg };
1106# need to check if no lines returned - means a bad buildreq
1107 my ($reqstat,undef,undef,$reqver) = split /\s+/, $pkglist[0];
1108 if ($reqstat !~ /install/) {
1109 print " * Missing build-dependency $pkg!\n";
1110 $reqflag = 0;
1111 } else {
1112# gotta be a better way to do this... :/
1113 if ($rel eq '>=' && !($reqver ge $ver)) {
1114 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
1115 $reqflag = 0;
1116 }
1117 if ($rel eq '>' && !($reqver gt $ver)) {
1118 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
1119 $reqflag = 0;
1120 }
1121 if ($rel eq '<=' && !($reqver le $ver)) {
1122 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
1123 $reqflag = 0;
1124 }
1125 if ($rel eq '<' && !($reqver lt $ver)) {
1126 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
1127 $reqflag = 0;
1128 }
1129 if ($rel eq '=' && !($reqver eq $ver)) {
1130 print " * Buildreq $pkg is installed, but wrong version ($reqver): Need $ver\n";
1131 $reqflag = 0;
1132 }
1133 } # end not installed/installed check
1134 } # end req loop
1135
1136 return $reqflag;
1137} # end checkbuildreq()
1138
1139
1140## getreqs()
1141# Find out which libraries/packages are required for any
1142# executables and libs in a given file tree.
1143# (Debian doesn't have soname-level deps; just package-level)
1144# Returns an empty string if the tree contains no binaries.
1145# Doesn't work well on shell scripts. but those *should* be
1146# fine anyway. (Yeah, right...)
1147sub getreqs() {
1148 my $pkgtree = $_[0];
1149
1150 print "Checking library requirements...\n";
1151 my @binlist = qx { find $pkgtree -type f -perm 755 };
1152
1153 if (scalar(@binlist) == 0) {
1154 return '';
1155 }
1156
1157 my @reqlist;
1158 foreach (@binlist) {
1159 push @reqlist, qx { ldd $_ };
1160 }
1161
1162 # Get the list of libs provided by this package. Still doesn't
1163 # handle the case where the lib gets stuffed into a subpackage. :/
1164 my @intprovlist = qx { find $pkgtree -type f -name "*.so*" };
1165 my $provlist = '';
1166 foreach (@intprovlist) {
1167 s/$pkgtree//;
1168 $provlist .= "$_";
1169 }
1170
1171 my %reqs;
1172 my $reqlibs = '';
1173
1174 foreach (@reqlist) {
1175 next if /^$pkgtree/;
1176 next if /not a dynamic executable/;
1177 next if m|/lib/ld-linux.so|; # Hack! Hack! PTHBTT! (libc suxx0rz)
1178
1179 my ($req) = (/^\s+([a-z0-9._-]+)/); # dig out the actual library (so)name
1180
1181 # Ignore libs provided by this package. Note that we don't match
1182 # on word-boundary at the *end* of the lib we're looking for, as the
1183 # looked-for lib may not have the full soname version. (ie, it may
1184 # "just" point to one of the symlinks that get created somewhere.)
1185 next if $provlist =~ /\b$req/;
1186
1187 $reqlibs .= " $req";
1188 }
1189
1190 if ($reqlibs ne '') {
1191 foreach (qx { dpkg -S $reqlibs }) {
1192 my ($libpkg,undef) = split /:\s+/;
1193 $reqs{$libpkg} = 1;
1194 }
1195 }
1196
1197 my $deplist = '';
1198 foreach (keys %reqs) {
1199 $deplist .= ", $_";
1200 }
1201
1202# For now, we're done. We're not going to meddle with versions yet.
1203# Among other things, it's messier than handling "simple" yes/no "do
1204# we have this lib?" deps. >:(
1205
1206 return $deplist;
1207} # end getreqs()
1208
1209
1210## install_sdeb()
1211# Extracts .sdeb contents to %_topdir as appropriate
1212sub install_sdeb {
1213 $srcpkg = abs_path($srcpkg);
1214
1215 my $paxcmd = "cd $topdir; pax -r <$srcpkg; cd -";
1216
1217 # In case of %-macros...
1218 $paxcmd = expandmacros($paxcmd,'gp');
1219
1220 system "$paxcmd";
1221 print "Extracted source package $srcpkg to $topdir.\n";
1222} # end install_sdeb()
1223
1224
1225## expandmacros()
1226# Expands all %{blah} macros in the passed string
1227# Split up a bit with some sections so we don't spend time trying to
1228# expand macros that are only used in a few specific places.
1229sub expandmacros {
1230 my $macrostring = shift;
1231 my $section = shift;
1232
1233 # To allow the FHS-ish %configure and %makeinstall to work The Right Way.
1234 # (Without clobbering the global $buildroot.)
1235 my $prefix = '';
1236
1237 if ($section =~ /c/) {
1238 # %configure macro
1239# Don't know what it's for, don't have a useful default replacement
1240# --program-prefix=%{_program_prefix} \
1241 $macrostring =~ s'%configure'./configure --host=$DEB_HOST_GNU_TYPE \
1242 --build=$DEB_BUILD_GNU_TYPE \
1243 --prefix=%{_prefix} \
1244 --exec-prefix=%{_exec_prefix} \
1245 --bindir=%{_bindir} \
1246 --sbindir=%{_sbindir} \
1247 --sysconfdir=%{_sysconfdir} \
1248 --datadir=%{_datadir} \
1249 --includedir=%{_includedir} \
1250 --libdir=%{_libdir} \
1251 --libexecdir=%{_libexecdir} \
1252 --localstatedir=%{_localstatedir} \
1253 --sharedstatedir=%{_sharedstatedir} \
1254 --mandir=%{_mandir} \
1255 --infodir=%{_infodir} ';
1256 } # done %configure
1257
1258 if ($section =~ /m/) {
1259 $macrostring =~ s'%{__make}'make ';
1260 } # done make
1261
1262 if ($section =~ /i/) {
1263 # This is where we need to mangle $prefix.
1264 $macrostring =~ s'%makeinstall'make %{fhs} install';
1265 $prefix = $buildroot;
1266 } # done %install and/or %makeinstall
1267
1268 # Build data
1269 # Note that these are processed in reverse order to get the substitution order right
1270 if ($section =~ /b/) {
1271# $macrostring =~ s'%{fhs}'host=$DEB_HOST_GNU_TYPE \
1272# build=$DEB_BUILD_GNU_TYPE \
1273 $macrostring =~ s'%{fhs}'prefix=%{_prefix} \
1274 exec-prefix=%{_exec_prefix} \
1275 bindir=%{_bindir} \
1276 sbindir=%{_sbindir} \
1277 sysconfdir=%{_sysconfdir} \
1278 datadir=%{_datadir} \
1279 includedir=%{_includedir} \
1280 libdir=%{_libdir} \
1281 libexecdir=%{_libexecdir} \
1282 localstatedir=%{_localstatedir} \
1283 sharedstatedir=%{_sharedstatedir} \
1284 mandir=%{_mandir} \
1285 infodir=%{_infodir} \
1286';
1287
1288 # Note that the above regex terminates with the extra space
1289 # "Just In Case" of user additions, which will then get neatly
1290 # tagged on the end where they take precedence (supposedly)
1291 # over the "default" ones.
1292
1293 # Now we cascade the macros introduced above. >_<
1294 # Wot ot to go theah:
1295 $macrostring =~ s|%{_mandir}|%{_datadir}/man|g; #/usr/share/man
1296 $macrostring =~ s|%{_infodir}|%{_datadir}/info|g; #/usr/share/info
1297 $macrostring =~ s|%{_oldincludedir}|/usr/include|g; #/usr/include
1298 $macrostring =~ s|%{_includedir}|%{_prefix}/include|g; #/usr/include
1299 $macrostring =~ s|%{_libdir}|%{_exec_prefix}/%{_lib}|g; #/usr/lib
1300 $macrostring =~ s|%{_lib}|lib|g; #?
1301 $macrostring =~ s|%{_localstatedir}|/var|g; #/var
1302 $macrostring =~ s|%{_sharedstatedir}|%{_prefix}/com|g; #/usr/com WTF?
1303 $macrostring =~ s|%{_sysconfdir}|/etc|g; #/etc
1304 $macrostring =~ s|%{_datadir}|%{_prefix}/share|g; #/usr/share
1305 $macrostring =~ s|%{_libexecdir}|%{_exec_prefix}/libexec|g; #/usr/libexec
1306 $macrostring =~ s|%{_sbindir}|%{_exec_prefix}/sbin|g; #/usr/sbin
1307 $macrostring =~ s|%{_bindir}|%{_exec_prefix}/bin|g; #/usr/bin
1308 $macrostring =~ s|%{_exec_prefix}|%{_prefix}|g; #/usr
1309 $macrostring =~ s|%{_prefix}|/usr|g; #/usr
1310 } # done with config section
1311
1312 # Package data
1313 if ($section =~ /p/) {
1314 $macrostring =~ s/\%\{buildroot\}/$buildroot/gi;
1315 foreach my $source (keys %{$pkgdata{sources}}) {
1316 $macrostring =~ s/\%\{source$source\}/$topdir\/SOURCES\/$pkgdata{sources}{$source}/gi;
1317 }
1318 $macrostring =~ s/\%\{name\}/$pkgdata{main}{name}/gi;
1319 $macrostring =~ s/\%\{version\}/$pkgdata{main}{version}/gi;
1320 $macrostring =~ s/\%\{release\}/$pkgdata{main}{release}/gi;
1321 }
1322
1323 # Globals, and not-so-globals
1324 if ($section =~ /g/) {
1325
1326 $macrostring =~ s|%{_builddir}|%{_topdir}/BUILD|g;
1327 $macrostring =~ s|%{_topdir}|$topdir|g;
1328 $macrostring =~ s|%{_tmppath}|$tmpdir|g;
1329 $macrostring =~ s'%{_docdir}'%{_datadir}/doc'g;
1330
1331 # Standard FHS locations. More or less.
1332 $macrostring =~ s'%{_bindir}'/usr/bin'g;
1333 $macrostring =~ s'%{_sbindir}'/usr/sbin'g;
1334 $macrostring =~ s'%{_mandir}'%{_datadir}/man'g;
1335 $macrostring =~ s'%{_includedir}'/usr/include'g;
1336 $macrostring =~ s'%{_libdir}'/usr/lib'g;
1337 $macrostring =~ s'%{_sysconfdir}'/etc'g;
1338 $macrostring =~ s'%{_localstatedir}'/var'g;
1339
1340 # FHS-ish locations that aren't quite actually FHS-specified.
1341 $macrostring =~ s'%{_datadir}'/usr/share'g;
1342
1343 # %define's
1344 foreach my $key (keys %specglobals) {
1345 $macrostring =~ s|%{$key}|$specglobals{$key}|g;
1346 }
1347
1348 # special %define's. Handle the general case where we eval anything.
1349 # Even more general: %(...) is a spec-parse-time shell code wrapper.
1350 # Prime example:
1351 #%define perl_vendorlib %(eval "`perl -V:installvendorlib`"; echo $installvendorlib)
1352 if ($macrostring =~ /\%\((.+)\)/) {
1353 my $shellstr = $1;
1354 # Oy vey this gets silly for the perl bits. Executing a shell to
1355 # call Perl to get the vendorlib/sitelib/whatever "core" globals.
1356 # This can do more, but... eww.
1357 $shellstr = qx { /bin/sh -c '$shellstr' }; # Yay! ' characters apparently get properly exscapededed.
1358 $macrostring =~ s/\%\(.+\)/$shellstr/;
1359 }
1360
1361 # support for **some** %if constructs. Note that this breaks somewhat if
1362 # there's no value specified... but so does rpm.
1363 while ($macrostring =~ /\%\{\?(\!)?([a-z0-9_.-]+)(?:\:([a-z0-9_.-]+))?\}/) { #Whew....
1364 my $neg = $1;
1365 my $macro = $2;
1366 my $value = $3;
1367 if ($specglobals{$macro}) {
1368 $value = '' if $neg;
1369 } else {
1370 $value = '' if !$neg;
1371 }
1372 $macrostring =~ s/\%\{\?\!?[a-z0-9_.-]+(?:\:[a-z0-9_.-]+)?\}/$value/;
1373 }
1374
1375 # system programs. RPM uses a global config file for these; we'll just
1376 # ASS-U-ME and make life a little simpler.
1377 if ($macrostring =~ /\%\{\_\_([a-z0-9_-]+)\}/) {
1378 $macrostring =~ s|%{__([a-z0-9_-]+)}|$1|g;
1379 }
1380
1381 # Misc expansions
1382 $macrostring =~ s|%{_arch}|$hostarch|g;
1383 $macrostring =~ s|%{optflags}|$optflags{$hostarch}|g;
1384
1385 } # done with globals section
1386
1387 return $macrostring;
1388} # end expandmacros()
1389
1390
1391
1392__END__
1393
1394
1395
1396=head1 NAME
1397
1398debbuild - Build Debian-compatible packages from RPM spec files
1399
1400=head1 SYNOPSIS
1401
1402 debbuild {-ba|-bb|-bp|-bc|-bi|-bl|-bs} [build-options] file.spec
1403
1404 debbuild {-ta|-tb|-tp|-tc|-ti|-tl|-ts} [build-options] file.tar.{gz|bz2}
1405
1406 debbuild --rebuild file.{src.rpm|sdeb}
1407
1408 debbuild --showpkgs
1409
1410=head1 DESCRIPTION
1411
1412This script attempts to build Debian-friendly semi-native packages from RPM spec files,
1413RPM-friendly tarballs, and RPM source packages (.src.rpm files). It accepts I<most> of the
1414options rpmbuild does, and should be able to interpret most spec files usefully. Perl
1415modules should be handled via CPAN+dh-make-perl instead; Debian's conventions for such
1416things do not lend themselves to automated conversion.
1417
1418As far as possible, the command-line options are identical to those from rpmbuild, although
1419several rpmbuild options are not supported:
1420
1421 --recompile
1422 --showrc
1423 --buildroot
1424 --clean
1425 --nobuild
1426 --rmsource
1427 --rmspec
1428 --sign
1429 --target
1430
1431Some of these could probably be trivially added. Feel free to send me a patch. ;)
1432
1433Complex spec files will most likely not work well, if at all. Rewrite them from scratch -
1434you'll have to make heavy modifications anyway.
1435
1436If you see something you don't like, mail me. Send a patch if you feel inspired. I don't
1437promise I'll do anything other than say "Yup, that's broken" or "Got your message".
1438
1439=head1 ASSUMPTIONS
1440
1441As with rpmbuild, debbuild makes some assumptions about your system.
1442
1443=over 4
1444
1445=item *
1446
1447Either you have rights to do as you please under /usr/src/debian, or you have created a file
1448~/.debmacros containing a suitable %_topdir definition.
1449
1450Both rpmbuild and debbuild require the directories %_topdir/{BUILD,SOURCES,SPECS}. However,
1451where rpmbuild requires the %_topdir/{RPMS,SRPMS} directories, debbuild
1452requires %_topdir/{DEBS,SDEBS} instead. Create them in advance;
1453some subdirectories are created automatically as needed, but most are not.
1454
1455=item *
1456
1457/var/tmp must allow script execution - rpmbuild and debbuild both rely on creating and
1458executing shell scripts for much of their functionality. By default, debbuild also creates
1459install trees under /var/tmp - however this is (almost) entirely under the control of the
1460package's .spec file.
1461
1462=item *
1463
1464If you wish to --rebuild a .src.rpm, your %_topdir for both debbuild and rpmbuild must either
1465match, or be suitably symlinked one direction or another so that both programs are effectively
1466working in the same tree. (Or you could just manually wrestle files around your system.)
1467
1468You could symlink ~/.rpmmacros to ~/.debmacros (or vice versa) and save yourself some hassle
1469if you need to rebuild .src.rpm packages on a regular basis. Currently debbuild only uses the
1470%_topdir macro definition, although there are many more things that rpmbuild can use from
1471~/.rpmmacros.
1472
1473=back
1474
1475=head1 ERRATA
1476
1477debbuild deliberately does a few things differently from rpm.
1478
1479=head2 BuildArch or BuildArchitecture
1480
1481rpm takes the last BuildArch entry it finds in the .spec file, whatever it is, and runs with
1482that for all packages. Debian's repository system is fairly heavily designed around the
1483assumption that a single source package may generate small binary (executable) packages
1484for each arch, and large binary arch-all packages containing shared data.
1485
1486debbuild allows this by using the architecture specified by (in order of preference):
1487
1488=over 4
1489
1490=item * Host architecture
1491
1492=item * BuildArch specified in .spec file preamble
1493
1494=item * "Last specified" BuildArch for packages with several subpackages
1495
1496=item * BuildArch specified in the %package section for that subpackage
1497
1498=back
1499
1500=head2 Finding out what packages should be built (--showpkgs)
1501
1502rpmbuild does not include any convenient method I know of to list the packages a spec file
1503will produce. Since I needed this ability for another tool, I added it.
1504
1505It requires the .spec file for the package, and produces a list of full package filenames
1506(without path data) that would be generated by one of --rebuild, -ta, -tb, -ba, or -bb.
1507This includes the .sdeb source package.
1508
1509=head1 AUTHOR
1510
1511debbuild was written by Kris Deugau <kdeugau@deepnet.cx>. A version that approximates
1512current is available at http://www.deepnet.cx/debbuild/.
1513
1514=head1 BUGS
1515
1516Funky Things Happen if you forget a command-line option or two. I've been too lazy to bother
1517fixing this.
1518
1519Many macro expansions are unsupported or incompletely supported.
1520
1521The generated scriptlets don't quite match those from rpmbuild exactly. There are extra
1522environment variables and preprocessing that I haven't needed (yet).
1523
1524Dcumentation, such as it is, will likely remain perpetually out of date.
1525
1526%_topdir and the five "working" directories under %_topdir could arguably be created by
1527debbuild. However, rpmbuild doesn't create these directories either.
1528
1529=head1 SEE ALSO
1530
1531rpm(8), rpmbuild(8), and pretty much any document describing how to write a .spec file.
1532
1533=cut
Note: See TracBrowser for help on using the repository browser.