#! /usr/bin/perl # # $Id$ # autoautotools aware of CVS and rpm/deb. # Copyright (C) 2002-2003 Jan Kratochvil # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; exactly version 2 of June 1991 is required # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package AutoGen; require 5.6.0; # at least 'use warnings;' but we need some 5.6.0+ modules anyway use vars qw($VERSION); $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; }; use strict; use warnings; BEGIN { my @missing; for (split "\n",<<'HERE') { use Carp qw(cluck confess); use Getopt::Long; # &GetOptions, $Getopt::Long::* use File::Basename; # &basename use File::Grep qw(fgrep); use File::HomeDir; # &home use File::Remove qw(remove); use File::NCopy qw(copy); use File::chdir; # $CWD use File::Touch; # &touch use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG WIFSTOPPED WSTOPSIG); HERE eval "$_\n; 1;" or push @missing,(/^\s*use\s+([^ ;]+)/)[0]; } die "You are missing some modules - install them by:\n" ."\tperl -MCPAN -e 'install qw(".join(" ",@missing).")'\n" ."If you see messages: Writing Makefile for -- NOT OK\n" ."you may did not have installed make(1) while configuring Perl CPAN.\n" ."Install make(1) and type 'o conf init' in: perl -MCPAN -e shell\n" if @missing; } our %Options; sub _help { return ' Beware: '.basename($0).' is a tool only for maintainers! Supported parameters: --rpm Build RPM packages locally (needs /usr/src/(redhat|packages)/ access) --rpmtest Build RPM like "rpm" but w/o gpg/pgp signing --deb Build DEB packages locally --debtest Build DEB like "deb" but w/o gpg/pgp signing --dist Prepare all build files but do not run the final "./configure" --copy Behave exactly like in default mode but copy all instead of symlinks --clean Standard cleanup method --fullclean Like clean but even the .cvsignore files are removed -h|--help Print this help message. '.($Options{"help"} || ""); } sub checkcommandversion { my($class,$command,$version)=@_; local *F; do { open F,$_ or confess "Open $_: $!"; } for ("$command --version|"); local $/; undef $/; my $command_out=; close F; my $command_version=($command_out=~m#([\d.]+)#)[0]; confess "$command(1) version not found in its output" if !$command_version; confess "'$command' version $version or higher required" # Do not take 3rd+ numbers as it would not be a number if ($command_version=~/^(\d+[.]\d+)/)[0]<$version; } sub _readfile { my($filename)=@_; local $/=undef(); local *F; open F,$filename or confess "Open \"$filename\": $!"; my $r=; close F or confess "Close \"$filename\": $!"; # Do not &cluck as it may be pipe result return $r; } sub _writefile { my($filename,@content)=@_; local *F; open F,($filename=~/^[|]/ ? "" : ">").$filename or confess "rewrite \"$filename\": $!"; print F @content; close F or confess "close \"$filename\": $!"; # Do not &cluck as it may be pipe result } my %_rpmeval_cache; sub _rpmeval { my(@names)=@_; my @r=map({ my $name=$_; my $nameref=\$_rpmeval_cache{$name}; $$nameref=_readfile('rpm --eval %'.$name.'|') if !defined $$nameref; chomp $$nameref; $$nameref; } @names); return @r if wantarray(); confess "Scalar return for ".scalar(@r)." values" if 1!=@r; return $r[0]; } sub _system_error { my($code,$cmd)=@_; confess $cmd.": $code=".join(",", (!WIFEXITED($code) ? () : ("EXITSTATUS(".WEXITSTATUS($code).")")), (!WIFSIGNALED($code) ? () : ("TERMSIG(" .WTERMSIG($code) .")")), (!WIFSTOPPED($code) ? () : ("STOPSIG(" .WSTOPSIG($code) .")")), ); } sub _system { my(@args)=@_; my $rc_sub=pop @args if ref $args[$#args]; $rc_sub||=sub { $_[0]==0; }; my $rc=system(@args); _system_error $?,join(" ",@args) if !WIFEXITED($?) || !&{$rc_sub}(WEXITSTATUS($?)); } # Assumed wildcard pattern expansion to exactly one item if !$nocheck sub _copy { my(@files)=@_; my $nocheck=shift @files if $files[0] eq "nocheck"; my $flag=shift @files if ref $files[0]; my $dest=pop @files; # expand pattern to properly match © resulting filenames count @files=map({ glob $_; } @files); @files==copy((!$flag ? () : $flag),@files,$dest) or $nocheck or confess "$!"; } # Assumed wildcard pattern expansion to exactly one item if !$nocheck sub _remove { my(@files)=@_; my $nocheck=shift @files if $files[0] eq "nocheck"; my $flag=shift @files if ref $files[0]; # expand pattern to properly match &remove resulting filenames count @files=map({ glob $_; } @files); @files==remove((!$flag ? () : $flag),@files) or $nocheck or confess "$!"; } # FIXME: File::NCopy exists but File::NMove doesn't sub _move { my(@files)=@_; my $dest=pop @files; _copy @files,$dest; _remove @files; } sub _mkdir { my(@dirs)=@_; for (@dirs) { mkdir $_ or confess "$!"; } } sub _prepdist { my($class,$name)=@_; my($specsrc)=map((-e $_ ? $_ : "$name.spec.in"),"$name.spec.m4.in"); my $spec=_readfile $specsrc; $spec=~s/\\\n/ /gs; my $configure_args=($spec=~/^[%]configure\b[ \t]*(.*)$/m)[0]; $configure_args=~s/--disable-gtk-doc\b/--enable-gtk-doc/g; # optional; gtk-doc reqd for 'make dist' $class->run(%Options, "ARGV"=>[qw(--copy)], "configure_args"=>[split /\s+/,$configure_args], ); _remove "nocheck",($Options{"ChangeLog"} || "ChangeLog"); # force its rebuild by Makefile/rcs2log } # $args{ # "sign"=>bool, # }, sub _rpmbuild { my($class,%args)=@_; my $name=$Options{"name"}; _remove "nocheck",\1, _rpmeval("_tmppath" )."/$name-*-root", _rpmeval("_builddir")."/$name-*"; $class->_prepdist($name); _system "make $name.spec"; my $spec=_readfile "$name.spec"; my $patch=($spec=~/^Patch\d*\s*:\s*(.*)$/m)[0]; _system "make dist"; if (!$patch) { _copy "$name-*.tar.gz",_rpmeval("_sourcedir"); } else { my @origs; for my $glob ("orig-$name-*.tar.{gz,Z,bz2}") { @origs=glob $glob; confess "Invalid glob $glob: ".join(",",@origs) if 1!=@origs; } my($origbase,$ext)=($origs[0]=~/^orig-(.*)[.]tar[.](gz|Z|bz2)$/); _copy $origs[0],_rpmeval("_sourcedir")."/".($origs[0]=~/^orig-(.*)$/)[0]; _system "tar x".($ext eq "bz2" ? "j" : "z")."f ".$origs[0]; _mkdir $origbase."-orig"; # FIXME: Copy also dot-prefixed files! _move \1,$origbase."/*",$origbase."-orig/"; rmdir $origbase or confess "rmdir $origbase: $!"; my @ours; for my $glob ("$name-*.tar.gz") { @ours=glob $glob; confess "Invalid glob $glob: ".join(",",@ours) if 1!=@ours; } my($ourbase)=($ours[0]=~/^(.*)[.]tar[.]gz$/); _system "tar xzf ".$ours[0]; # Use single-argument system() as we need shell redirection. # FIXME: Use directory-independent _cleanfiles(), not root-directory '.cvsignore'. # "-X -" does not work, it needs to be stat(2)able file. _system "diff -ruP -X .cvsignore -I '".'[$]\(Id\|RCSfile\)\>.*[$]'."'" ." $origbase-orig/ $ourbase/" ." >"._rpmeval("_sourcedir")."/".$patch, sub { $_[0]==0 || $_[0]==1; }; # diff(1) returns non-zero return code on any diffs. _remove \1,$ourbase,$origbase."-orig"; } _system(join(" ","rpmbuild", "-ba", "--rmsource", # _remove _rpmeval("_sourcedir")."/$name-*.tar.gz"; "--clean", # _remove _rpmeval("_builddir")."/$name-VERSION"; (!$args{"sign"} ? () : "--sign"), "$name.spec", )); _system "make dist-tarZ" if $Options{"dist-tarZ"}; _move _rpmeval("_srcrpmdir")."/$name-*.src.rpm","."; _move _rpmeval("_rpmdir")."/"._rpmeval("_target_cpu")."/$name-*."._rpmeval("_target_cpu").".rpm","."; _system "ls -l $name-*"; exit 0; # should never return } # $args{ # "sign"=>bool, # }, sub _debbuild { my($class,%args)=@_; my $name=$Options{"name"}; $class->_prepdist($name); _system "make distdir"; # Copy 'orig' archive after &_prepdist which would delete it. my @origs; my $base; for my $glob ("orig-$name-*.tar.{gz,Z,bz2}") { @origs=glob $glob; if (@origs) { confess "Invalid glob $glob: ".join(",",@origs) if 1!=@origs; $origs[0]=~/^orig-([^-]+)-(.*)([.]tar[.][^.]+)$/; my $deborig="$1_$2.orig$3"; $base="$1-$2"; _copy $origs[0],$deborig; } } my @subdirs; for my $glob ("$name-*") { @subdirs=glob $glob; confess "Invalid glob $glob: ".join(",",@subdirs) if 1!=@subdirs; } rename $subdirs[0],$base or confess "$!"; _system(join(" ","cd ".$base.";dpkg-buildpackage", "-rfakeroot", ($args{"sign"} ? () : ("-us","-uc")), )); _remove \1,$base; _system "ls -l ${name}*_[0-9]*"; exit 0; # should never return } # WARNING: doesn't respect %Options change! my @_cleanfiles_cache; sub _cleanfiles { # maintainer-clean hack is not safe, please list all files for 'rm'. # When the filename doesn't contain '/', it is applied to ALL directories. # Please note that files exactly in root dir MUST have ./ in the front # (to not to be considered as ALL-directories files). if (!@_cleanfiles_cache) { @_cleanfiles_cache=map({ local $_=$_; # Prevent: Modification of a read-only value attempted s/\Q\E/$Options{"name"}/ego; s#/+#/#g; # "*xyzzy" basename -> "*xyzzy",".*xyzzy" for proper cleaning (!m#^((?:.*/)?)([*][^/]*)$# ? ($_) : ("$1$2","$1.$2")); } ( ".#*", # Possible attempt to put comments in qw() list qw( *~ *.orig *.rej core Makefile Makefile.in TAGS tags ID .deps .libs *.[oa] *.l[oa] *.l[oa]T .cvsignore ./errs* ./intl ./configure ./configure.scan ./config.guess ./config.status ./config.sub ./config.log ./config.cache ./config.h ./config.h.in ./confdefs.h ./conftest* ./autoh[0-9]* ./confcache ./config.rpath ./depcomp ./compile ./stamp-h ./stamp-h.in ./stamp-h1 ./install-sh ./aclocal.m4 ./autom4te.cache ./autom4te-*.cache ./m4 ./missing ./mkinstalldirs ./libtool ./ltconfig ./ltmain.sh ./ABOUT-NLS ./-[0-9]* ./-*-[0-9]* ./.spec ./.m4 ./.spec.m4 ./debian/tmp ./debian/ ./*_[0-9]* ./macros/macros.dep ./macros/glade-w.sh ./po/Makefile.in.in ./po/POTFILES* ./po/cat-id-tbl.c ./po/cat-id-tbl.tmp ./po/*.gmo ./po/*.mo ./po/stamp-cat-id ./po/.pot ./po/ChangeLog ./po/Makevars ./po/Makevars.template ./po/Rules-quot ./po/*.sed ./po/*.sin ./po/*.header ), map(("./$_"),($Options{"ChangeLog"} || "ChangeLog")), map((!$_ ? () : do { my $dir=$_; map("$dir/$_",qw( *.stamp sgml* tmpl* html* xml *.txt *.txt.bak *.new *.sgml *.args *.hierarchy *.signals *.interfaces *.prerequisites )); }),$Options{"gtk-doc-dir"}), map((!$_ ? () : do { my $dir=$_; map("$dir/$_",qw( *.html *.info* *.txt *.tex *.sgml )); }),$Options{"docbook-lite-dir"}), map((!$_ ? () : @$_),$Options{"clean"}), )); # sanity check for (@_cleanfiles_cache) { confess "dir-specific 'clean'-pattern must start with './': $_" if m#^(?!\Q./\E).*/#; }; } return @_cleanfiles_cache; } sub _cleanfilesfordir { my($dir)=@_; return map({ if (m#^\Q$dir\E/([^/]+)$#) { # this-dir: "./this-dir/file-name.c" ($1); } elsif (m#^[^/]+$#) { # all-dirs: "file-name.c"; the same as "./*/file-name.c" ($&); } elsif (do { # all-subdirs: "./parent-of-this-dir/*/file-name.c" m#/[*]/([^/]+)$#; ($_=$1) && $dir=~m#^\Q$`\E(?:/|$)#; }) { ($_); } else { (); } } _cleanfiles()); } sub _cvsdirs { my(@startdirs)=@_; my @r=(); my @todo=(@startdirs); while (defined(my $dir=shift @todo)) { local *ENTRIES; my $entries_filename="$dir/CVS/Entries"; open ENTRIES,$entries_filename or (cluck "open \"$entries_filename\": $!" and next); push @r,$dir; local $/="\n"; my %local=(); local $_; while () { chomp; next if !m#^D/([^/]+)/#; $local{$1}=1; } close ENTRIES or cluck "close \"$entries_filename\": $!"; if (-e (my $entries_log_filename=$dir."/CVS/Entries.Log")) { local *ENTRIES_LOG; if (open ENTRIES_LOG,$entries_log_filename or cluck "open \"$entries_log_filename\": $!") { local $_; while () { chomp; if (m#^A D/([^/]*)/#) { $local{$1}=1; } elsif (m#^R D/([^/]*)/#) { delete $local{$1}; } else { cluck "$entries_log_filename: Unrecognized line $.: $_"; } } close ENTRIES_LOG or cluck "close \"$entries_log_filename\": $!"; } } push @todo,map(("$dir/$_"),keys(%local)); } return @r; } sub _expandclass { my($patt)=@_; return $patt if $patt!~/\Q[\E(.*?)\Q]\E/; my($pre,$range,$post)=($`,$1,$'); # FIXME: local($`,$1,$') doesn't work - why? 1 while $range=~s#(.)-(.)# join("",map(chr,(ord($1)..ord($2)))); #ge; return map({ _expandclass("$pre$_$post"); } split("",$range)); } sub run { my($class,%options)=@_; local %Options=%options; do { require $_ if -e; } for (home()."/.".$Options{"name"}.".autogen.pl"); do { $$_=1 if !defined($$_) && fgrep { /^\s*AUTOMAKE_OPTIONS\s*=[^#]*\bdist-tarZ\b/m; } "Makefile.am"; } for (\$Options{"dist-tarZ"}); Getopt::Long::Configure('noignorecase','prefix_pattern=(--|-|\+|)'); local @ARGV=@{$Options{"ARGV"}}; print _help() and confess if !GetOptions( "rpm" ,sub { $class->_rpmbuild("sign"=>1); }, "rpmtest" ,sub { $class->_rpmbuild("sign"=>0); }, "deb" ,sub { $class->_debbuild("sign"=>1); }, "debtest" ,sub { $class->_debbuild("sign"=>0); }, "cleanfilesfordir=s",sub { print "$_\n" for (_cleanfilesfordir $_[1]); exit 0; }, "dist" ,\$Options{"ARGV_dist"}, "copy!" ,\$Options{"ARGV_copy"}, "clean" ,\$Options{"ARGV_clean"}, "fullclean",\$Options{"ARGV_fullclean"}, "h|help" ,sub { print _help(); exit 0; }, $Options{"GetOptions_args"}, ) || @ARGV; for my $subdir (map((!$_ ? () : @$_),$Options{"subdirs"})) { local $CWD=$subdir; _system "./autogen.pl",@{$Options{"ARGV"}},"--dist"; # use "--dist" just as fallback! } for my $dir (_cvsdirs(".")) { my @cleanfilesfordir=_cleanfilesfordir $dir; _writefile $dir."/.cvsignore",map("$_\n",@cleanfilesfordir) if !$Options{"ARGV_fullclean"}; _remove "nocheck",\1,map({ _expandclass("$dir/$_"); } grep({ $Options{"ARGV_fullclean"} or $_ ne ".cvsignore"; } @cleanfilesfordir)); } return if $Options{"ARGV_clean"} || $Options{"ARGV_fullclean"}; $Options{"aclocal_args"}=[qw(-I macros),map((!$_ ? () : @$_),$Options{"aclocal_args"})]; my $configure_name; do { $configure_name||=$_ if -f $_ } for ("configure.in"); do { $configure_name||=$_ if -f $_ } for ("configure.ac"); $configure_name or confess "Cannot find configure.{in,ac}"; my $configure_in=_readfile($configure_name); do { $$_=1 if !defined($$_) && $configure_in=~/^\s*AM_GNU_GETTEXT\b/m; } for (\$Options{"want-gettextize"}); do { $$_=1 if !defined($$_) && $configure_in=~/^\s*AM_GLIB_GNU_GETTEXT\b/m; } for (\$Options{"want-glib-gettextize"}); do { $$_=1 if !defined($$_) && $configure_in=~/^\s*AM_PROG_LIBTOOL\b/m; } for (\$Options{"want-libtoolize"}); do { $$_=1 if !defined($$_) && $configure_in=~/^\s*A[CM]_CONFIG_HEADER\b/m; } for (\$Options{"want-autoheader"}); my @copy_arg=(!$Options{"ARGV_copy"} ? () : "--copy"); do { &$_ if $_; } for ($Options{"prep"}); touch "po/POTFILES.in" if -d "po"; if ($Options{"want-gettextize"}) { # don't use multi-arg system() here as it would reject "printflush("gettextize recovery rename \"$_~\"->\"$_\"... "); rename "$_~","$_" or confess "$!"; STDERR->printflush("ok\n"); } if (!-e "po/Makevars") { my $Makevars_template="po/Makevars.template"; my $makevars=_readfile $Makevars_template; $makevars=~s/^(COPYRIGHT_HOLDER)\b.*$/"$1=".$Options{"COPYRIGHT_HOLDER"}/meg or confess "COPYRIGHT_HOLDER not found in $Makevars_template"; _writefile "po/Makevars",$makevars; } # Prevent updating of contents during touch of any source file; # change the .po contents only when some data get updated for my $Makefile_in_in ("po/Makefile.in.in") { my $file=_readfile $Makefile_in_in; $file=~s%(\$\Q(MSGMERGE_UPDATE)\E) (\$\$\Q{lang}.po \E\$\Q(DOMAIN).pot\E)$% $1.q< --backup=simple --suffix="~" >.$2.q<;> .q< if test `diff -u $${lang}.po~ $${lang}.po> .q< | sed> .q< -e '1,/^@@.*@@$$/d'> .q< -e '/^[+-]"POT-Creation-Date:/d'> .q< -e '/^[^+-]/d'> .q< -e '/^[+-]#/d'> .q< | wc -l` -eq 0;then> .q< touch --reference=$${lang}.po $${lang}.po~;> .q< mv -f $${lang}.po~ $${lang}.po;> .q< else> .q< rm -f $${lang}.po~;> .q< fi> %me or confess; unlink $Makefile_in_in or confess "$!"; _writefile $Makefile_in_in,$file; } } if ($Options{"want-glib-gettextize"}) { _system "glib-gettextize",@copy_arg; # "po/ChangeLog" is somehow missing at this point File::Touch->new("atime_only"=>1)->touch("po/ChangeLog"); } _system "aclocal",map((!$_ ? () : @$_),$Options{"aclocal_args"}); _system qw(libtoolize),@copy_arg if $Options{"want-libtoolize"}; _system qw(autoheader) if $Options{"want-autoheader"}; # "ChangeLog" is reqd by automake(1) # Don't remove it afterwards as it may still be needed during automatic automake Makefile rebuilds File::Touch->new("atime_only"=>1)->touch("ChangeLog") if !$Options{"ChangeLog"}; _system qw(automake --add-missing),@copy_arg; _system qw(autoconf); _writefile "| patch configure",<<'CONFIGURE_SUBST_X_EOF'; --- configure-orig Wed Aug 20 12:10:37 2003 +++ configure Wed Aug 20 13:22:51 2003 @@ -21590,6 +21590,11 @@ rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file + for f in $ac_file_inputs; do + if test -x $f; then + chmod +x $ac_file + fi + done else cat $tmp/out rm -f $tmp/out CONFIGURE_SUBST_X_EOF _remove "nocheck","./configure.orig"; # It is usually produced by 'CONFIGURE_SUBST_X'. # Why it is left there after RedHat autoconf-2.53-8 ? _remove "nocheck",\1,"autom4te-*.cache"; return if $Options{"ARGV_dist"}; # 'configure' defaults to CFLAGS '-g -O2' but our --enable-maintainer-mode # should force '-ggdb3' $ENV{"CFLAGS"}||=""; # shared/static switching cannot be based on maintainer-mode in configure _system(qw(./configure --enable-maintainer-mode), (!$Options{"want-libtoolize"} ? () : qw(--enable-shared --disable-static)), map((!$_ ? () : @$_),$Options{"configure_args"}), ); } 1; __END__ =head1 NAME AutoGen - autoautotools aware of CVS and rpm/deb =head1 SYNOPSIS use lib "./macros/"; use AutoGen; AutoGen->run( "name"=>"fooproject", "COPYRIGHT_HOLDER"=>'Joe ', "ARGV"=>\@ARGV, ); =head1 ABSTRACT Projects usually have their B script files to run B(1), B(1) and similiar tools. This project has some additional features: =over =item Clean the checkout directory without Bs for 'make clean'. =item Maintain B<.cvsignore> files containing the files being cleaned. =item Single-command package building of B<.rpm>/B<.deb> out of CVS checkout. =item Supports: B, B, B, B, B. =back =head1 OPTIONS B module itself parses its command-line arguments from B<"ARGV"> field of the hash passed to B<&AutoGen->run>. Its initial double-dash ('B<-->') prefixes can be omitted. B will build the sources for development purposes in the current directory if no other packaging options were given. =over =item B<--rpm> Build B<.rpm> packages. Either you need access to directory B (or B etc.) or you should set B<%_usrsrc> (default B) elsewhere in your B<$HOME/.rpmmacros>: %_usrsrc /home/joe/src As the resulting package will be digitally signed you should also set signing properties in your B<$HOME/.rpmmacros>: %_signature gpg %_gpg_name Joe =item B<--rpmtest> The same as B<--rpm> but the resulting package will not be I digitally signed. I passphrase is requested in the middle of package building which would be very discomfortable during project development. =item B<--deb> Build B<.deb> packages. =item B<--debtest> The same as B<--deb> but the resulting package will not be I digitally signed. I passphrase is requested in the middle of package building which would be very discomfortable during project development. =item B<--dist> Prepare all build files but do not run the final B<./configure>. The resulting directory is similiar to the one produced by B but DO NOT INTERCHANGE THEM! =item B<--copy> Some distribution files are created just as symlinks to the system ones by default. This option forces their copy such as B does. =item B<--clean> Clean all files not belonging to the CVS repository. You SHOULD backup your directory first during B bootstrap. Only B<.cvsignore> files are left in our directory tree. =item B<--fullclean> Similiar to B<--clean> option but even B<.cvsignore> files get deleted. =item B<-h|--help> Print short help message describing these I. =back =head1 CONFIGURATION B<&AutoGen->run> method is passed configuration hash by your B. Its possible fields: =over =item B Project basename - it should contain no special characters. =item B Pass here B<\@ARGV> from the user. =item B Array specifying files to be cleaned during B<./autogen.pl clean> and put into B<.cvsignore> files. You still should add these entries also to B, B or B variables of Bs. There are several formats of B field possible entries: =over =item B<./path/to/file.c> Full project-basedir relative path to the deleted file. Rule applies only to the one specified directory. Although B would be also possible it is not recommended to have unified format with B<./only_basedir.c> entries to delete file just in the root directory (as B would delete it in ALL subdirectories incl. the root one). =item B Delete B found in any subdirectory of the project. It has the same effect as B<./*/basename_generated.c> entry. =item B<./subdir/*/name.c> Delete B in B<./subdir> and all of its subdirector descendants. Entry B would have the same effect. =back =item B B reference run to prepare the sources being built in the current directory (if no packaging options given). Check for needed B versions required to build B script by Bcheckcommandversion>. Other version checks belong to B instead. Referenced B must return true value. =item B Alternative name of B, defaults to "B". You should define alternative name if patching existing package with its own B. =item B Optional directory name where B(1) generated files reside. =back =head1 FUNCTIONS =over =item Bcheckcommandversion>(B<$command>,B<$minimal_version>) Functions runs B<$command --version> and checks the resulting version number against given B<$minimal_version>. Currently only the first 2 version numbers are parsed. =back =head1 PATCHED PACKAGES This section applies to projects where you patch foreign existing packages. =over =item Use the following alternate B prolog if your branch does not belong to the B
CVS branch: BEGIN { # 'macros' could got checked out empty by our '-r captive' tag. if (!-f "./macros/AutoGen.pm") { do { system $_ and die "$_: $!"; } for ("cvs co -A macros"); } } use lib "./macros/"; use AutoGen; =item Put B archive to the project root directory as B. =item Set B<$VERSION> in B file with you vendor prefix such as B<1.2.3joe1>. Set B<$VERSION_BASE> to B<1.2.3> and B<$VERSION_EXT> to B. B() them both. =item B<.spec> file changes =over =item Change B to B. =item Change (expected) line B to B. =item Change B to B. =item Add line B. This patch file name is not mandatory. =item Remember to call B commands manually in the B<%build> section if their source files were modified in your vendor branch. =back =item B-specific changes =over =item Include both B and B to B. =item Include B to B field in B. =item Use B<@VERSION@> in B. Package will be built as Debian-native package (no I<.diff> file) as the patches are not just for Debian packages, it is a new package (read: dpkg does not support branch patches). =back =back =head1 AUTHOR Jan Kratochvil >, I