#!/usr/bin/perl -w # # Project Builder main application # # $Id$ # # Copyright B. Cornec 2007 # Provided under the GPL v2 # Syntax: see at end use strict 'vars'; use Getopt::Std; use Data::Dumper; use English; use AppConfig qw(:argcount :expand); use File::Basename; use Time::localtime qw(localtime); use POSIX qw(strftime); use vars qw (%defpkgdir %extpkgdir %version %confparam %filteredfiles $debug $LOG); %extpkgdir = (); %filteredfiles = (); $debug = 0; # Debug level $LOG = *STDOUT; # Where to log use lib qw (lib); use common qw (env_init); use pb qw (pb_init); use distro qw (distro_init); use cms; use changelog qw (changelog); my %opts; # CLI Options my $action; # action to realize my $test = "FALSE"; my $option = ""; my @pkgs; my $pbtag; # Global TAG variable my $pbver; # Global VERSION variable my $pbrev; # GLOBAL REVISION variable my @date=(localtime->sec(), localtime->min(), localtime->hour(), localtime->mday(), localtime->mon(), localtime->year(), localtime->wday(), localtime->yday(), localtime->isdst()); my $pbdate = strftime("%Y-%m-%d", @date); getopts('hl:p:qtv',\%opts); if (defined $opts{'h'}) { syntax(); exit(0); } if (defined $opts{'v'}) { $debug++; } if (defined $opts{'q'}) { $debug=-1; } if (defined $opts{'l'}) { open(LOG,"> $opts{'l'}") || die "Unable to log to $opts{'l'}: $!"; $LOG = *LOG; $debug = 0 if ($debug == -1); } # Handles project name if any if (defined $opts{'p'}) { $ENV{'PBPROJ'} = env_init($opts{'p'}); } else { $ENV{'PBPROJ'} = env_init(); } # Handles test option if (defined $opts{'t'}) { $test = "TRUE"; # Works only for SVN $option = "-r BASE"; } # Get Action $action = shift @ARGV; die syntax() if (not defined $action); print $LOG "Project $ENV{'PBPROJ'}\n" if ($debug >= 0); print $LOG "Action: $action\n" if ($debug >= 0); # Act depending on action if ($action =~ /^cms2build$/) { my $ptr = get_pkg(); @pkgs = @$ptr; cms_init(); foreach my $pkg (@pkgs) { if (-f "$ENV{'PBROOT'}/$pkg/VERSION") { open(V,"$ENV{'PBROOT'}/$pkg/VERSION") || die "Unable to open $ENV{'PBROOT'}/$pkg/VERSION"; $pbver = ; chomp($pbver); close(V); } else { $pbver = $ENV{'PBVER'}; } if (-f "$ENV{'PBROOT'}/$pkg/TAG") { open(T,"$ENV{'PBROOT'}/$pkg/TAG") || die "Unable to open $ENV{'PBROOT'}/$pkg/TAG"; $pbtag = ; chomp($pbtag); close(T); } else { $pbtag = $ENV{'PBTAG'}; } $pbrev = $ENV{'PBREVISION'}; print $LOG "\n" if ($debug >= 0); print $LOG "Management of $pkg $pbver-$pbtag (rev $pbrev)\n" if ($debug >= 0); die "Unable to get env var PBDESTDIR" if (not defined $ENV{'PBDESTDIR'}); # Clean up dest if necessary. The export will recreate it my $dest = "$ENV{'PBDESTDIR'}/$pkg-$pbver"; pbrm_rf($dest) if (-d $dest); # Export CMS tree for the concerned package to dest # And generate some additional files $OUTPUT_AUTOFLUSH=1; print $LOG "$ENV{'PBCMSEXP'} of $pkg..." if ($debug >= 0); # computes in which dir we have to work my $dir = $defpkgdir{$pkg}; $dir = $extpkgdir{$pkg} if (not defined $dir); system("$ENV{'PBCMSEXP'} $option $ENV{'PBROOT'}/$dir $dest 1>/dev/null"); if ($? == -1) { print $LOG "failed to execute: $!\n" if ($debug >= 0); } elsif ($? & 127) { printf $LOG "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without' if ($debug >= 0); } else { print $LOG " OK\n" if ($debug >= 0); } # Creates a REVISION file open(R,"> $dest/REVISION") || die "Unable to create $dest/REVISION"; print R "$pbrev\n"; close(R); # Extract cms log history and store it system("$ENV{'PBCMSLOG'} $option $ENV{'PBROOT'}/$dir > $dest/$ENV{'PBCMSLOGFILE'}"); print $LOG "$ENV{'PBCMSLOG'} of $pkg..." if ($debug >= 0); if ($? == -1) { print $LOG "failed to execute: $!\n" if ($debug >= 0); } elsif ($? & 127) { printf $LOG "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without' if ($debug >= 0); } else { print $LOG " OK\n" if ($debug >= 0); } my %build; open(D,"$ENV{'PBCONF'}/DISTROS") || die "Unable to find $ENV{'PBCONF'}/DISTROS\n"; while () { my $d = $_; my ($dir,$ver) = split(/_/,$d); chomp($ver); my ($ddir, $dver, $dfam, $dtype, $dsuf) = distro_init($dir,$ver); print $LOG "DEBUG: distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $dsuf)."\n" if ($debug >= 1); print $LOG "DEBUG Filtering PBDATE => $pbdate, PBTAG => $pbtag, PBVER => $pbver\n" if ($debug >= 1); # Filter build files from the less precise up to the most with overloading # Filter all files found, keeping the name, and generating in dest # Find all build files first relatively to PBROOT my %bfiles; print $LOG "DEBUG dir: $ENV{'PBCONF'}/$pkg\n" if ($debug >= 1); $build{"$ddir-$dver"} = "yes"; if (-d "$ENV{'PBCONF'}/$pkg/$dtype") { opendir(BDIR,"$ENV{'PBCONF'}/$pkg/$dtype" || die "Unable to open dir $ENV{'PBCONF'}/$pkg/$dtype: $!"); foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pkg/$dtype/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } elsif (-d "$ENV{'PBCONF'}/$pkg/$dfam") { opendir(BDIR,"$ENV{'PBCONF'}/$pkg/$dfam" || die "Unable to open dir $ENV{'PBCONF'}/$pkg/$dfam: $!"); foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pkg/$dfam/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } elsif (-d "$ENV{'PBCONF'}/$pkg/$ddir") { opendir(BDIR,"$ENV{'PBCONF'}/$pkg/$ddir" || die "Unable to open dir $ENV{'PBCONF'}/$pkg/$ddir: $!"); foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pkg/$ddir/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } elsif (-d "$ENV{'PBCONF'}/$pkg/$ddir-$dver") { opendir(BDIR,"$ENV{'PBCONF'}/$pkg/$ddir-$dver" || die "Unable to open dir $ENV{'PBCONF'}/$pkg/$ddir-$dver: $!"); foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pkg/$ddir-$dver/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } else { $build{"$ddir-$dver"} = "no"; next; } print $LOG "DEBUG bfiles: ".Dumper(\%bfiles)."\n" if ($debug >= 1); # Get all filters to apply # They're cumulative from less specific to most specific # suffix is .pbf my @ffiles; my ($ffile0, $ffile1, $ffile2, $ffile3); if (-d "$ENV{'PBCONF'}/$pkg/pbfilter") { $ffile0 = "$ENV{'PBCONF'}/$pkg/pbfilter/$dtype.pbf" if (-f "$ENV{'PBCONF'}/$pkg/pbfilter/$dtype.pbf"); $ffile1 = "$ENV{'PBCONF'}/$pkg/pbfilter/$dfam.pbf" if (-f "$ENV{'PBCONF'}/$pkg/pbfilter/$dfam.pbf"); $ffile2 = "$ENV{'PBCONF'}/$pkg/pbfilter/$ddir.pbf" if (-f "$ENV{'PBCONF'}/$pkg/pbfilter/$ddir.pbf"); $ffile3 = "$ENV{'PBCONF'}/$pkg/pbfilter/$ddir-$dver.pbf" if (-f "$ENV{'PBCONF'}/$pkg/pbfilter/$ddir-$dver.pbf"); push @ffiles,$ffile0 if (defined $ffile0); push @ffiles,$ffile1 if (defined $ffile1); push @ffiles,$ffile2 if (defined $ffile2); push @ffiles,$ffile3 if (defined $ffile3); } my $config = AppConfig->new({ # Auto Create variables mentioned in Conf file CREATE => 1, DEBUG => 0, GLOBAL => { # Each conf item is a hash ARGCOUNT => AppConfig::ARGCOUNT_HASH } }); my $ptr; if (@ffiles) { print $LOG "DEBUG ffiles: ".Dumper(\@ffiles)."\n" if ($debug >= 1); $config->file(@ffiles); $ptr = $config->get("filter"); print $LOG "DEBUG f:".Dumper($ptr)."\n" if ($debug >= 1); } else { $ptr = { }; } # Apply now all the filters on all the files concerned # All files are relative to PBROOT # destination dir depends on the type of file if (defined $ptr) { foreach my $f (values %bfiles) { filter_file($f,$ptr,"$dest/pbconf/$ddir-$dver/".basename($f),$pkg,$dtype,$dsuf); } foreach my $f (keys %filteredfiles) { filter_file($f,$ptr,"$dest/$f",$pkg,$dtype,$dsuf); } } } if ($debug >= 0) { my @found; my @notfound; foreach my $b (keys %build) { push @found,$b if ($build{$b} =~ /yes/); push @notfound,$b if ($build{$b} =~ /no/); } print $LOG "Build files generated for ".join(',',@found)."\n"; print $LOG "No Build files found for ".join(',',@notfound)."\n"; } close(D); # Prepare the dest directory for archive if (-x "$ENV{'PBCONF'}/$pkg/pbpkginit") { print $LOG " Executing $ENV{'PBCONF'}/$pkg/pbinit...\n" if ($debug >= 0); system("cd $dest ; $ENV{'PBCONF'}/$pkg/pbinit"); if ($? == -1) { print $LOG "failed to execute: $!\n" if ($debug >= 0); } elsif ($? & 127) { printf $LOG "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without' if ($debug >= 0); } else { print $LOG " OK\n" if ($debug >= 0); } } # Archive dest dir chdir "$ENV{'PBDESTDIR'}"; print $LOG "Creating $pkg tar files (gzip... " if ($debug >= 0); system("tar cfphz $pkg-$pbver.tar.gz $pkg-$pbver"); if ($? == -1) { print $LOG "failed to execute: $!\n" if ($debug >= 0); } elsif ($? & 127) { printf $LOG "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without' if ($debug >= 0); } else { print $LOG " OK)\n" if ($debug >= 0); print $LOG "Under $ENV{'PBDESTDIR'}/$pkg-$pbver.tar.gz\n" if ($debug >= 0); # Keep track of what is generated for build2pkg default open(LAST,"> $ENV{'PBDESTDIR'}/LAST") || die "Unable to create $ENV{'PBDESTDIR'}/LAST"; print LAST "$pbver-$pbtag\n"; close(LAST); } } } elsif ($action =~ /^build2pkg$/) { # Check whether we have a specific version to build my $vertag = shift @ARGV; if (not defined $vertag) { open(LAST,"$ENV{'PBDESTDIR'}/LAST") || die "Unable to open $ENV{'PBDESTDIR'}/LAST\nYou may want to precise as parameter version-tag"; $vertag = ; chomp($vertag); close(LAST); } ($pbver,$pbtag) = split(/-/,$vertag); # Get list of packages to build my $ptr = get_pkg(); @pkgs = @$ptr; # Get the running distro to build on foreach my $pkg (@pkgs) { } } else { print $LOG "'$action' is not available\n"; syntax(); } # Function which applies filter on files sub filter_file { my $f=shift; my $ptr=shift; my %filter=%$ptr; my $destfile=shift; my $pkg=shift; my $dtype=shift; my $dsuf=shift; print $LOG "DEBUG: From $f to $destfile\n" if ($debug >= 1); pbmkdir_p(dirname($destfile)) if (! -d dirname($destfile)); open(DEST,"> $destfile") || die "Unable to create $destfile"; open(FILE,"$ENV{'PBROOT'}/$f") || die "Unable to open $f: $!"; while () { my $line = $_; foreach my $s (keys %filter) { # Process single variables print $LOG "DEBUG filter{$s}: $filter{$s}\n" if ($debug > 1); my $tmp = $filter{$s}; next if (not defined $tmp); # Expand variables if any single one found if ($tmp =~ /\$/) { eval { $tmp =~ s/(\$\w+)/$1/eeg }; # special case for ChangeLog } elsif (($tmp =~ /^yes$/) && ($s =~ /^PBLOG$/) && ($line =~ /^PBLOG$/)) { my $p = $defpkgdir{$pkg}; $p = $extpkgdir{$pkg} if (not defined $p); changelog($dtype, $pkg, $pbtag, $dsuf, $p, \*DEST); $tmp = ""; } $line =~ s|$s|$tmp|; } print DEST $line; } close(FILE); close(DEST); } sub get_pkg { my @pkgs; # Get packages list if (not defined $ARGV[0]) { @pkgs = keys %defpkgdir; } elsif ($ARGV[0] =~ /^all$/) { @pkgs = keys %defpkgdir; if (defined %extpkgdir) { my $k = keys %extpkgdir; if (defined $k) { push(@pkgs, keys %extpkgdir); } } } else { @pkgs = @ARGV; } print $LOG "Packages: ".join(',',@pkgs)."\n" if ($debug >= 0); return(\@pkgs); } sub syntax { print "Syntax: pb [-vhqt][-p project] [...]\n"; print "\n"; print "-h : This help file\n"; print "-q : Quiet mode\n"; print "-t : Test mode (not done yet)\n"; print "-v : Verbose mode\n"; print "\n"; print "-p project : Name of the project you're working on\n"; print " (or use the env variable PBPROJ) \n"; print "\n"; print " can be:\n"; print "\n"; print "\tcms2build: Create a tar file of the project under your CMS\n"; print "\t CMS supported are SVN and CVS\n"; print "\t parameters are packages to build\n"; print "\t if not using default list\n"; print "\n"; print "\tbuild2pkg: Create packages for your running distribution \n"; print "\t first parameter is version-tag to build\n"; print "\t if not using default version-tag\n"; print "\t following parameters are packages to build\n"; print "\t if not using default list\n"; print "\n"; print "\n"; }