#!/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 File::Copy; use Time::localtime qw(localtime); use POSIX qw(strftime); # Global variables use vars qw (%defpkgdir %extpkgdir %filteredfiles %pbrc $debug $LOG); $debug = 0; # Debug level $LOG = *STDOUT; # Where to log use lib qw (lib); use ProjectBuilder::Distribution qw (pb_distro_init); use ProjectBuilder::Changelog qw (pb_changelog); use ProjectBuilder::Version qw (pb_version_init); use ProjectBuilder::Base qw (pb_conf_read pb_conf_get pb_cms_init pb_mkdir_p pb_system pb_rm_rf pb_get_filters pb_filter_file pb_filter_file_pb); 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 %pbver; # per package my %pbtag; # per package 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:m:p:qr:tv',\%opts); my ($projectbuilderver,$projectbuilderrev) = pb_version_init(); if (defined $opts{'h'}) { pb_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 test option if (defined $opts{'t'}) { $test = "TRUE"; # Works only for SVN $option = "-r BASE"; } # Get Action $action = shift @ARGV; die pb_syntax() if (not defined $action); # Handle root of the project if defined if (defined $opts{'r'}) { $ENV{'PBROOT'} = $opts{'r'}; } # Handle virtual machines if any if (defined $opts{'m'}) { $ENV{'PBVM'} = $opts{'m'}; } # Handles project name if any if (defined $opts{'p'}) { $ENV{'PBPROJ'} = pb_env_init($opts{'p'}); } else { $ENV{'PBPROJ'} = pb_env_init(); } print $LOG "Project $ENV{'PBPROJ'}\n" if ($debug >= 0); print $LOG "Action: $action\n" if ($debug >= 0); # Keep those project values to store them at the end each time my $pbprojtag = $ENV{'PBTAG'}; my $pbprojver = $ENV{'PBVER'}; # Act depending on action if ($action =~ /^cms2build$/) { pb_cms2build(); } elsif ($action =~ /^build2pkg$/) { pb_build2pkg(); } elsif ($action =~ /^cms2pkg$/) { pb_cms2build(); pb_build2pkg(); } elsif ($action =~ /^build2ssh$/) { pb_build2ssh(); } elsif ($action =~ /^pkg2ssh$/) { pb_pkg2ssh(); } elsif ($action =~ /^build2vm$/) { pb_build2vm(); } elsif ($action =~ /^cms2vm$/) { pb_cms2build(); pb_build2vm(); } else { print $LOG "'$action' is not available\n"; pb_syntax(); } sub pb_cms2build { my $ptr = pb_get_pkg(); @pkgs = @$ptr; pb_cms_init($ENV{'PBPROJ'}); foreach my $pbpkg (@pkgs) { if (-f "$ENV{'PBROOT'}/$pbpkg/VERSION") { open(V,"$ENV{'PBROOT'}/$pbpkg/VERSION") || die "Unable to open $ENV{'PBROOT'}/$pbpkg/VERSION"; $pbver = ; chomp($pbver); close(V); } else { $pbver = $ENV{'PBVER'}; } if (-f "$ENV{'PBROOT'}/$pbpkg/TAG") { open(T,"$ENV{'PBROOT'}/$pbpkg/TAG") || die "Unable to open $ENV{'PBROOT'}/$pbpkg/TAG"; $pbtag = ; chomp($pbtag); close(T); } else { $pbtag = $ENV{'PBTAG'}; } $pbrev = $ENV{'PBREVISION'}; print $LOG "\n" if ($debug >= 0); print $LOG "Management of $pbpkg $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'}/$pbpkg-$pbver"; pb_rm_rf($dest) if (-d $dest); # Export CMS tree for the concerned package to dest # And generate some additional files $OUTPUT_AUTOFLUSH=1; # computes in which dir we have to work my $dir = $defpkgdir{$pbpkg}; $dir = $extpkgdir{$pbpkg} if (not defined $dir); pb_system("$ENV{'PBCMSEXP'} $option $ENV{'PBROOT'}/$dir $dest 1>/dev/null", "Exporting $ENV{'PBROOT'}/$dir"); # 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 pb_system("$ENV{'PBCMSLOG'} $option $ENV{'PBROOT'}/$dir > $dest/$ENV{'PBCMSLOGFILE'}", "Extracting log info"); my %build; my ($ptr) = pb_conf_get("vmlist"); foreach my $d (split(/,/,$ptr->{$ENV{'PBPROJ'}})) { my ($name,$ver) = split(/_/,$d); chomp($ver); my ($ddir, $dver, $dfam, $dtype, $dsuf) = pb_distro_init($name,$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'}/$pbpkg\n" if ($debug >= 1); $build{"$ddir-$dver"} = "yes"; if (-d "$ENV{'PBCONF'}/$pbpkg/$dtype") { opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$dtype") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$dtype: $!"; foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$dtype/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } elsif (-d "$ENV{'PBCONF'}/$pbpkg/$dfam") { opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$dfam") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$dfam: $!"; foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$dfam/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } elsif (-d "$ENV{'PBCONF'}/$pbpkg/$ddir") { opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$ddir") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$ddir: $!"; foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$ddir/$f"; $bfiles{$f} =~ s~$ENV{'PBROOT'}~~; } closedir(BDIR); } elsif (-d "$ENV{'PBCONF'}/$pbpkg/$ddir-$dver") { opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$ddir-$dver") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$ddir-$dver: $!"; foreach my $f (readdir(BDIR)) { next if ($f =~ /^\./); $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$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 my $ptr = pb_get_filters($pbpkg, $dtype, $dfam, $ddir, $dver); # Apply now all the filters on all the files concerned # destination dir depends on the type of file if (defined $ptr) { foreach my $f (values %bfiles) { pb_filter_file_pb("$ENV{'PBROOT'}/$f",$ptr,"$dest/pbconf/$ddir-$dver/".basename($f),$dtype,$dsuf,$pbpkg,$pbver,$pbtag,$pbrev,$pbdate); } if (defined $filteredfiles{$pbpkg}) { foreach my $f (split(/,/,$filteredfiles{$pbpkg})) { pb_filter_file("$ENV{'PBROOT'}/$dir/$f",$ptr,"$dest/$f",$pbpkg,$pbver,$pbtag,$pbrev,$pbdate); } } } } 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"; } # Prepare the dest directory for archive if (-x "$ENV{'PBCONF'}/$pbpkg/pbinit") { pb_system("cd $dest ; $ENV{'PBCONF'}/$pbpkg/pbinit","Executing init script $ENV{'PBCONF'}/$pbpkg/pbinit"); } # Archive dest dir chdir "$ENV{'PBDESTDIR'}" || die "Unable to change dir to $ENV{'PBDESTDIR'}"; # Possibility to look at PBSRC to guess more the filename pb_system("tar cfpz $pbpkg-$pbver.tar.gz $pbpkg-$pbver","Creating $pbpkg tar files compressed"); print $LOG "Under $ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz\n" if ($debug >= 0); # Keep track of what is generated for default open(LAST,"> $pbrc{$ENV{'PBPROJ'}}") || die "Unable to create $pbrc{$ENV{'PBPROJ'}}"; print LAST "pbroot $pbprojver-$pbprojtag = $ENV{'PBROOT'}\n"; close(LAST); # Keep track of per package version if (! -f "$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb") { open(PKG,">$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb") || die "Unable to create $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb"; print PKG "# Empty\n"; close(PKG); } my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb","pbpkg"); $pkg = { } if (not defined $pkg); if ((not defined $pkg->{$pbpkg}) || ($pkg->{$pbpkg} ne "$pbver-$pbtag")) { $pkg->{$pbpkg} = "$pbver-$pbtag"; } print $LOG "DEBUG pkg: ".Dumper($pkg)."\n" if ($debug >= 1); open(PKG,"> $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb") || die "Unable to create $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb"; foreach my $p (keys %$pkg) { print PKG "pbpkg $p = $pkg->{$p}\n"; } close(PKG); } } sub pb_build2pkg { # Get list of packages to build my $ptr = pb_get_pkg(); @pkgs = @$ptr; # Get the running distro to build on my ($ddir, $dver, $dfam, $dtype, $dsuf) = pb_distro_init(); print $LOG "DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $dsuf))."\n" if ($debug >= 1); # Get content saved in cms2build my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb","pbpkg"); $pkg = { } if (not defined $pkg); chdir "$ENV{'PBBUILDDIR'}"; foreach my $pbpkg (@pkgs) { my $vertag = $pkg->{$pbpkg}; # get the version of the current package - maybe different ($pbver,$pbtag) = split(/-/,$vertag); my $src="$ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz"; print $LOG "Source file: $src\n" if ($debug >= 0); if ($dtype eq "rpm") { # rpm has its own standard build directory my $tmp=`rpmquery --eval '%{_topdir}' 2> /dev/null`; chomp($tmp); $ENV{'PBBUILDDIR'}=$tmp; print $LOG "Working under $ENV{'PBBUILDDIR'}\n" if ($debug >= 0); foreach my $d ('RPMS','SRPMS','SPECS','SOURCES','BUILD') { if (! -d "$ENV{'PBBUILDDIR'}/$d") { pb_mkdir_p("$ENV{'PBBUILDDIR'}/$d") || die "Please ensure that you can write into $ENV{'PBBUILDDIR'} to create $d\nSolution: setup _topdir in your ~/.rpmmacros or\nchown the $ENV{'PBBUILDDIR'} directory to your uid"; } } # We need to first extract the spec file symlink "$src","$ENV{'PBBUILDDIR'}/SOURCES/".basename($src) || die "Unable to symlink $src in $ENV{'PBBUILDDIR'}/SOURCES"; my @specfile; @specfile = pb_extract_build_files($src,"$pbpkg-$pbver/pbconf/$ddir-$dver/","$ENV{'PBBUILDDIR'}/SPECS"); print $LOG "specfile: ".Dumper(\@specfile)."\n" if ($debug >= 1); # set LANGUAGE to check for correct log messages $ENV{'LANGUAGE'}="C"; #system("ls -R $ENV{'PBBUILDDIR'}") if ($debug >= 1); foreach my $f (@specfile) { if ($f =~ /\.spec$/) { pb_system("rpmbuild -ba $f","Building package with $f"); last; } } } elsif ($dtype eq "tgz") { pb_mkdir_p("$ENV{'PBBUILDDIR'}/install") if (! -d "$ENV{'PBBUILDDIR'}/install"); } elsif ($dtype eq "ebuild") { pb_mkdir_p("$ENV{'PBBUILDDIR'}/portage") if (! -d "$ENV{'PBBUILDDIR'}/portage"); } else { } } } sub pb_build2ssh { pb_send2ssh("Sources"); } sub pb_pkg2ssh { pb_send2ssh("Packages"); } sub pb_send2ssh { my $cmt = shift; my @src; # Get list of packages to build my $ptr = pb_get_pkg(); @pkgs = @$ptr; # Get the running distro to build on my ($ddir, $dver, $dfam, $dtype, $dsuf) = pb_distro_init(); print $LOG "DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $dsuf))."\n" if ($debug >= 1); # Get content saved in cms2build my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb","pbpkg"); $pkg = { } if (not defined $pkg); chdir "$ENV{'PBBUILDDIR'}"; my $src; foreach my $pbpkg (@pkgs) { my $vertag = $pkg->{$pbpkg}; # get the version of the current package - maybe different ($pbver,$pbtag) = split(/-/,$vertag); if ($cmt eq "Sources") { $src="$ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz"; } elsif ($cmt eq "Packages") { if ($dtype eq "rpm") { $src="$ENV{'PBBUILDDIR'}/RPMS/*/$pbpkg$pbver-$pbtag$dsuf.*.rpm $ENV{'PBBUILDDIR'}/SRPMS/$pbpkg$pbver-$pbtag$dsuf.src.rpm" } elsif ($dtype eq "deb") { my $tmp = "$ENV{'PBBUILDDIR'}/$pbpkg"; $src="$tmp"."_*.deb $tmp"."_*.dsc $tmp"."_*.tar.gz" } elsif ($dtype eq "ebuild") { $src="$ENV{'PBBUILDDIR'}/portage/*/$pbpkg/$pbpkg$pbver.ebuild" } elsif ($dtype eq "slackware") { $src="$ENV{'PBBUILDDIR'}/build-$pbpkg/$pbpkg$pbver-*-$pbtag.tgz" } else { die "Unknown dtype format $dtype"; } } print $LOG "$cmt: $src\n" if ($debug >= 0); push @src, $src; } my ($pt) = pb_conf_get("sshhost", "sshlogin", "sshdir"); my ($sshhost,$sshlogin,$sshdir) = @$pt; my $mac = "$sshlogin->{$ENV{'PBPROJ'}}\@$sshhost->{$ENV{'PBPROJ'}}"; my $dir; if ($cmt eq "Sources") { $dir = "$sshdir->{$ENV{'PBPROJ'}}/src"; } elsif ($cmt eq "Packages") { $dir = "$sshdir->{$ENV{'PBPROJ'}}/$ddir/$dver"; } else { return; } $src = join(' ',@src); pb_system("ssh -q $mac \"mkdir -p $dir ; cd $dir ; rm -f $src\"","Preparing $dir on $mac"); pb_system("scp -p $src $mac:$dir","$cmt delivery in $dir on $mac"); } sub pb_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 pb_get_vm { my @vm; # Get VM list if (not defined $ENV{'PBVM'}) { @vm = keys %defpkgdir; } elsif ($ENV{'PBVM'} =~ /^all$/) { @vm = keys %defpkgdir; if (defined %extpkgdir) { my $k = keys %extpkgdir; if (defined $k) { push(@vm, keys %extpkgdir); } } } else { @vm = $ENV{'PBVM'}; } print $LOG "VMs: ".join(',',@vm)."\n" if ($debug >= 0); return(\@vm); } sub pb_extract_build_files { my $src=shift; my $dir=shift; my $ddir=shift; my @files; pb_system("tar xfpz $src $dir >/dev/null","Extracting build files"); opendir(DIR,"$dir") || die "Unable to open directory $dir"; foreach my $f (readdir(DIR)) { next if ($f =~ /^\./); move("$dir/$f","$ddir") || die "Unable to move $dir/$f to $ddir"; print $LOG "mv $dir/$f $ddir\n" if ($debug >= 1); push @files,"$ddir/$f"; } closedir(DIR); # Not enough but still a first cleanup pb_rm_rf("$dir"); return(@files); } sub pb_syntax { print "pb (aka project-builder) Version $projectbuilderver-$projectbuilderrev\n"; print "\n"; print "Syntax: pb [-vhqt][-r pbroot][-p project][-m \"mach-1...\"] [...]\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 "-m machine : Name of the virtual Machines you want\n"; print " to build on (space separated) \n"; print " (or use the env variable PBVM) \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 "-r pbroot : Path Name of project under the CMS \n"; print " (or use the env variable PBROOT) \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 "\tcms2pkg: cms2build + build2pkg\n"; print "\n"; }