#!/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-28 22:04:31 +0000 (Fri, 28 Oct 2005) $ # SVN revision $Rev: 5 $ # Last update by $Author: kdeugau $ ### use strict; # 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; my $verbosity = 0; my %cmdopts = (type => '', stage => 'a', short => 'n' ); my %targets = ('p' => 'Prep', 'c' => 'Compile', 'i' => 'Install', 'l' => 'Verify %files', 'a' => 'Build binary and source', 'b' => 'Build binary', 's' => 'Build source' ); my $topdir = "/usr/src/debian"; # Package data # This is the form of $pkgdata{pkgname}{meta} # meta includes Summary, Name, Version, Release, Group, Copyright, # Source, URL, Packager, BuildRoot, Description my %pkgdata; # 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 -zxvf $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 -jxvf $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"; while () { next if /^#/; next if /^\s+$/; if (/^\%/) { # A macro that needs further processing. if (/^\%build/) { # %build. This is pretty much just a shell script. } } 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; } #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 {}