build/scripts/automation/winbld.pl (186 lines of code) (raw):
use warnings;
use winbldroutines;
use strict;
use Time::HiRes;
use Time::Duration;
use sigtrap qw{handler sigHandler normal-signals error-signals};
# -----------------------------------------------------------------------
# Useful functions - prototypes
# -----------------------------------------------------------------------
sub usage;
sub ParseCmdLine;
sub GetPlan;
sub ValidateOptions;
sub main;
# -----------------------------------------------------------------------
# Global variable declarations
# -----------------------------------------------------------------------
my @OPTIONSET;
my $proper_usage_flag = 0;
my %permissible_arguments = (
'products' => 1,
'config' => 1,
'branch' => 1,
'buildphase' => 1,
'majorversion' => 1,
'minorversion' => 1,
'patchsetversion' => 1,
'patchversion' => 1,
'mailto' => 1,
'buildquality' => 1,
'cvsupdate' => 1,
'partner' => 1,
'incremental' => 1,
'srcpath' => 1,
'buildcopypath' => 1,
'loadsymbols' => 1,
'symbolserverpath' => 1,
'signing' => 1,
'file' => 1
);
my %permissible_products = ('cx' => 1, 'ucx' => 1, 'cx_tp' => 1, 'vx' => 1, 'fx' => 1, 'ua' => 1, 'asrua' => 1, 'asrsetup' => 1, 'axiom' => 1, 'ua_win2k' => 1, 'pi' => 1, 'br1' => 1, 'bx' => 1, 'scoutvcon' => 1, 'scoutcloudvcon' => 1, 'psmsi' => 1);
my %permissible_config = ('debug' => 1, 'release' => 1);
my %permissible_branch = (
'develop' => 1,
'release' => 1,
'master' => 1
);
# -----------------------------------------------------------------------
# Subroutines used in the program
# -----------------------------------------------------------------------
sub sigHandler
{
my $sig = shift;
print "Caught a SIG$sig !\n";
die;
}
sub ParseConfigurationFile
{
# This function is only for parsing a SINGLE configuration file.
# It fills @OPTIONSET elements with hashrefs to option sets.
# A single configuration file can accommodate multiple option sets.
# An option set begins with a BEGIN and ends with an END.
print "DEBUG: in ParseConfigurationFile()\n";
my ($start_parsing,$counter,$lines_processed)=(3,0,0);
my (%begin_line_number,%end_line_number);
my $ConfigFileName = shift;
&usage("Configuration file name suffix must be 'bcf' and prefix must contain only alphanumeric characters and underscores!\n") unless $ConfigFileName =~ /^[\w_]+\.bcf$/i;
open(CNF_FILE,"<$ConfigFileName") or die "Cannot open configuration file $ConfigFileName for reading!\n";
while(<CNF_FILE>)
{
if (/^\s*BEGIN\s*$/i)
{ $start_parsing = 1; $counter++; $begin_line_number{$counter} = $.; }
if (/^\s*END\s*$/i)
{ $start_parsing = 0; $counter++; $end_line_number{$counter} = $. ; }
# counter is supposed to have odd values for BEGIN and even values for END.
# So if a BEGIN has even counter, it means there was no END for the previous BEGIN, or it's a nested BEGIN-END.
# Similarly, if an END has odd counter, it means there was no corresponding BEGIN or it's a nested BEGIN-END.
# Do not allow this.
if ($start_parsing == 0)
{
die "Configuration file has a missing BEGIN for the END at line $end_line_number{$counter} (or has a nested BEGIN-END which is not allowed)!\n" if $counter%2==1;
die "Configuration file has an empty BEGIN-END block ending at line $end_line_number{$counter} (not allowed)!\n" if $end_line_number{$counter} == $begin_line_number{$counter-1} + 1;
}
if ($start_parsing == 1)
{
die "Configuration file has a missing END for the BEGIN at line $begin_line_number{$counter - 1} (or has a nested BEGIN-END which is not allowed)!\n" if $counter%2 == 0;
# Current line is either a BEGIN or something after that and before an END.
# Ignore lines that are beginning with # (i.e. a comment) or that are empty!
$lines_processed++ and next if /^\s*#\s*/ or /^\s*$/;
# Consider lines that are of the form <spaces>something<spaces>=<spaces>something_else<spaces>
if (/^\s*(\w+)\s*=\s*(.+?)\s*$/)
{
&usage("Inappropriate argument $1 entered!\n") unless exists $permissible_arguments{lc($1)};
$lines_processed++;
# Ignore lines that mention another configuration file in a "file" argument!
next if lc($1) eq "file";
# Fill the OPTIONSET hashref here
# The block iteration value is ($counter+1)/2. Subtract 1 from this since arrays index from 0.
$OPTIONSET[($counter+1)/2 - 1]->{lc($1)} = $2;
}
}
}
# Final check is whether file has an END at its end. In that case, counter is even.
die "The last BEGIN in the configuration file has an END missing!\n" if $counter%2==1;
die "The configuration file has no BEGIN or END!\n" if $start_parsing == 3;
return;
}
sub ParseCommandLineArgs
{
# This function is only for parsing command-line arguments.
# It fills a hashref residing in $OPTIONSET[0].
# Command-line mode can accommodate only 1 option set.
print "DEBUG: in ParseCommandLineArgs()\n";
foreach (@ARGV)
{
/(.+)=(.+)/;
$OPTIONSET[0]->{lc($1)} = $2;
}
}
sub ParseCmdLine
{
# This is the main parsing function.
print "DEBUG: in ParseCmdLine()\n";
usage("Please enter some arguments!\n") unless defined $ARGV[0];
# Do an initial loop over the arguments. If "file" is found, call ParseConfigurationFile.
# Else, call ParseCommandLineArgs.
foreach (@ARGV)
{
/(.+)=(.+)/;
usage("Inappropriate argument $1 entered!\n") unless exists $permissible_arguments{lc($1)};
if (lc($1) eq "file")
{
ParseConfigurationFile($2);
return;
}
}
# If control remain till here, it means that all arguments are permissible and none of them is "file".
ParseCommandLineArgs();
return;
}
sub ValidateOptions
{
# This is a generic function which loops over the option sets and validates each 1 of the arguments.
print "DEBUG: in ValidateOptions()\n";
foreach my $i (0..$#OPTIONSET) # Beginning of 'for' loop over OPTIONSET elements
{
# Product
print "DEBUG: parsing option set $i\n";
if (defined $OPTIONSET[$i]->{products})
{
my @products_to_build;
while( $OPTIONSET[$i]->{products} =~ /(\w+)(,?)/g )
{
push @products_to_build, lc($1) if defined $permissible_products{lc($1)};
}
$OPTIONSET[$i]->{products} = \@products_to_build;
}
else
{
$OPTIONSET[$i]->{products} = [qw(cx ucx cx_tp fx vx ua asrua asrsetup axiom ua_win2k pi br1 bx scoutvcon scoutcloudvcon)] ;
}
&usage("In option set $i: component can be one or more (comma-separated) of cx, ucx, cx_tp, vx, fx, pi, ua, asrua, asrsetup, ua_win2k, br1, bx, scoutvcon, scoutcloudvcon or axiom (default is cx,asrsetup,ucx,cx_tp,fx,vx,ua,asrua,ua_win2k,pi,br1,bx,scoutvcon,scoutcloudvcon,axiom). If you enter anything else, it will be silently ignored!\n") unless exists $OPTIONSET[$i]->{products}->[0];
# Configuration
if (defined $OPTIONSET[$i]->{config})
{
my @configs_to_build;
while( $OPTIONSET[$i]->{config} =~ /(\w+)(,?)/g )
{
push @configs_to_build, lc($1) if defined $permissible_config{lc($1)};
}
$OPTIONSET[$i]->{config} = \@configs_to_build;
}
else
{
$OPTIONSET[$i]->{config} = [qw(release)] ;
}
&usage("In option set $i: config can be one or more (comma-separated) of debug and release (default is release). If you enter anything else, it will be silently ignored!\n") unless exists $OPTIONSET[$i]->{config}->[0];
# Other options
$OPTIONSET[$i]->{branch} = "TRUNK" unless defined $OPTIONSET[$i]->{branch};
&usage("In option set $i: branch not supported!\n") unless exists $permissible_branch{$OPTIONSET[$i]->{branch}};
$OPTIONSET[$i]->{buildphase} = "DIT" unless defined $OPTIONSET[$i]->{buildphase};
$OPTIONSET[$i]->{mailto} = 'inmiet@microsoft.com' unless defined $OPTIONSET[$i]->{mailto};
$OPTIONSET[$i]->{buildquality} = "DAILY" unless defined $OPTIONSET[$i]->{buildquality};
$OPTIONSET[$i]->{cvsupdate} = "yes" unless defined $OPTIONSET[$i]->{cvsupdate};
&usage("In option set $i: cvs update mode can be either yes or no (default is yes)!\n") unless $OPTIONSET[$i]->{cvsupdate} =~ /^(yes|no)$/i;
$OPTIONSET[$i]->{partner} = "inmage" unless defined $OPTIONSET[$i]->{partner};
&usage("In option set $i: partner can be either inmage or xiotech or quantum (default is inmage)!\n") unless $OPTIONSET[$i]->{partner} =~ /^(inmage|xiotech|INTEL|quantum)$/i;
$OPTIONSET[$i]->{incremental} = "no" unless defined $OPTIONSET[$i]->{incremental};
&usage("In option set $i: incremental can be either yes or no (default is no)!\n") unless $OPTIONSET[$i]->{incremental} =~ /^(yes|no)$/i;
$OPTIONSET[$i]->{loadsymbols} = "no" unless defined $OPTIONSET[$i]->{loadsymbols};
&usage("In option set $i: loadsymbols can be either yes or no (default is no)!\n") unless $OPTIONSET[$i]->{loadsymbols} =~ /^(yes|no)$/i;
&usage("In option set $i: symbolserverpath has to be defined since loadsymbols is yes!\n") if defined $OPTIONSET[$i]->{loadsymbols} and not defined $OPTIONSET[$i]->{symbolserverpath};
$OPTIONSET[$i]->{signing} = "no" unless defined $OPTIONSET[$i]->{signing};
&usage("In option set $i: signing can be either complete or mandatory (default is complete)!\n") unless $OPTIONSET[$i]->{signing} =~ /^(complete|mandatory)$/i;
# B. Mandatory parameters
&usage("In option set $i: Please enter the top source directory!\n") unless defined $OPTIONSET[$i]->{srcpath};
&usage("In option set $i: Please enter the target directory where built binaries shall be copied!\n") unless defined $OPTIONSET[$i]->{buildcopypath};
# If control reaches till here, it means all arguments are entered properly.
$proper_usage_flag = 1;
if ($proper_usage_flag == 1)
{
print "\nPrinting set $i now...\n\n";
print "$_ = $OPTIONSET[$i]->{$_}\n" foreach (keys %{$OPTIONSET[$i]});
}
} # End of 'for' loop over OPTIONSET elements
}
sub Build
{
my @PerOptBldObjects;
my $NumOfPerOptBldObjects = $#OPTIONSET + 1;
foreach my $i (0..$#OPTIONSET)
{
$PerOptBldObjects[$i] = winbldroutines->new($OPTIONSET[$i]);
$PerOptBldObjects[$i]->Start();
}
return;
}
sub usage
{
my $message = shift;
print "\n\n$message";
print <<TOC;
$0: Usage details
====================================
$0 [products=cx|ucx|cx_tp|vx|fx|ua|ua_win2k|pi|scoutvcon|scoutcloudvcon|axiom|bx|br1|all] [cvsupdate=yes|no] [partner=inmage|INTEL|xiotech] [branch=<CVSBranch>] [config=debug|release|both] [srcpath=<SrcCodePath>] [file=<ConfigurationFile>] [buildcopypath=<DirForCopyingBinaries>] [incremental=yes|no] [buildphase=<3-letter build phase>] [mailto=<CommaSepEmailIDs>] [buildquality=<QualityOfBuild-like BETA/ALPHA/GA> [singing=complete|mandatory]]
Notes:
======
1. You can specify more than 1 product value (comma-separated). Permitted products are cx, ucx, cx_tp, vx, fx, ua, scoutvcon,scoutcloudvcon, axiom, ua_win2k, bx br1 or all. Anything else will be silently ignored.
2. If a configuration file is specified as value for the 'file' parameter, other command-line arguments (including other 'file' arguments) are silently ignored.
3. The configuration file can have multiple option sets defined within BEGIN-END blocks. Option sets are made of the same parameters as on a command-line. If a 'file' parameter is entered in these option sets, it will be silently ignored as recursion is not permitted in such cases. Option sets are counted beginning with 0 (so a command-line option set is also the 0th set!)
4. Nested and empty BEGIN-END blocks are not permitted in the configuration file. Comments (beginning with a #) and blank lines are silently ignored.
TOC
exit 1;
}
sub main
{
print "DEBUG: in main()\n";
ParseCmdLine();
ValidateOptions();
my $program_start_time = time();
Build();
print "Total runtime for builds = ", duration(time() - $program_start_time), ".\n";
}
# -----------------------------------------------------------------------
# Entry point in the program
# -----------------------------------------------------------------------
&main();