#!/usr/bin/perl # debbuild script # Shamlessly steals intreface from rpm's "rpmbuild" to create # Debian packages. Please note that such packages are highly # unlikely to conform to "Debian Policy". ### # SVN revision info # $Date: 2005-10-31 22:58:40 +0000 (Mon, 31 Oct 2005) $ # SVN revision $Rev: 6 $ # Last update by $Author: kdeugau $ ### use strict; use warnings; # Program flow: # -> Parse/execute "system" config/macros (if any - should be rare) # -> Parse/execute "user" config/macros (if any - *my* requirement is %_topdir) # -> Parse command line for options, spec file/tarball/.src.deb (NB - also accept .src.rpm) # User's prefs for dirs, environment, etc,etc,etc. # config file ~/.debmacros # Default ordered search paths for config/macros: # /usr/lib/rpm/rpmrc /usr/lib/rpm/redhat/rpmrc /etc/rpmrc ~/.rpmrc # /usr/lib/rpm/macros /usr/lib/rpm/redhat/macros /etc/rpm/macros ~/.rpmmacros # **NOTE: May be possible to (ab)use bits of debhelper # Build tree # default is /usr/src/debian/{BUILD,SOURCES,SPECS,DEBS,SDEBS} # Globals my $specfile; my $tarball; my $srcpkg; # Initialized globals my $verbosity = 0; my %cmdopts = (type => '', stage => 'a', short => 'n' ); my $topdir = "/usr/src/debian"; # "Constants" my %targets = ('p' => 'Prep', 'c' => 'Compile', 'i' => 'Install', 'l' => 'Verify %files', 'a' => 'Build binary and source', 'b' => 'Build binary', 's' => 'Build source' ); my $scriptletbase = q(#!/bin/sh RPM_SOURCE_DIR="%{_topdir}/SOURCES" RPM_BUILD_DIR="%{_topdir}/BUILD" RPM_OPT_FLAGS="-O2 -g -march=i386 -mcpu=i686" RPM_ARCH="i386" RPM_OS="linux" export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS RPM_DOC_DIR="/usr/share/doc" export RPM_DOC_DIR RPM_PACKAGE_NAME="%{name}" RPM_PACKAGE_VERSION="%{version}" RPM_PACKAGE_RELEASE="%{release}" export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE RPM_BUILD_ROOT="%{buildroot}" export RPM_BUILD_ROOT set -x umask 022 cd %{_topdir}/BUILD ); # Package data # This is the form of $pkgdata{pkgname}{meta} # meta includes Summary, Name, Version, Release, Group, Copyright, # Source, URL, Packager, BuildRoot, Description # 10/31/2005 Maybe this should be flatter? -kgd my %pkgdata; # Scriptlets my $prepscript; my $buildscript; my $installscript; # Functions sub prep; sub parse_spec; die "Not enough arguments\n" if #$argv == 0; load_userconfig(); parse_cmd(); # Hokay. Need to: # Execute prep if *not* --short-circuit prep() if ($cmdopts{short} ne 'y'); if ($cmdopts{stage} =~ /ciab/) { # Execute build if bc # Execute build if bi, ba, bb and NOT --short-circuit build() if ( ($cmdopts{stage} eq 'c') || (($cmdopts{stage} =~ /iab/) && ($cmdopts{short} ne 'y')) ); # -> Execute install() if ($cmdopts{short} ne 'y'); binpackage(); } srcpackage(); # Just in case. exit 0; ## load_userconfig() # Loads user configuration (if any) # Currently only handles .debmacros # Needs to handle "other files" sub load_userconfig { my (undef,undef,undef,undef,undef,undef,undef,$homedir,undef) = getpwuid($<); if (-e "$homedir/.debmacros") { open USERMACROS,"<$homedir/.debmacros"; while () { # And we also only handle the %_topdir macro at the moment. if (/^\%_topdir/) { my (undef,$tmp) = split /\s+/, $_; $topdir = $tmp; } } } } # end load_userconfig() ## parse_cmd() # Parses command line into global hash %cmdopts, other globals # Options based on rpmbuild's options sub parse_cmd { # Don't feel like coding my own option parser... #use Getopt::Long; # ... but I may have to: (OTOH, rpm uses popt, so maybe we can too.) #use Getopt::Popt qw(:all); # Or not. >:( Stupid Debian lack of findable Perl module names in packages. # Stuff it. my $prevopt = ''; foreach (@ARGV) { chomp; $prevopt = $_; # Is it an option? if (/^-/) { # Is it a long option? if (/^--/) { if (/^--short-circuit/) { $cmdopts{short} = 'y'; } elsif (/^--rebuild/) { $cmdopts{type} = 's'; } else { print "Long opt $_\n"; } } else { if (/^-[bt]/) { if ($cmdopts{stage} eq 's') { # Mutually exclusive options. die "Can't use $_ with --rebuild\n"; } else { ($cmdopts{stage}) = (/^-[bt]([pcilabs])/); ($cmdopts{type}) = (/^-([bt])[pcilabs]/); } } elsif (/^-v/) { # bump verbosity. Not sure what I'll actually do here... } else { die "Bad option $_\n"; } } } else { # --buildroot is the only option that takes an argument # Therefore, any *other* bare arguments are the spec file, # tarball, or source package we're operating on - depending # on which one we meet. if ($prevopt eq '--buildroot') { # Set buildroot } else { if ($cmdopts{type} eq 's') { # Source package if (!/\.src\.(deb|rpm)$/) { die "Can't --rebuild with $_\n"; } } elsif ($cmdopts{type} eq 'b') { $specfile = $_; # Spec file } else { # Tarball } } } } # Some cross-checks. rpmbuild limits --short-circuit to just # the "compile" and "install" targets - with good reason IMO. # NB - this is NOT fatal, just ignored! if ($cmdopts{short} eq 'y') { warn "Can't use --short-circuit for $targets{$cmdopts{stage}} stage. Ignoring.\n"; $cmdopts{short} = 'n'; } # Valid options, with example arguments (if any): # Build from .spec file; mutually exclusive: # -bp # -bc # -bi # -bl # -ba # -bb # -bs # Build from tarball; mutually exclusive: # -tp # -tc # -ti # -ta # -tb # -ts # Build from .src.(deb|rpm) # --rebuild # --recompile # General options # --buildroot=DIRECTORY # --clean # --nobuild # --nodeps # --nodirtokens # --rmsource # --rmspec # --short-circuit # --target=CPU-VENDOR-OS #my $popt = new Getopt::Popt(argv => \@ARGV, options => \@optionsTable); } # end parse_cmd() ## prep() # Unpacks the tarball from the SOURCES directory to the BUILD directory. # Speaks gzip and bzip2. # Finishes by applying patches in %prep section of spec file sub prep { if ($cmdopts{type} eq 'b') { # Need to read the spec file to find the tarball parse_spec(); # Need to find the filename part of the tarball. In theory, we # could go out to the URL and retrieve it, but that's Messy. # want to make this local $pkgdata{main}{source} =~ s|.+/([^/]+)$|$1|; if ($pkgdata{main}{source} =~ /(.+)\.tar\.gz$/) { -e "$topdir/BUILD/$1" && system "rm -rf $topdir/BUILD/$1"; # system "cd $topdir/BUILD;tar -zxvvf $topdir/SOURCES/$pkgdata{main}{source}"; } elsif ($pkgdata{main}{source} =~ /(.+)\.tar\.bz2$/) { -e "$topdir/BUILD/$1" && system "rm -rf $topdir/BUILD/$1"; system "cd $topdir/BUILD;tar -jxvvf $topdir/SOURCES/$pkgdata{main}{source}"; } else { die "Unknown source tarball format\n"; } # And now for patches. (not) } } # end prep() ## parse_spec() # Parse the .spec file once we've.... uh.... sub parse_spec { open SPECFILE,"<$specfile"; LINE: while () { next if /^#/; next if /^\s+$/; if (/^\%/) { # A macro that needs further processing. if (/^\%prep/) { # %prep section. May have %setup macro; may include %patch tags, # may be just a bare shell script. # This really should be local-ish, but we need just the filename for the source $pkgdata{main}{source} =~ s|.+/([^/]+)$|$1|; # Replace some core macros $pkgdata{main}{source} =~ s/\%\{name\}/$pkgdata{main}{name}/; $pkgdata{main}{source} =~ s/\%\{version\}/$pkgdata{main}{version}/; PREPSCRIPT: while () { if (/^\%setup/) { # Parse out the %setup macro. Note that we aren't supporting # most of RPM's %setup features. $prepscript .= "cd $topdir/BUILD\n". ( /\s+-n\s+([^\s]+)\s+/ ? "rm -rf $1\n" : "rm -rf $pkgdata{main}{name}-$pkgdata{main}{version}\n" ). "tar -". ( $pkgdata{main}{source} =~ /\.tar\.gz$/ ? "z" : "" ). ( $pkgdata{main}{source} =~ /\.tar\.bz2$/ ? "j" : "" ). "x".( /\s+-q\s+/ ? '' : 'vv' )."f ". "$topdir/SOURCES/$pkgdata{main}{source}\n". qq(STATUS=\$?\nif [ \$STATUS -ne 0 ]; then\n exit \$STATUS\nfi\n). ( /\s+-n\s+([^\s]+)\s+/ ? "cd $1\n" : "cd $pkgdata{main}{name}-$pkgdata{main}{version}\n" ). qq([ `/usr/bin/id -u` = '0' ] && /bin/chown -Rhf root .\n). qq([ `/usr/bin/id -u` = '0' ] && /bin/chgrp -Rhf root .\n). qq(/bin/chmod -Rf a+rX,g-w,o-w .\n); } elsif (/^\%patch/) { print "patch macro\n"; } else { last PREPSCRIPT if /^\%/; $prepscript .= $_; } } redo LINE; } if (/^\%build/) { # %build. This is pretty much just a shell script. There # *are* a few macros, but we're not going to deal with them yet. while () { redo LINE if /^\%/; $buildscript .= $_; } } if (/^\%install/) { while () { redo LINE if /^\%/; $installscript .= $_; } } if (/^\%clean/) { } if (/^\%post/) { } if (/^\%preun/) { } if (/^\%files/) { # Danger! Danger! } } else { if (/^summary:\s+(.+)/i) { $pkgdata{main}{summary} = $1; } elsif (/^name:\s+(.+)/i) { $pkgdata{main}{name} = $1; } elsif (/^version:\s+(.+)/i) { $pkgdata{main}{version} = $1; } elsif (/^release:\s+(.+)/i) { $pkgdata{main}{release} = $1; } elsif (/^group:\s+(.+)/i) { $pkgdata{main}{group} = $1; } elsif (/^copyright:\s+(.+)/i) { $pkgdata{main}{copyright} = $1; } elsif (/^url:\s+(.+)/i) { $pkgdata{main}{url} = $1; } elsif (/^packager:\s+(.+)/i) { $pkgdata{main}{packager} = $1; } elsif (/^source:\s+(.+)/i) { $pkgdata{main}{source} = $1; die "Unknown tarball format $1\n" if $1 !~ /\.tar\.(?:gz|bz2)$/; } #Name: suwrap #Version: 0.04 #Release: 3 #Group: Applications/System #Copyright: WebHart internal ONLY. :( #BuildArchitectures: i386 #BuildRoot: /tmp/%{name}-%{version} #Url: http://virtual.webhart.net #Packager: Kris Deugau #Source: ftp://virtual.webhart.net/%{name}-%{version}.tar.gz } } # Parse and replace macros in $pkgdata{}{} # Start with the ones I use $pkgdata{main}{source} =~ s/\%\{name\}/$pkgdata{main}{name}/; $pkgdata{main}{source} =~ s/\%\{version\}/$pkgdata{main}{version}/; } # end parse_spec() ## build() # Execute commands provided as a shell script. It may prove simpler # to do as rpm does and actually create a little shell script. sub build { } # end build() sub install {} sub binpackage {} sub srcpackage {}