source: devel/pb/bin/pb @ 300

Last change on this file since 300 was 300, checked in by Bruno Cornec, 12 years ago

Adds support for supplemental files in projects (such as init scripts different for different distributions such as in collectl)

  • Property svn:executable set to *
File size: 33.1 KB
Line 
1#!/usr/bin/perl -w
2#
3# Project Builder main application
4#
5# $Id$
6#
7# Copyright B. Cornec 2007
8# Provided under the GPL v2
9
10# Syntax: see at end
11
12use strict 'vars';
13use Getopt::Std;
14use Data::Dumper;
15use English;
16use File::Basename;
17use File::Copy;
18use Time::localtime qw(localtime);
19use POSIX qw(strftime);
20
21# Global variables
22use lib qw (lib);
23use ProjectBuilder::Distribution qw (pb_distro_init);
24use ProjectBuilder::Version qw (pb_version_init);
25use 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 pb_cms_export pb_cms_log pb_cms_isdiff pb_cms_copy pb_cms_checkout);
26
27my %opts;                   # CLI Options
28my $action;                 # action to realize
29my $test = "FALSE";
30my $option = "";
31my @pkgs;
32my $pbtag;                  # Global Tag variable
33my $pbver;                  # Global Version variable
34my $pbscript;               # Name of the script
35my %pbver;                  # per package
36my %pbtag;                  # per package
37my $pbrev;                  # Global REVISION variable
38my @date=(localtime->sec(), localtime->min(), localtime->hour(), localtime->mday(), localtime->mon(), localtime->year(), localtime->wday(), localtime->yday(), localtime->isdst());
39my $pbdate = strftime("%Y-%m-%d", @date);
40my $pbdatecvs = strftime("%Y-%m-%d %H:%M:%S", @date);
41my $debug = 0;
42my $pbaccount;              # Login to use to connect to the VM
43my $pbport;                 # Port to use to connect to the VM
44my $newver;                 # New version to create
45my $iso;                    # ISO iage for the VM to create
46my $LOG = \*STDOUT;
47
48getopts('a:hi:l:m:P:p:qr:s:tvV:',\%opts);
49
50my ($projectbuilderver,$projectbuilderrev) = pb_version_init();
51if (defined $opts{'h'}) {
52    pb_syntax();
53    exit(0);
54}
55if (defined $opts{'v'}) {
56    $debug++;
57}
58if (defined $opts{'q'}) {
59    $debug=-1;
60}
61if (defined $opts{'l'}) {
62    open(LOG,"> $opts{'l'}") || die "Unable to log to $opts{'l'}: $!";
63    $LOG = *LOG;
64    $debug = 0  if ($debug == -1);
65    }
66# Handles test option
67if (defined $opts{'t'}) {
68    $test = "TRUE";
69    # Works only for SVN
70    $option = "-r BASE";
71}
72
73# Handle root of the project if defined
74if (defined $opts{'r'}) {
75    $ENV{'PBROOT'} = $opts{'r'};
76}
77# Handle virtual machines if any
78if (defined $opts{'m'}) {
79    $ENV{'PBVM'} = $opts{'m'};
80}
81if (defined $opts{'s'}) {
82    $pbscript = $opts{'s'};
83}
84if (defined $opts{'a'}) {
85    $pbaccount = $opts{'a'};
86}
87if (defined $opts{'P'}) {
88    $pbport = $opts{'P'};
89}
90if (defined $opts{'V'}) {
91    $newver = $opts{'V'};
92}
93if (defined $opts{'i'}) {
94    $iso = $opts{'i'};
95}
96
97# Get Action
98$action = shift @ARGV;
99die pb_syntax() if (not defined $action);
100
101my ($pbrc, $filteredfiles, $supfiles, $defpkgdir, $extpkgdir);
102my $pbinit = undef;
103$pbinit = 1 if ($action =~ /^newproj$/);
104
105# Handles project name if any
106# And get global params
107if (defined $opts{'p'}) {
108    ($ENV{'PBPROJ'},$debug,$LOG, $pbrc, $filteredfiles, $supfiles, $defpkgdir, $extpkgdir) 
109    = pb_env_init($opts{'p'},$pbinit);
110} else {
111    ($ENV{'PBPROJ'},$debug,$LOG, $pbrc, $filteredfiles, $supfiles, $defpkgdir, $extpkgdir) 
112    = pb_env_init(undef,$pbinit);
113}
114
115print $LOG "Project: $ENV{'PBPROJ'}\n" if ($debug >= 0);
116print $LOG "Action: $action\n" if ($debug >= 0);
117
118# Keep those project values to store them at the end each time
119my $pbprojtag = $ENV{'PBTAG'};
120my $pbprojver = $ENV{'PBVER'};
121
122# Act depending on action
123if ($action =~ /^cms2build$/) {
124    pb_cms2build();
125} elsif ($action =~ /^build2pkg$/) {
126    pb_build2pkg();
127} elsif ($action =~ /^cms2pkg$/) {
128    pb_cms2build();
129    pb_build2pkg();
130} elsif ($action =~ /^build2ssh$/) {
131    pb_build2ssh();
132} elsif ($action =~ /^cms2ssh$/) {
133    pb_cms2build();
134    pb_build2ssh();
135} elsif ($action =~ /^pkg2ssh$/) {
136    pb_pkg2ssh();
137} elsif ($action =~ /^build2vm$/) {
138    pb_build2vm();
139} elsif ($action =~ /^cms2vm$/) {
140    pb_cms2build();
141    pb_build2vm();
142} elsif ($action =~ /^launchvm$/) {
143    pb_launchvm($ENV{'PBVM'},0);
144} elsif ($action =~ /^script2vm$/) {
145    pb_script2vm($pbscript);
146} elsif ($action =~ /^newver$/) {
147    pb_newver();
148} elsif ($action =~ /^newvm$/) {
149    pb_launchvm($ENV{'PBVM'},1);
150} elsif ($action =~ /^newproj$/) {
151    # Nothing to do - already done in pb_env_init
152} elsif ($action =~ /^clean$/) {
153} else {
154    print $LOG "'$action' is not available\n";
155    pb_syntax();
156}
157
158sub pb_cms2build {
159
160    my $ptr = pb_get_pkg($defpkgdir,$extpkgdir);
161    @pkgs = @$ptr;
162    my $cms=pb_cms_init($ENV{'PBPROJ'});
163
164    my ($pkgv, $pkgt) = pb_conf_read("$ENV{'PBCONF'}/$ENV{'PBPROJ'}.pb","pkgver","pkgtag");
165
166    # declare packager for filtering
167    my ($tmp) = pb_conf_get("packager");
168    my $pbpackager = $tmp->{$ENV{'PBPROJ'}};
169
170    foreach my $pbpkg (@pkgs) {
171        $ENV{'PBPKG'} = $pbpkg;
172        $ENV{'PBVER'} = $pbprojver;
173        $ENV{'PBTAG'} = $pbprojtag;
174        if ((defined $pkgv) && (defined $pkgv->{$pbpkg})) {
175            $pbver = $pkgv->{$pbpkg};
176            $ENV{'PBVER'} = $pbver;
177        } else {
178            $pbver = $ENV{'PBVER'};
179        }
180        if ((defined $pkgt) && (defined $pkgt->{$pbpkg})) {
181            $pbtag = $pkgt->{$pbpkg};
182            $ENV{'PBTAG'} = $pbtag;
183        } else {
184            $pbtag = $ENV{'PBTAG'};
185        }
186
187        $pbrev = $ENV{'PBREVISION'};
188        print $LOG "\n";
189        print $LOG "Management of $pbpkg $pbver-$pbtag (rev $pbrev)\n";
190        die "Unable to get env var PBDESTDIR" if (not defined $ENV{'PBDESTDIR'});
191        # Clean up dest if necessary. The export will recreate it
192        my $dest = "$ENV{'PBDESTDIR'}/$pbpkg-$pbver";
193        pb_rm_rf($dest) if (-d $dest);
194
195        # Export CMS tree for the concerned package to dest
196        # And generate some additional files
197        $OUTPUT_AUTOFLUSH=1;
198
199        # computes in which dir we have to work
200        my $dir = $defpkgdir->{$pbpkg};
201        $dir = $extpkgdir->{$pbpkg} if (not defined $dir);
202        print "def:".Dumper($defpkgdir)." ext: ".Dumper($extpkgdir)." \n" if ($debug >= 1);
203        pb_cms_export($cms,$pbdatecvs,"$ENV{'PBROOT'}/$dir",$dest);
204
205        # Get project info on authors and log file
206        my $chglog = "$ENV{'PBCONF'}/$pbpkg/pbcl";
207        $chglog = "$ENV{'PBCONF'}/pbcl" if (! -f $chglog);
208        $chglog = undef if (! -f $chglog);
209
210        my $authors = "$ENV{'PBCONF'}/$pbpkg/pbauthors";
211        $authors = "$ENV{'PBCONF'}/pbauthors" if (! -f $authors);
212        $authors = "/dev/null" if (! -f $authors);
213
214        # Extract cms log history and store it
215        if ((defined $chglog) && (! -f "$dest/NEWS")) {
216            print $LOG "Generating NEWS file from $chglog\n";
217            copy($chglog,"$dest/NEWS") || die "Unable to create $dest/NEWS";
218        }
219        pb_cms_log($cms,"$ENV{'PBROOT'}/$dir",$dest,$chglog,$authors);
220
221        my %build;
222
223        my ($ptr) = pb_conf_get("vmlist");
224        foreach my $d (split(/,/,$ptr->{$ENV{'PBPROJ'}})) {
225            my ($name,$ver) = split(/_/,$d);
226            chomp($ver);
227            my ($ddir, $dver, $dfam, $dtype, $pbsuf) = pb_distro_init($name,$ver);
228            print $LOG "DEBUG: distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf)."\n" if ($debug >= 1);
229            print $LOG "DEBUG Filtering PBDATE => $pbdate, PBTAG => $pbtag, PBVER => $pbver\n" if ($debug >= 1);
230
231            # Filter build files from the less precise up to the most with overloading
232            # Filter all files found, keeping the name, and generating in dest
233
234            # Find all build files first relatively to PBROOT
235            # Find also all specific files referenced in the .pb conf file
236            my %bfiles;
237            my %pkgfiles;
238            print $LOG "DEBUG dir: $ENV{'PBCONF'}/$pbpkg\n" if ($debug >= 1);
239            $build{"$ddir-$dver"} = "yes";
240            if (-d "$ENV{'PBCONF'}/$pbpkg/$dtype") {
241                opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$dtype") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$dtype: $!";
242                foreach my $f (readdir(BDIR)) {
243                    next if ($f =~ /^\./);
244                    $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$dtype/$f";
245                    $bfiles{$f} =~ s~$ENV{'PBROOT'}~~;
246                    if (defined $supfiles->{$pbpkg}) {
247                        $pkgfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$dtype/$f" if ($f =~ /$supfiles->{$pbpkg}/);
248                    }
249                }
250                closedir(BDIR);
251            } elsif (-d "$ENV{'PBCONF'}/$pbpkg/$dfam") {
252                opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$dfam") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$dfam: $!";
253                foreach my $f (readdir(BDIR)) {
254                    next if ($f =~ /^\./);
255                    $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$dfam/$f";
256                    $bfiles{$f} =~ s~$ENV{'PBROOT'}~~;
257                    if (defined $supfiles->{$pbpkg}) {
258                        $pkgfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$dfam/$f" if ($f =~ /$supfiles->{$pbpkg}/);
259                    }
260                }
261                closedir(BDIR);
262            } elsif (-d "$ENV{'PBCONF'}/$pbpkg/$ddir") {
263                opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$ddir") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$ddir: $!";
264                foreach my $f (readdir(BDIR)) {
265                    next if ($f =~ /^\./);
266                    $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$ddir/$f";
267                    $bfiles{$f} =~ s~$ENV{'PBROOT'}~~;
268                    if (defined $supfiles->{$pbpkg}) {
269                        $pkgfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$ddir/$f" if ($f =~ /$supfiles->{$pbpkg}/);
270                    }
271                }
272                closedir(BDIR);
273            } elsif (-d "$ENV{'PBCONF'}/$pbpkg/$ddir-$dver") {
274                opendir(BDIR,"$ENV{'PBCONF'}/$pbpkg/$ddir-$dver") || die "Unable to open dir $ENV{'PBCONF'}/$pbpkg/$ddir-$dver: $!";
275                foreach my $f (readdir(BDIR)) {
276                    next if ($f =~ /^\./);
277                    $bfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$ddir-$dver/$f";
278                    $bfiles{$f} =~ s~$ENV{'PBROOT'}~~;
279                    if (defined $supfiles->{$pbpkg}) {
280                        $pkgfiles{$f} = "$ENV{'PBCONF'}/$pbpkg/$ddir-$dver/$f" if ($f =~ /$supfiles->{$pbpkg}/);
281                    }
282                }
283                closedir(BDIR);
284            } else {
285                $build{"$ddir-$dver"} = "no";
286                next;
287            }
288            print $LOG "DEBUG bfiles: ".Dumper(\%bfiles)."\n" if ($debug >= 1);
289
290            # Get all filters to apply
291            my $ptr = pb_get_filters($pbpkg, $dtype, $dfam, $ddir, $dver);
292
293            # Apply now all the filters on all the files concerned
294            # destination dir depends on the type of file
295            if (defined $ptr) {
296                foreach my $f (values %bfiles,values %pkgfiles) {
297                    pb_filter_file_pb("$ENV{'PBROOT'}/$f",$ptr,"$dest/pbconf/$ddir-$dver/".basename($f),$dtype,$pbsuf,$ENV{'PBPROJ'},$pbpkg,$pbver,$pbtag,$pbrev,$pbdate,$defpkgdir,$extpkgdir,$pbpackager,$chglog);
298                }
299            }
300        }
301        if ($debug >= 0) {
302            my @found;
303            my @notfound;
304            foreach my $b (keys %build) {
305                push @found,$b if ($build{$b} =~ /yes/);
306                push @notfound,$b if ($build{$b} =~ /no/);
307            }
308            print $LOG "Build files generated for ".join(',',@found)."\n";
309            print $LOG "No Build files found for ".join(',',@notfound)."\n";
310        }
311        # Get the generic filter (all.pbf) and
312        # apply those to the non-build files including those
313        # generated by pbinit if applicable
314
315        # Get only all.pbf filter
316        $ptr = pb_get_filters($pbpkg);
317
318        my $liste ="";
319        if (defined $filteredfiles->{$pbpkg}) {
320            foreach my $f (split(/,/,$filteredfiles->{$pbpkg})) {
321                pb_filter_file("$ENV{'PBROOT'}/$dir/$f",$ptr,"$dest/$f",$ENV{'PBPROJ'},$pbpkg,$pbver,$pbtag,$pbrev,$pbdate,$pbpackager);
322                $liste = "$f $liste";
323            }
324        }
325        print $LOG "Files ".$liste."have been filtered\n";
326
327        # Prepare the dest directory for archive
328        if (-x "$ENV{'PBCONF'}/$pbpkg/pbinit") {
329            pb_filter_file("$ENV{'PBCONF'}/$pbpkg/pbinit",$ptr,"$ENV{'PBTMP'}/pbinit",$ENV{'PBPROJ'},$pbpkg,$pbver,$pbtag,$pbrev,$pbdate,$pbpackager);
330            chmod 0755,"$ENV{'PBTMP'}/pbinit";
331            pb_system("cd $dest ; $ENV{'PBTMP'}/pbinit","Executing init script from $ENV{'PBCONF'}/$pbpkg/pbinit");
332        }
333
334        # Archive dest dir
335        chdir "$ENV{'PBDESTDIR'}" || die "Unable to change dir to $ENV{'PBDESTDIR'}";
336        # Possibility to look at PBSRC to guess more the filename
337        pb_system("tar cfz $pbpkg-$pbver.tar.gz $pbpkg-$pbver","Creating $pbpkg tar files compressed");
338        print $LOG "Under $ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz\n" if ($debug >= 0);
339
340        # Keep track of what is generated for default
341        open(LAST,"> $pbrc->{$ENV{'PBPROJ'}}") || die "Unable to create $pbrc->{$ENV{'PBPROJ'}}";
342        print LAST "pbroot $pbprojver-$pbprojtag = $ENV{'PBROOT'}\n";
343        close(LAST);
344
345        # Keep track of per package version
346        if (! -f "$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb") {
347            open(PKG,">$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb") || die "Unable to create $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb";
348            print PKG "# Empty\n";
349            close(PKG);
350        }
351        my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb","pbpkg");
352        $pkg = { } if (not defined $pkg);
353        if ((not defined $pkg->{$pbpkg}) || ($pkg->{$pbpkg} ne "$pbver-$pbtag")) {
354            $pkg->{$pbpkg} = "$pbver-$pbtag";
355        }
356
357        print $LOG "DEBUG pkg: ".Dumper($pkg)."\n" if ($debug >= 1);
358        open(PKG,"> $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb") || die "Unable to create $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb";
359        foreach my $p (keys %$pkg) {
360            print PKG "pbpkg $p = $pkg->{$p}\n";
361        }
362        close(PKG);
363
364        # Final cleanup
365        pb_rm_rf($dest) if (-d $dest);
366    }
367}
368
369sub pb_build2pkg {
370
371    # Get list of packages to build
372    my $ptr = pb_get_pkg($defpkgdir,$extpkgdir);
373    @pkgs = @$ptr;
374
375    # Get the running distro to build on
376    my ($ddir, $dver, $dfam, $dtype, $pbsuf) = pb_distro_init();
377    print $LOG "DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $pbsuf))."\n" if ($debug >= 1);
378
379    # Get content saved in cms2build
380    my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb","pbpkg");
381    $pkg = { } if (not defined $pkg);
382
383    # declare packager
384    my ($tmp) = pb_conf_get("packager");
385    my $pbpackager = $tmp->{$ENV{'PBPROJ'}};
386
387    chdir "$ENV{'PBBUILDDIR'}";
388    my $made = ""; # pkgs made during build
389    foreach my $pbpkg (@pkgs) {
390        my $vertag = $pkg->{$pbpkg};
391        # get the version of the current package - maybe different
392        ($pbver,$pbtag) = split(/-/,$vertag);
393
394        my $src="$ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz";
395        print $LOG "Source file: $src\n" if ($debug >= 0);
396
397        print $LOG "Working directory: $ENV{'PBBUILDDIR'}\n" if ($debug >= 0);
398        if ($dtype eq "rpm") {
399            foreach my $d ('RPMS','SRPMS','SPECS','SOURCES','BUILD') {
400                if (! -d "$ENV{'PBBUILDDIR'}/$d") {
401                pb_mkdir_p("$ENV{'PBBUILDDIR'}/$d") || die "Please ensure that you can write into $ENV{'PBBUILDDIR'} to create $d\nchown the $ENV{'PBBUILDDIR'} directory to your uid";
402                }
403            }
404
405            symlink "$src","$ENV{'PBBUILDDIR'}/SOURCES/".basename($src) || die "Unable to symlink $src in $ENV{'PBBUILDDIR'}/SOURCES";
406            # We need to first extract the spec file
407            my @specfile;
408            @specfile = pb_extract_build_files($src,"$pbpkg-$pbver/pbconf/$ddir-$dver/","$ENV{'PBBUILDDIR'}/SPECS");
409
410            print $LOG "specfile: ".Dumper(\@specfile)."\n" if ($debug >= 1);
411            # set LANGUAGE to check for correct log messages
412            $ENV{'LANGUAGE'}="C";
413            #system("ls -R $ENV{'PBBUILDDIR'}") if ($debug >= 1);
414            foreach my $f (@specfile) {
415                if ($f =~ /\.spec$/) {
416                    pb_system("rpmbuild --define \'packager $pbpackager\' --define \"_topdir $ENV{'PBBUILDDIR'}\" -ba $f","Building package with $f under $ENV{'PBBUILDDIR'}");
417                    last;
418                }
419            }
420            $made="$made RPMS/*/$pbpkg-$pbver-$pbtag$pbsuf.*.rpm SRPMS/$pbpkg-$pbver-$pbtag$pbsuf.src.rpm";
421            if (-f "/usr/bin/rpmlint") {
422                pb_system("rpmlint $made","Checking validity of rpms with rpmlint");
423            }
424        } elsif ($dtype eq "deb") {
425            chdir "$ENV{'PBBUILDDIR'}" || die "Unable to chdir to $ENV{'PBBUILDDIR'}";
426            pb_system("tar xfz $src","Extracting sources");
427
428            chdir "$pbpkg-$pbver" || die "Unable to chdir to $pbpkg-$pbver";
429            symlink "pbconf/$ddir-$dver","debian" || die "Unable to symlink to pbconf/$ddir-$dver";
430            chmod 0755,"debian/rules";
431            pb_system("dpkg-buildpackage -us -uc -rfakeroot","Building package");
432            $made="$made $pbpkg"."_*.deb $pbpkg"."_*.dsc $pbpkg"."_*.tar.gz";
433        } elsif ($dtype eq "ebuild") {
434            my @ebuildfile;
435            # For gentoo we need to take pb as subsystem name
436            # We put every apps here under sys-apps. hope it's correct
437            # We use pb's home dir in order o have a single OVERLAY line
438            my $tmpd = "$ENV{'HOME'}/portage/pb/sys-apps/$pbpkg";
439            pb_mkdir_p($tmpd) if (! -d "$tmpd");
440            pb_mkdir_p("$ENV{'HOME'}/portage/distfiles") if (! -d "$ENV{'HOME'}/portage/distfiles");
441
442            # We need to first extract the ebuild file
443            @ebuildfile = pb_extract_build_files($src,"$pbpkg-$pbver/pbconf/$ddir-$dver/","$tmpd");
444
445            # Prepare the build env for gentoo
446            my $found = 0;
447            my $pbbd = $ENV{'HOME'};
448            $pbbd =~ s|/|\\/|g;
449            if (-r "/etc/make.conf") {
450                open(MAKE,"/etc/make.conf");
451                while (<MAKE>) {
452                    $found = 1 if (/$pbbd\/portage/);
453                }
454                close(MAKE);
455            }
456            if ($found == 0) {
457                pb_system("sudo sh -c 'echo PORTDIR_OVERLAY=\"$ENV{'HOME'}/portage\" >> /etc/make.conf'");
458            }
459            #$found = 0;
460            #if (-r "/etc/portage/package.keywords") {
461            #open(KEYW,"/etc/portage/package.keywords");
462            #while (<KEYW>) {
463            #$found = 1 if (/portage\/pb/);
464            #}
465            #close(KEYW);
466            #}
467            #if ($found == 0) {
468            #pb_system("sudo sh -c \"echo portage/pb >> /etc/portage/package.keywords\"");
469            #}
470
471            # Build
472            foreach my $f (@ebuildfile) {
473                if ($f =~ /\.ebuild$/) {
474                    move($f,"$tmpd/$pbpkg-$pbver.ebuild");
475                    pb_system("cd $tmpd ; ebuild $pbpkg-$pbver.ebuild clean ; ebuild $pbpkg-$pbver.ebuild digest ; ebuild $pbpkg-$pbver.ebuild package");
476                    # Now move it where pb expects it
477                    pb_mkdir_p("$ENV{'PBBUILDDIR'}/portage/pb/sys-apps/$pbpkg");
478                    move("$tmpd/$pbpkg-$pbver.ebuild","$ENV{'PBBUILDDIR'}/portage/pb/sys-apps/$pbpkg");
479                }
480            }
481
482            $made="$made portage/pb/sys-apps/$pbpkg/$pbpkg-$pbver.ebuild";
483        } elsif ($dtype eq "slackware") {
484            $made="$made build-$pbpkg/$pbpkg-$pbver-*-$pbtag.tgz";
485            pb_mkdir_p("$ENV{'PBBUILDDIR'}/install") if (! -d "$ENV{'PBBUILDDIR'}/install");
486        } else {
487            die "Unknown dtype format $dtype";
488        }
489    }
490    # Keep track of what is generated so that we can get them back from VMs
491    open(KEEP,"> $ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag") || die "Unable to create $ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag";
492    print KEEP "$made\n";
493    close(KEEP);
494}
495
496sub pb_build2ssh {
497    pb_send2ssh("Sources");
498}
499
500sub pb_pkg2ssh {
501    pb_send2ssh("Packages");
502}
503
504# By default deliver to the the public site hosting the
505# ftp structure (or whatever) or a VM
506sub pb_send2ssh {
507
508    my $cmt = shift;
509    my $vm = shift || undef;
510    my $vmexist = shift || 0;           # 0 is FALSE
511    my $vmpid = shift || 0;             # 0 is FALSE
512    my $host = shift || "sshhost";
513    my $login = shift || "sshlogin";
514    my $dir = shift || "sshdir";
515    my $port = shift || "sshport";
516    my $tmout = shift || "vmtmout";
517    my $cmd = "";
518
519    # Get list of packages to build
520    my $ptr = pb_get_pkg($defpkgdir,$extpkgdir);
521    @pkgs = @$ptr;
522
523    # Get the running distro to consider
524    my ($odir,$over) = (undef, undef);
525    if (defined $vm) {
526        ($odir,$over) = split(/_/,$vm);
527    }
528    my ($ddir, $dver, $dfam, $dtype, $pbsuf) = pb_distro_init($odir,$over);
529    print $LOG "DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $pbsuf))."\n" if ($debug >= 1);
530
531    # Get content saved in cms2build
532    my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb","pbpkg");
533    $pkg = { } if (not defined $pkg);
534
535    my $src = "";
536    chdir "$ENV{'PBBUILDDIR'}";
537    foreach my $pbpkg (@pkgs) {
538        my $vertag = $pkg->{$pbpkg};
539        # get the version of the current package - maybe different
540        ($pbver,$pbtag) = split(/-/,$vertag);
541
542        if (($cmt eq "Sources") || ($cmt eq "VMs")) {
543            $src = "$src $ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz";
544            if ($cmd eq "") {
545                $cmd = "ln -sf $pbpkg-$pbver.tar.gz $pbpkg-latest.tar.gz";
546            } else {
547                $cmd = "$cmd ; ln -sf $pbpkg-$pbver.tar.gz $pbpkg-latest.tar.gz";
548            }
549        }
550    }
551    if ($cmt eq "VMs") {
552        $src="$src $ENV{'PBDESTDIR'}/pbscript $ENV{'PBCONF'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$pbprojver-$pbprojtag.pb $ENV{'PBETC'}";
553    } elsif ($cmt eq "Script") {
554        $src="$src $ENV{'PBDESTDIR'}/pbscript";
555    } elsif ($cmt eq "Packages") {
556        # Get package list from file made during build2pkg
557        open(KEEP,"$ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag") || die "Unable to read $ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag";
558        $src = <KEEP>;
559        chomp($src);
560        close(KEEP);
561        if ($dtype eq "rpm") {
562            # Also make a pbscript to generate yum/urpmi bases
563            # $src = "$src $ENV{'PBDESTDIR'}/pbscript"
564        } elsif ($dtype eq "deb") {
565            # Also make a pbscript to generate apt bases
566            # $src = "$src $ENV{'PBDESTDIR'}/pbscript"
567        }
568    }
569    # Remove potential leading spaces (cause pb with basename)
570    $src =~ s/^ *//;
571    my $basesrc = "";
572    foreach my $i (split(/ +/,$src)) {
573        $basesrc .= " ".basename($i);
574    }
575
576    print $LOG "Sources handled ($cmt): $src\n" if ($debug >= 0);
577    my ($sshhost,$sshlogin,$sshdir,$sshport,$vmtmout,$testver) = pb_conf_get($host,$login,$dir,$port,$tmout,"testver");
578    my $mac = "$sshlogin->{$ENV{'PBPROJ'}}\@$sshhost->{$ENV{'PBPROJ'}}";
579    # Overwrite account value if passed as parameter
580    $mac = "$pbaccount\@$sshhost->{$ENV{'PBPROJ'}}" if (defined $pbaccount);
581    my $tdir;
582    my $bdir;
583    if (($cmt eq "Sources") || ($cmt eq "Script")) {
584        $tdir = "$sshdir->{$ENV{'PBPROJ'}}/src";
585    } elsif ($cmt eq "VMs") {
586        $tdir = dirname("$sshdir->{$ENV{'PBPROJ'}}")."/delivery";
587        $bdir = dirname("$sshdir->{$ENV{'PBPROJ'}}")."/build";
588        # Remove a potential $ENV{'HOME'} as bdir should be relative to pb's home
589        $bdir =~ s|\$ENV.+\}/||;
590    } elsif ($cmt eq "Packages") {
591        $tdir = "$sshdir->{$ENV{'PBPROJ'}}/$ddir/$dver";
592        if (defined $testver->{$ENV{'PBPROJ'}}) {
593            # This is a test pkg => target dir is under test
594            $tdir .= "/test";
595        }
596    } else {
597        return;
598    }
599    my $nport = $sshport->{$ENV{'PBPROJ'}};
600    $nport = "$pbport" if (defined $pbport);
601
602    # Remove a potential $ENV{'HOME'} as tdir should be relative to pb's home
603    $tdir =~ s|\$ENV.+\}/||;
604
605    my $tm = $vmtmout->{$ENV{'PBPROJ'}};
606    pb_system("ssh -q -p $nport $mac \"mkdir -p $tdir ; cd $tdir ; echo \'for i in $basesrc; do if [ -f \$i ]; then rm -f \$i; fi; done\ ; $cmd' | bash\"","Preparing $tdir on $mac");
607    pb_system("cd $ENV{'PBBUILDDIR'} ; scp -p -P $nport $src $mac:$tdir 2> /dev/null","$cmt delivery in $tdir on $mac");
608    pb_system("ssh -q -p $nport $mac \"echo \'cd $tdir ; if [ -f pbscript ]; then ./pbscript; fi\' | bash\"","Executing pbscript on $mac  if needed");
609    if ($cmt eq "VMs") {
610        # Get back info on pkg produced, compute their name and get them from the VM
611        pb_system("scp -p -P $nport $mac:$bdir/pbgen-$pbprojver-$pbprojtag $ENV{'PBBUILDDIR'} 2> /dev/null","Get package names in $bdir on $mac");
612        open(KEEP,"$ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag") || die "Unable to read $ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag";
613        my $src = <KEEP>;
614        chomp($src);
615        close(KEEP);
616        $src =~ s/^ *//;
617        pb_mkdir_p("$ENV{'PBBUILDDIR'}/$odir/$over");
618        # Change pgben to make the next send2ssh happy
619        my $made = "";
620        open(KEEP,"> $ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag") || die "Unable to write $ENV{'PBBUILDDIR'}/pbgen-$pbprojver-$pbprojtag";
621        foreach my $p (split(/ +/,$src)) {
622            my $j = basename($p);
623            pb_system("scp -p -P $nport $mac:\'$bdir/$p\' $ENV{'PBBUILDDIR'}/$odir/$over 2> /dev/null","Package recovery of $j in $bdir from $mac");
624            $made="$made $odir/$over/$j" if (($dtype ne "rpm") || ($j !~ /.src.rpm$/));
625        }
626        print KEEP "$made\n";
627        close(KEEP);
628        pb_system("ssh -q -p $nport $mac \"rm -rf $tdir $bdir\"","VM cleanup on $mac");
629        pb_send2ssh("Packages","$odir"."_"."$over");
630        if (! $vmexist) {
631            pb_system("ssh -q -p $nport $mac \"sudo /sbin/halt -p \"; sleep $tm ; echo \'if [ -d /proc/$vmpid ]; then kill -9 $vmpid; fi \' | bash ; sleep 10","VM $vm halt (pid $vmpid)");
632        }
633        pb_rm_rf("$ENV{'PBBUILDDIR'}/$odir");
634    }
635}
636
637sub pb_script2vm {
638    my $pbscript=shift;
639
640    # Prepare the script to be executed on the VM
641    # in $ENV{'PBDESTDIR'}/pbscript
642    if ((defined $pbscript ) && ($pbscript ne "$ENV{'PBDESTDIR'}/pbscript")) {
643        copy($pbscript,"$ENV{'PBDESTDIR'}/pbscript") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript";
644        chmod 0755,"$ENV{'PBDESTDIR'}/pbscript";
645    }
646
647    my ($vm,$all) = pb_get_vm();
648
649    foreach my $v (@$vm) {
650        # Launch the VM
651        my ($vmexist,$vmpid) = pb_launchvm($v,0);
652
653        # Skip that VM if it something went wrong
654        next if (($vmpid == 0) && ($vmexist ==0));
655
656        # Gather all required files to send them to the VM
657        # and launch the build thourgh pbscript
658        pb_send2ssh("Script","$v",$vmexist,$vmpid,"vmhost","vmlogin","pbrc","vmport","vmtmout");
659
660    }
661}
662
663sub pb_launchvm {
664    my $vm = shift;
665    my $create = shift || 0;        # By default do not create a VM
666
667    die "-i iso parameter needed" if (((not defined $iso) || ($iso eq "")) && ($create != 0));
668    die "No VM defined, unable to launch" if (not defined $vm);
669    # Keep only the first VM in case many were given
670    $vm =~ s/,.*//;
671
672    # Launch the VMs
673    my ($ptr,$vmopt,$vmport,$vmpath,$vmtmout,$vmsize) = pb_conf_get("vmtype","vmopt","vmport","vmpath","vmtmout","vmsize");
674    my $vmtype = $ptr->{$ENV{'PBPROJ'}};
675    if (not defined $ENV{'PBVMOPT'}) {
676        $ENV{'PBVMOPT'} = "";
677    }
678    if (defined $vmopt->{$ENV{'PBPROJ'}}) {
679        $ENV{'PBVMOPT'} .= " $vmopt->{$ENV{'PBPROJ'}}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$ENV{'PBPROJ'}}/);
680    }
681    my $nport = $vmport->{$ENV{'PBPROJ'}};
682    $nport = "$pbport" if (defined $pbport);
683
684    my $cmd;
685    my $vmcmd;      # has to be used for pb_check_ps
686    my $vmm;        # has to be used for pb_check_ps
687    if ($vmtype eq "qemu") {
688        my $arch = `uname -m`;
689        chomp($arch);
690        my $qemucmd32;
691        my $qemucmd64;
692        if ($arch eq "x86_64") {
693            $qemucmd32 = "/usr/bin/qemu-system-i386";
694            $qemucmd64 = "/usr/bin/qemu";
695        } else {
696            $qemucmd32 = "/usr/bin/qemu";
697            $qemucmd64 = "/usr/bin/qemu-system-x86_64";
698        }
699        if ($vm =~ /_64/) {
700            $vmcmd = "$qemucmd64 -no-kqemu";
701        } else {
702            $vmcmd = "$qemucmd32";
703        }
704        $vmm = "$vmpath->{$ENV{'PBPROJ'}}/$vm.qemu";
705        if ($create != 0) {
706            $ENV{'PBVMOPT'} .= " -cdrom $iso -boot d";
707        }
708        $cmd = "$vmcmd $ENV{'PBVMOPT'} -redir tcp:$nport:10.0.2.15:22 $vmm"
709    } elsif ($vmtype eq "xen") {
710    } elsif ($vmtype eq "vmware") {
711    } else {
712        die "VM of type $vmtype not supported. Report to the dev team";
713    }
714    my ($tmpcmd,$void) = split(/ +/,$cmd);
715    my $vmexist = pb_check_ps($tmpcmd,$vmm);
716    my $vmpid = 0;
717    if (! $vmexist) {
718        if ($create != 0) {
719            pb_system("/usr/bin/qemu-img create -f qcow2 $vmm $vmsize->{$ENV{'PBPROJ'}}","Creating the QEMU VM");
720        }
721        if (! -f "$vmm") {
722            print "Unable to find VM $vmm\n";
723        } else {
724            pb_system("$cmd &","Launching the VM $vmm");
725            pb_system("sleep $vmtmout->{$ENV{'PBPROJ'}}","Waiting for VM $vm to come up");
726            $vmpid = pb_check_ps($tmpcmd,$vmm);
727        }
728    } else {
729        print "Found an existing VM $vmm (pid $vmexist)\n";
730    }
731    return($vmexist,$vmpid);
732}
733
734sub pb_build2vm {
735    # Prepare the script to be executed on the VM
736    # in $ENV{'PBDESTDIR'}/pbscript
737    my ($ntp) = pb_conf_get("vmntp");
738    my $vmntp = $ntp->{$ENV{'PBPROJ'}};
739    open(SCRIPT,"> $ENV{'PBDESTDIR'}/pbscript") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript";
740    print SCRIPT "#!/bin/bash\n";
741    print SCRIPT "echo ... Execution needed\n";
742    print SCRIPT "# This is in directory delivery\n";
743    print SCRIPT "# Setup the variables required for building\n";
744    print SCRIPT "export PBPROJ=$ENV{'PBPROJ'}\n";
745    print SCRIPT "# Preparation for pb\n";
746    print SCRIPT "mkdir -p ../pbconf\n";
747    print SCRIPT "mv $ENV{'PBPROJ'}.pb ../pbconf\n";
748    print SCRIPT "mv .pbrc \$HOME\n";
749    print SCRIPT "cd ..\n";
750    # Force new date to be in the future compared to the date of the tar file by adding 1 minute
751    my @date=(localtime->sec(), localtime->min(), localtime->hour(), localtime->mday(), localtime->mon(), localtime->year(), localtime->wday(), localtime->yday(), localtime->isdst());
752    $date[1]++;
753    my $upddate = strftime("%m%d%H%M%Y", @date);
754    print SCRIPT "echo Setting up date on $vmntp...\n";
755    # Or use ntpdate if available
756    print SCRIPT "sudo date $upddate\n";
757    print SCRIPT "export PBROOT=\`pwd\`\n";
758    print SCRIPT "# Build\n";
759    my $p = "";
760    $p = join(' ',@ARGV) if (defined $ARGV[0]);
761    print SCRIPT "echo Building packages on VM...\n";
762    print SCRIPT "pb -p $ENV{'PBPROJ'} build2pkg $p\n";
763    close(SCRIPT);
764    chmod 0755,"$ENV{'PBDESTDIR'}/pbscript";
765
766    my ($vm,$all) = pb_get_vm();
767
768    # Send tar files when we do a global generation
769    pb_build2ssh() if ($all == 1);
770
771    foreach my $v (@$vm) {
772        # Launch the VM
773        my ($vmexist,$vmpid) = pb_launchvm($v,0);
774
775        # Skip that VM if it something went wrong
776        next if (($vmpid == 0) && ($vmexist == 0));
777
778        # Gather all required files to send them to the VM
779        # and launch the build thourgh pbscript
780        pb_send2ssh("VMs","$v",$vmexist,$vmpid,"vmhost","vmlogin","pbrc","vmport","vmtmout");
781    }
782}
783
784
785sub pb_newver {
786
787    die "-V Version parameter needed" if ((not defined $newver) || ($newver eq ""));
788    my $cms=pb_cms_init($ENV{'PBPROJ'});
789    if ($cms->{$ENV{'PBPROJ'}} ne "svn") {
790        die "Only SVN is supported at the moment";
791    }
792    my $res = pb_cms_isdiff($cms);
793    die "You need to have no differences before creating a new version" if ($res != 0);
794    my $cmsurl = pb_cms_getinfo($cms);
795    my $newurl = dirname($cmsurl)."/$newver";
796    pb_cms_copy($cms,$cmsurl,$newurl);
797    pb_cms_checkout($cms,$newurl,"$ENV{'PBROOT'}/../$newver");
798    my $oldver=basename($cmsurl);
799    open(FILE,"$ENV{'PBROOT'}/../$newver/pbconf/$ENV{'PBPROJ'}.pb") || die "Unable to open $ENV{'PBROOT'}/../$newver/pbconf/$ENV{'PBPROJ'}.pb";
800    open(OUT,"> $ENV{'PBROOT'}/../$newver/pbconf/$ENV{'PBPROJ'}.pb.new") || die "Unable to write to $ENV{'PBROOT'}/../$newver/pbconf/$ENV{'PBPROJ'}.pb.new";
801    while(<FILE>) {
802        s/projver\s+$ENV{'PBPROJ'}\s*=\s*$oldver/projver $ENV{'PBPROJ'} = $newver/;
803        print OUT $_;
804    }
805    close(FILE);
806    close(OUT);
807    rename("$ENV{'PBROOT'}/../$newver/pbconf/$ENV{'PBPROJ'}.pb.new","$ENV{'PBROOT'}/../$newver/pbconf/$ENV{'PBPROJ'}.pb");
808    pb_cms_checkin($cms,"$ENV{'PBROOT'}/../$newver");
809}
810
811#
812# Return the list of packages we are working on
813#
814sub pb_get_pkg {
815
816my @pkgs;
817my $defpkgdir = shift;
818my $extpkgdir = shift;
819
820# Get packages list
821if (not defined $ARGV[0]) {
822    @pkgs = keys %$defpkgdir;
823} elsif ($ARGV[0] =~ /^all$/) {
824    @pkgs = keys %$defpkgdir;
825    push(@pkgs, keys %$extpkgdir);
826} else {
827    @pkgs = @ARGV;
828}
829print $LOG "Packages: ".join(',',@pkgs)."\n" if ($debug >= 0);
830return(\@pkgs);
831}
832
833#
834# Return the list of VMs we are working on
835# $all is a flag to know if we return all of them
836# or only some (if all we publish also tar files in addition to pkgs
837#
838sub pb_get_vm {
839
840my @vm;
841my $all = 0;
842
843# Get VM list
844if ((not defined $ENV{'PBVM'}) || ($ENV{'PBVM'} =~ /^all$/)) {
845    my ($ptr) = pb_conf_get("vmlist");
846    my $ptr2 = $ptr->{$ENV{'PBPROJ'}};
847    $ptr2 =~ s/,/_64,/g;
848    $ptr2 =~ s/$/_64/g;
849    $ENV{'PBVM'} = "$ptr->{$ENV{'PBPROJ'}},$ptr2";
850    $all = 1;
851}
852print $LOG "VMs: $ENV{'PBVM'}\n";
853@vm = split(/,/,$ENV{'PBVM'});
854return(\@vm,$all);
855}
856
857# Returns the pid of a running VM command using a specific VM file
858sub pb_check_ps {
859    my $vmcmd = shift;
860    my $vmm = shift;
861    my $vmexist = 0;        # FALSE by default
862
863    open(PS, "ps auxhww|") || die "Unable to call ps";
864    while (<PS>) {
865        next if (! /$vmcmd/);
866        next if (! /$vmm/);
867        my ($void1, $void2);
868        ($void1, $vmexist, $void2) = split(/ +/);
869        last;
870    }
871    return($vmexist);
872}
873
874
875sub pb_extract_build_files {
876
877my $src=shift;
878my $dir=shift;
879my $ddir=shift;
880my @files;
881
882if ($src =~ /tar\.gz$/) {
883    pb_system("tar xfpz $src $dir","Extracting build files");
884} elsif ($src =~ /tar\.bz2$/) {
885    pb_system("tar xfpj $src $dir","Extracting build files");
886} else {
887    die "Unknown compression algorithm for $src";
888}
889opendir(DIR,"$dir") || die "Unable to open directory $dir";
890foreach my $f (readdir(DIR)) {
891    next if ($f =~ /^\./);
892    move("$dir/$f","$ddir") || die "Unable to move $dir/$f to $ddir";
893    print $LOG "mv $dir/$f $ddir\n" if ($debug >= 1);
894    push @files,"$ddir/$f";
895}
896closedir(DIR);
897# Not enough but still a first cleanup
898pb_rm_rf("$dir");
899return(@files);
900}
901
902sub pb_syntax {
903
904    print "pb (aka project-builder) Version $projectbuilderver-$projectbuilderrev\n";
905    print "\n";
906    print "Syntax: pb [-vhqt][-r pbroot][-p project][[-s script -a account -P port] -m \"mach-1[,...]\"][-i iso] <action> [<pkg1>...]\n";
907    print "\n";
908    print "-h : This help file\n";
909    print "-q : Quiet mode\n";
910    print "-t : Test mode (not done yet)\n";
911    print "-v : Verbose mode\n";
912    print "\n";
913    print "-m machine : Name of the Virtual Machines (VM) you want\n";
914    print "             to build on (coma separated). All if none precised\n";
915    print "             (or use the env variable PBVM)       \n";
916    print "\n";
917    print "-s script  : Name of the script you want\n";
918    print "             to execute on the related VMs.\n";
919    print "\n";
920    print "-i iso     : Name of the ISO image of the distribution you want\n";
921    print "             to install on the related VMs.\n";
922    print "\n";
923    print "-a account : Name of the account to use\n";
924    print "             to connect on the related VMs.\n";
925    print "\n";
926    print "-P port    : Number of the port to use\n";
927    print "             to connect on the related VMs.\n";
928    print "\n";
929    print "-p project : Name of the project you're working on\n";
930    print "             (or use the env variable PBPROJ)     \n";
931    print "\n";
932    print "-r pbroot  : Path Name of project under the CMS \n";
933    print "             (or use the env variable PBROOT)   \n";
934    print "\n";
935    print "-V newver  : New version of the project to create\n";
936    print "             from the current one.              \n";
937    print "\n";
938    print "<action> can be:\n";
939    print "\n";
940    print "\tcms2build: Create tar files for the project under your CMS\n";
941    print "\t           CMS supported are SVN and CVS\n";
942    print "\t           parameters are packages to build\n";
943    print "\t           if not using default list\n";
944    print "\n";
945    print "\tbuild2pkg: Create packages for your running distribution  \n";
946    print "\n";
947    print "\tcms2pkg:   cms2build + build2pkg\n";
948    print "\n";
949    print "\tbuild2ssh: Send the tar files to a SSH host               \n";
950    print "\n";
951    print "\tcms2ssh:   cms2build + build2ssh\n";
952    print "\n";
953    print "\tpkg2ssh:   Send the packages built to a SSH host          \n";
954    print "\n";
955    print "\tbuild2vm:  Create packages in VMs, launching them if needed\n";
956    print "\t           and send those packages to a SSH host once built\n";
957    print "\t           VM type supported are QEMU            \n";
958    print "\n";
959    print "\tcms2vm:    cms2build + build2vm\n";
960    print "\n";
961    print "\tlaunchvm:  Launch one virtual machine\n";
962    print "\n";
963    print "\tscript2vm: Launch one virtual machine if needed        \n";
964    print "\t           and executes a script on it                 \n";
965    print "\n";
966    print "\tnewvm:     Create a new virtual machine\n";
967    print "\n";
968    print "\tnewver:    Create a new version of the project derived \n";
969    print "\t           from the current one                        \n";
970    print "\n";
971    print "\tnewproj:   Create a new project and a template set of  \n";
972    print "\t           configuration files under pbconf            \n";
973    print "\n";
974}
Note: See TracBrowser for help on using the repository browser.