source: trunk/debbuild@ 9

Last change on this file since 9 was 9, checked in by kdeugau, 19 years ago

/trunk

Bugfix - clean up creation of %_topdir/DEBS/i386 directory

  • Property svn:executable set to *
  • Property svn:keywords set to Date Rev Author
File size: 14.4 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: 2005-11-02 19:37:45 +0000 (Wed, 02 Nov 2005) $
9# SVN revision $Rev: 9 $
10# Last update by $Author: kdeugau $
11###
12
13use strict;
14use warnings;
15use Fcntl; # for sysopen flags
16
17# Program flow:
18# -> Parse/execute "system" config/macros (if any - should be rare)
19# -> Parse/execute "user" config/macros (if any - *my* requirement is %_topdir)
20# -> Parse command line for options, spec file/tarball/.src.deb (NB - also accept .src.rpm)
21
22# User's prefs for dirs, environment, etc,etc,etc.
23# config file ~/.debmacros
24# Default ordered search paths for config/macros:
25# /usr/lib/rpm/rpmrc /usr/lib/rpm/redhat/rpmrc /etc/rpmrc ~/.rpmrc
26# /usr/lib/rpm/macros /usr/lib/rpm/redhat/macros /etc/rpm/macros ~/.rpmmacros
27# **NOTE: May be possible to (ab)use bits of debhelper
28
29# Build tree
30# default is /usr/src/debian/{BUILD,SOURCES,SPECS,DEBS,SDEBS}
31
32# Globals
33my $specfile;
34my $tarball;
35my $srcpkg;
36my $cmdbuildroot;
37my $tarballdir; # This should really be initialized, but the coding makes it, um, ugly.
38
39# Initialized globals
40my $verbosity = 0;
41my %cmdopts = (type => '',
42 stage => 'a',
43 short => 'n'
44 );
45my $topdir = "/usr/src/debian";
46my $buildroot = "/var/tmp/%{name}-%{version}-%{release}.root".int(rand(99998)+1);
47
48# "Constants"
49my %targets = ('p' => 'Prep',
50 'c' => 'Compile',
51 'i' => 'Install',
52 'l' => 'Verify %files',
53 'a' => 'Build binary and source',
54 'b' => 'Build binary',
55 's' => 'Build source'
56 );
57my $scriptletbase =
58q(#!/bin/sh
59
60 RPM_SOURCE_DIR="%{_topdir}/SOURCES"
61 RPM_BUILD_DIR="%{_topdir}/BUILD"
62 RPM_OPT_FLAGS="-O2 -g -march=i386 -mcpu=i686"
63 RPM_ARCH="i386"
64 RPM_OS="linux"
65 export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS
66 RPM_DOC_DIR="/usr/share/doc"
67 export RPM_DOC_DIR
68 RPM_PACKAGE_NAME="%{name}"
69 RPM_PACKAGE_VERSION="%{version}"
70 RPM_PACKAGE_RELEASE="%{release}"
71 export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE
72 RPM_BUILD_ROOT="%{buildroot}"
73 export RPM_BUILD_ROOT
74
75 set -x
76 umask 022
77 cd %{_topdir}/BUILD
78);
79
80# Package data
81# This is the form of $pkgdata{pkgname}{meta}
82# meta includes Summary, Name, Version, Release, Group, Copyright,
83# Source, URL, Packager, BuildRoot, Description
84# 10/31/2005 Maybe this should be flatter? -kgd
85my %pkgdata;
86
87# Scriptlets
88my $prepscript;
89my $buildscript;
90my $installscript;
91my $cleanscript;
92
93# Functions
94sub prep;
95sub parse_spec;
96
97die "Not enough arguments\n" if #$argv == 0;
98
99##main
100
101load_userconfig();
102parse_cmd();
103
104if ($cmdopts{type} eq 'b') {
105 # Need to read the spec file to find the tarball. Note that
106 # this also generates most of the shell script required.
107 parse_spec();
108}
109
110# Hokay. Need to:
111# Execute prep if *not* --short-circuit
112if ($cmdopts{stage} eq 'p' || ($cmdopts{stage} =~ /[cilabs]/ && $cmdopts{short} ne 'y')) {
113 prep();
114}
115if ($cmdopts{stage} eq 'c' || ($cmdopts{stage} =~ /[ilabs]/ && $cmdopts{short} ne 'y')) {
116 build();
117}
118if ($cmdopts{stage} =~ /[ilabs]/) {
119 install();
120}
121if ($cmdopts{stage} eq 'a') {
122 binpackage();
123 srcpackage();
124}
125if ($cmdopts{stage} eq 'b') {
126 binpackage();
127}
128if ($cmdopts{stage} eq 's') {
129 srcpackage();
130}
131
132# Just in case.
133exit 0;
134
135
136## load_userconfig()
137# Loads user configuration (if any)
138# Currently only handles .debmacros
139# Needs to handle "other files"
140sub load_userconfig {
141 my (undef,undef,undef,undef,undef,undef,undef,$homedir,undef) = getpwuid($<);
142 if (-e "$homedir/.debmacros") {
143 open USERMACROS,"<$homedir/.debmacros";
144 while (<USERMACROS>) {
145 # And we also only handle a few macros at the moment.
146 if (/^\%_topdir/) {
147 my (undef,$tmp) = split /\s+/, $_;
148 $topdir = $tmp;
149 }
150 }
151 }
152} # end load_userconfig()
153
154
155## parse_cmd()
156# Parses command line into global hash %cmdopts, other globals
157# Options based on rpmbuild's options
158sub parse_cmd {
159 # Don't feel like coding my own option parser...
160 #use Getopt::Long;
161 # ... but I may have to: (OTOH, rpm uses popt, so maybe we can too.)
162 #use Getopt::Popt qw(:all);
163 # Or not. >:( Stupid Debian lack of findable Perl module names in packages.
164
165 # Stuff it.
166 my $prevopt = '';
167 foreach (@ARGV) {
168 chomp;
169
170 # Is it an option?
171 if (/^-/) {
172
173 # Is it a long option?
174 if (/^--/) {
175 if (/^--short-circuit/) {
176 $cmdopts{short} = 'y';
177 } elsif (/^--rebuild/) {
178 $cmdopts{type} = 's';
179 } else {
180 print "Long opt $_\n";
181 }
182 } else {
183 if (/^-[bt]/) {
184 if ($cmdopts{stage} eq 's') {
185 # Mutually exclusive options.
186 die "Can't use $_ with --rebuild\n";
187 } else {
188 ($cmdopts{stage}) = (/^-[bt]([pcilabs])/);
189 ($cmdopts{type}) = (/^-([bt])[pcilabs]/);
190 }
191 } elsif (/^-v/) {
192 # bump verbosity. Not sure what I'll actually do here...
193 } else {
194 die "Bad option $_\n";
195 }
196 }
197 } else {
198 # --buildroot is the only option that takes an argument
199 # Therefore, any *other* bare arguments are the spec file,
200 # tarball, or source package we're operating on - depending
201 # on which one we meet.
202 if ($prevopt eq '--buildroot') {
203 $cmdbuildroot = $_;
204 } else {
205 if ($cmdopts{type} eq 's') {
206 # Source package
207 if (!/\.src\.(deb|rpm)$/) {
208 die "Can't --rebuild with $_\n";
209 }
210 } elsif ($cmdopts{type} eq 'b') {
211 $specfile = $_;
212 # Spec file
213 } else {
214 # Tarball
215 }
216 }
217 }
218 $prevopt = $_;
219 } # foreach @ARGV
220
221 # Some cross-checks. rpmbuild limits --short-circuit to just
222 # the "compile" and "install" targets - with good reason IMO.
223 # NB - this is NOT fatal, just ignored!
224 if ($cmdopts{short} eq 'y') {
225 warn "Can't use --short-circuit for $targets{$cmdopts{stage}} stage. Ignoring.\n";
226 $cmdopts{short} = 'n';
227 }
228 # Valid options, with example arguments (if any):
229# Build from .spec file; mutually exclusive:
230 # -bp
231 # -bc
232 # -bi
233 # -bl
234 # -ba
235 # -bb
236 # -bs
237# Build from tarball; mutually exclusive:
238 # -tp
239 # -tc
240 # -ti
241 # -ta
242 # -tb
243 # -ts
244# Build from .src.(deb|rpm)
245 # --rebuild
246 # --recompile
247
248# General options
249 # --buildroot=DIRECTORY
250 # --clean
251 # --nobuild
252 # --nodeps
253 # --nodirtokens
254 # --rmsource
255 # --rmspec
256 # --short-circuit
257 # --target=CPU-VENDOR-OS
258
259 #my $popt = new Getopt::Popt(argv => \@ARGV, options => \@optionsTable);
260
261} # end parse_cmd()
262
263
264## parse_spec()
265# Parse the .spec file once we've.... uh....
266sub parse_spec {
267 open SPECFILE,"<$specfile";
268
269LINE: while (<SPECFILE>) {
270 next if /^#/;
271 next if /^\s+$/;
272 if (/^\%/) {
273 # A macro that needs further processing.
274 if (/^\%description/) {
275 $pkgdata{main}{desc} .= $_;
276 while (<SPECFILE>) {
277 redo LINE if /^\%/;
278 $pkgdata{main}{desc} .= " $_";
279 }
280
281 }
282 if (/^\%prep/) {
283 # %prep section. May have %setup macro; may include %patch tags,
284 # may be just a bare shell script.
285
286 # This really should be local-ish, but we need just the filename for the source
287 $pkgdata{main}{source} =~ s|.+/([^/]+)$|$1|;
288
289 # Replace some core macros
290 $pkgdata{main}{source} =~ s/\%\{name\}/$pkgdata{main}{name}/;
291 $pkgdata{main}{source} =~ s/\%\{version\}/$pkgdata{main}{version}/;
292
293PREPSCRIPT: while (<SPECFILE>) {
294 if (/^\%setup/) {
295 # Parse out the %setup macro. Note that we aren't supporting
296 # most of RPM's %setup features.
297 $prepscript .= "cd $topdir/BUILD\n";
298 if ( /\s+-n\s+([^\s]+)\s+/ ) {
299 $tarballdir = $1;
300 } else {
301 $tarballdir = "$pkgdata{main}{name}-$pkgdata{main}{version}";
302 }
303 $prepscript .= "rm -rf $tarballdir\ntar -".
304 ( $pkgdata{main}{source} =~ /\.tar\.gz$/ ? "z" : "" ).
305 ( $pkgdata{main}{source} =~ /\.tar\.bz2$/ ? "j" : "" ).
306 "x".( /\s+-q\s+/ ? '' : 'vv' )."f ".
307 "$topdir/SOURCES/$pkgdata{main}{source}\n".
308 qq(STATUS=\$?\nif [ \$STATUS -ne 0 ]; then\n exit \$STATUS\nfi\n).
309 ( /\s+-n\s+([^\s]+)\s+/ ?
310 "cd $1\n" : "cd $pkgdata{main}{name}-$pkgdata{main}{version}\n" ).
311 qq([ `/usr/bin/id -u` = '0' ] && /bin/chown -Rhf root .\n).
312 qq([ `/usr/bin/id -u` = '0' ] && /bin/chgrp -Rhf root .\n).
313 qq(/bin/chmod -Rf a+rX,g-w,o-w .\n);
314 } elsif (/^\%patch/) {
315 print "patch macro\n";
316 } else {
317 last PREPSCRIPT if /^\%/;
318 $prepscript .= $_;
319 }
320 }
321 redo LINE;
322 }
323 if (/^\%build/) {
324 # %build. This is pretty much just a shell script. There
325 # *are* a few macros, but we're not going to deal with them yet.
326 $buildscript .= "cd $tarballdir\n";
327 while (<SPECFILE>) {
328 redo LINE if /^\%/;
329 $buildscript .= $_;
330 }
331 }
332 if (/^\%install/) {
333 $installscript .= "cd $tarballdir\n";
334 while (<SPECFILE>) {
335 redo LINE if /^\%/;
336 $installscript .= $_;
337 }
338 }
339 if (/^\%clean/) {
340 while (<SPECFILE>) {
341 redo LINE if /^\%/;
342 $cleanscript .= $_;
343 }
344 }
345 if (/^\%post/) {
346 }
347 if (/^\%preun/) {
348 }
349 if (/^\%files/) {
350 # Danger! Danger!
351 }
352 } else {
353 if (/^summary:\s+(.+)/i) {
354 $pkgdata{main}{summary} = $1;
355 } elsif (/^name:\s+(.+)/i) {
356 $pkgdata{main}{name} = $1;
357 } elsif (/^version:\s+(.+)/i) {
358 $pkgdata{main}{version} = $1;
359 } elsif (/^release:\s+(.+)/i) {
360 $pkgdata{main}{release} = $1;
361 } elsif (/^group:\s+(.+)/i) {
362 $pkgdata{main}{group} = $1;
363 } elsif (/^copyright:\s+(.+)/i) {
364 $pkgdata{main}{copyright} = $1;
365 } elsif (/^url:\s+(.+)/i) {
366 $pkgdata{main}{url} = $1;
367 } elsif (/^packager:\s+(.+)/i) {
368 $pkgdata{main}{packager} = $1;
369 } elsif (/^buildroot:\s+(.+)/i) {
370 $buildroot = $1;
371 } elsif (/^source:\s+(.+)/i) {
372 $pkgdata{main}{source} = $1;
373 die "Unknown tarball format $1\n" if $1 !~ /\.tar\.(?:gz|bz2)$/;
374 }
375#Name: suwrap
376#Version: 0.04
377#Release: 3
378#Group: Applications/System
379#Copyright: WebHart internal ONLY. :(
380#BuildArchitectures: i386
381#BuildRoot: /tmp/%{name}-%{version}
382#Url: http://virtual.webhart.net
383#Packager: Kris Deugau <kdeugau@deepnet.cx>
384#Source: ftp://virtual.webhart.net/%{name}-%{version}.tar.gz
385
386 }
387 }
388 # Parse and replace macros
389 # Start with the ones I use
390# $pkgdata{main}{source} =~ s/\%\{name\}/$pkgdata{main}{name}/;
391# $pkgdata{main}{source} =~ s/\%\{version\}/$pkgdata{main}{version}/;
392
393# Scriptlet core. Use /g to make sure all occurrences get tweaked.
394 # "real" globals (apply to all packages the same way)
395 $scriptletbase =~ s/\%\{_topdir\}/$topdir/g;
396
397 # "fake" globals (may be tweaked by package)
398 $buildroot = $cmdbuildroot if $cmdbuildroot;
399 $scriptletbase =~ s/\%\{buildroot\}/$buildroot/g;
400
401 # sub-package(compatible) stuff
402 $scriptletbase =~ s/\%\{name\}/$pkgdata{main}{name}/g;
403 $scriptletbase =~ s/\%\{version\}/$pkgdata{main}{version}/g;
404 $scriptletbase =~ s/\%\{release\}/$pkgdata{main}{release}/g;
405
406# Some more macro substitution. Nrgh.
407 $buildroot =~ s/\%\{name\}/$pkgdata{main}{name}/g;
408 $buildroot =~ s/\%\{version\}/$pkgdata{main}{version}/g;
409 $buildroot =~ s/\%\{release\}/$pkgdata{main}{release}/g;
410} # end parse_spec()
411
412
413## prep()
414# Unpacks the tarball from the SOURCES directory to the BUILD directory.
415# Speaks gzip and bzip2.
416# Finishes by applying patches in %prep section of spec file
417sub prep {
418 {
419 # create script filename
420 my $prepscriptfile = "/var/tmp/deb-tmp.prep.".int(rand(99998)+1);
421 sysopen(PREPSCRIPT, $prepscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
422 or die $!;
423 print PREPSCRIPT $scriptletbase;
424 print PREPSCRIPT $prepscript;
425 close PREPSCRIPT;
426
427 # execute
428 print "Calling \%prep script $prepscriptfile...\n";
429 system("/bin/sh -e $prepscriptfile") == 0
430 or die "Can't exec: $!\nalso: $?";
431
432 # and clean up
433 unlink $prepscriptfile;
434 }
435} # end prep()
436
437
438## build()
439# Execute commands provided as a shell script. It may prove simpler
440# to do as rpm does and actually create a little shell script.
441sub build {
442 # create script filename
443 my $buildscriptfile = "/var/tmp/deb-tmp.build.".int(rand(99998)+1);
444 sysopen(BUILDSCRIPT, $buildscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
445 or die $!;
446 print BUILDSCRIPT $scriptletbase;
447 print BUILDSCRIPT $buildscript;
448 close BUILDSCRIPT;
449
450 # execute
451 print "Calling \%build script $buildscriptfile...\n";
452 system("/bin/sh -e $buildscriptfile") == 0
453 or die "Can't exec: $!\nalso: $?";
454
455 # and clean up
456 unlink $buildscriptfile;
457} # end build()
458
459
460## install()
461# Creates and executes %install script(let)
462sub install {
463 # create script filename
464 my $installscriptfile = "/var/tmp/deb-tmp.inst.".int(rand(99998)+1);
465 sysopen(INSTSCRIPT, $installscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
466 or die $!;
467 print INSTSCRIPT $scriptletbase;
468 print INSTSCRIPT $cleanscript; # Clean up our install target before installing into it.
469 print INSTSCRIPT $installscript;
470 close INSTSCRIPT;
471
472 # execute
473 print "Calling \%install script $installscriptfile...\n";
474 system("/bin/sh -e $installscriptfile") == 0
475 or die "Can't exec: $!\nalso: $?";
476
477 # and clean up
478 unlink $installscriptfile;
479} # end install()
480
481
482## binpackage()
483# Creates the binary .deb package from the installed tree in $buildroot.
484sub binpackage {
485 # Some checks on the .deb file location
486 if (!-e "$topdir/DEBS/i386") {
487# "if [ -e $topdir/DEBS/i386 ]; then\n\tmkdir $topdir/DEBS/i386\nfi\n".
488 mkdir "$topdir/DEBS/i386";
489 }
490 # Gotta do this first, otherwise the control file has nowhere to go. >:(
491 mkdir "$buildroot/DEBIAN";
492
493 # create script filename
494 my $debscriptfile = "/var/tmp/deb-tmp.pkg.".int(rand(99998)+1);
495 sysopen(DEBSCRIPT, $debscriptfile, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW)
496 or die $!;
497 print DEBSCRIPT $scriptletbase;
498 print DEBSCRIPT "tree $buildroot\n".
499 "fakeroot dpkg-deb -b $buildroot $topdir/DEBS/i386/$pkgdata{main}{name}_$pkgdata{main}{version}-$pkgdata{main}{release}_i386.deb\n";
500
501 close DEBSCRIPT;
502
503 my $control = "Package: $pkgdata{main}{name}\n".
504 "Version: $pkgdata{main}{version}-$pkgdata{main}{release}\n".
505 "Section: unknown\n".
506 "Priority: optional\n".
507 "Architecture: i386\n".
508 "Maintainer: $pkgdata{main}{packager}\n".
509 "Description: $pkgdata{main}{desc}\n";
510
511eval {
512 open CONTROL, ">$buildroot/DEBIAN/control";
513 print CONTROL $control;
514 close CONTROL;
515};
516if ($@) {
517 print $@;
518}
519
520#Package: httpd
521#Version: 2.0.54-7via
522#Section: unknown
523#Priority: optional
524#Architecture: i386
525#Depends: libc6 (>= 2.3.2.ds1-21), libdb4.2, libexpat1 (>= 1.95.8), libssl0.9.7, libapr0
526#Replaces: apache2
527#Installed-Size: 3076
528#Maintainer: Kris Deugau <kdeugau@vianet.ca>
529#Description: apache2 for ViaNet
530# apache2 for ViaNet. Includes per-vhost setuid patches from
531# http://home.samfundet.no/~sesse/mpm-itk/.
532
533
534 # execute
535 print "Creating .deb for $pkgdata{main}{name}...\n";
536 system("/bin/sh -e $debscriptfile") == 0
537 or die "Can't exec: $!\nalso: $?";
538
539 # and clean up
540 unlink $debscriptfile;
541
542} # end binpackage()
543
544
545sub srcpackage {}
Note: See TracBrowser for help on using the repository browser.