buildframework/helium/tools/common/packages/BuildJob.pm
changeset 1 be27ed110b50
equal deleted inserted replaced
0:044383f39525 1:be27ed110b50
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of the License "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description: 
       
    15 #
       
    16 #!perl -w
       
    17 # ==============================================================================
       
    18 #  %name:          BuildJob.pm %
       
    19 #  Part of:        Juno Build Tools
       
    20 #  Requires:       Symbian OS build tools
       
    21 #
       
    22 #  %derived_by:    hasegawa %
       
    23 #  %version:	   to1r1103#4.1.2 %
       
    24 #  %date_modified: Thu Oct  5 15:41:26 2006 %
       
    25 #
       
    26 #  See POD text at the end of this file for usage and other details.
       
    27 # ==============================================================================
       
    28 
       
    29 package BuildJob;
       
    30 use strict;
       
    31 use warnings;
       
    32 use Win32::Job;
       
    33 use Config;
       
    34 use Exporter;
       
    35 
       
    36 our @ISA       = qw(Exporter);
       
    37 our @EXPORT_OK = qw(EBS_PORT EBS_DIR EBS_CLIENT EBS_SERVER);
       
    38 
       
    39 our $EPOCROOT = $ENV{EPOCROOT} || "\\";
       
    40 our $BUILD_DRIVE = $ENV{BUILD_DRIVE};
       
    41 
       
    42 my @EBS_PATHS = ("epoc32\\tools\\build", "src\\common\\generic\\tools\\build");
       
    43 
       
    44 $BuildJob::EBS_PORT   = 1973;
       
    45 $BuildJob::EBS_DIR    = "";
       
    46 $BuildJob::EBS_CLIENT = "buildclient.pl";
       
    47 $BuildJob::EBS_SERVER = "buildserver.pl";
       
    48 
       
    49 sub find_ebs
       
    50 {
       
    51     return 1 if -d "$BuildJob::EBS_DIR\\$BuildJob::EBS_CLIENT";
       
    52     foreach (map("${BuildJob::BUILD_DRIVE}${BuildJob::EPOCROOT}$_", @EBS_PATHS))
       
    53     {
       
    54         $BuildJob::EBS_DIR = $_ and return 1 if -f "$_\\$BuildJob::EBS_CLIENT";
       
    55     }
       
    56     return 0;
       
    57 }
       
    58 
       
    59 sub run
       
    60 {
       
    61     my $data_source = shift;
       
    62     return 0 unless ($data_source and -e $data_source);
       
    63 
       
    64     # finding free port.
       
    65     my $offset = 0;
       
    66     my $trials = 10;
       
    67     $BuildJob::EBS_PORT = $BuildJob::EBS_PORT + int(rand(1000));
       
    68     for(;$offset < $trials; $offset++) {
       
    69       if (!is_buildjob_running($BuildJob::EBS_PORT + $offset)) {
       
    70         $BuildJob::EBS_PORT = $BuildJob::EBS_PORT + $offset;
       
    71         last;
       
    72       }
       
    73     }
       
    74     warn("Can't start build job, is one already running?\n") and return 0 if ($offset > $trials); 
       
    75     print "Using port: $BuildJob::EBS_PORT\n";
       
    76     
       
    77     my $log_file = shift;
       
    78     $log_file = "build.log" unless $log_file;
       
    79     my $num_clients = shift;
       
    80 
       
    81     # default number of clients is the number of processors
       
    82     $num_clients = $ENV{NUMBER_OF_PROCESSORS} unless ($num_clients);
       
    83 
       
    84     # if we can't find the number of processors, just run one client
       
    85     $num_clients = 1 unless ($num_clients);
       
    86 
       
    87     find_ebs()
       
    88       or die("Can't find EBS scripts; looked in: "
       
    89              . join(",", map("${BuildJob::EPOCROOT}$_", @EBS_PATHS)));
       
    90 
       
    91     my $job = Win32::Job->new();
       
    92     for (my $i = 0; $i < $num_clients; $i++)
       
    93     {
       
    94         $job->spawn(
       
    95             $Config{perlpath},
       
    96             "perl $BuildJob::EBS_DIR\\$BuildJob::EBS_CLIENT -d localhost:$BuildJob::EBS_PORT -w 2 -c EBS_CLIENT$i",
       
    97             {new_console => 1, window_attr => 'minimized'});
       
    98     }
       
    99     my $server_pid = $job->spawn($Config{perlpath},
       
   100                                  "perl $BuildJob::EBS_DIR\\$BuildJob::EBS_SERVER -d $data_source -p $BuildJob::EBS_PORT -l $log_file"
       
   101                                 );
       
   102 
       
   103     my $job_failure = 0;
       
   104     {
       
   105         # kill the job if we receive a sigint
       
   106         local $SIG{INT} = sub {$job->kill();};
       
   107 
       
   108         # start job with no timeout (a null watchdog run every 60s)
       
   109         # and complete when the first process terminates
       
   110         $job_failure = !$job->watch(sub {return 0}, 60, 0);
       
   111     }
       
   112 
       
   113     my $job_status = $job->status();
       
   114     $job_failure ||= ($job_status->{$server_pid}{exitcode} != 0);
       
   115 
       
   116     if ($job_failure)
       
   117     {
       
   118         print(STDERR "Abnormal job termination:\nProcess\tExit Code\n");
       
   119         foreach my $pid (keys %$job_status)
       
   120         {
       
   121             print(  STDERR (($pid == $server_pid) ? "server" : "client") . "\t"
       
   122                   . $job_status->{$pid}{exitcode}
       
   123                   . "\n");
       
   124         }
       
   125     }
       
   126     return !$job_failure;
       
   127 }
       
   128 
       
   129 sub is_buildjob_running
       
   130 {
       
   131     my $port = shift;
       
   132     use IO::Socket::INET;
       
   133 
       
   134     # try to open a socket on the build server port
       
   135     my $sock =
       
   136       IO::Socket::INET->new(LocalAddr => '127.0.0.1',
       
   137                             LocalPort => $port,
       
   138                             Proto     => 'tcp',
       
   139                             Timeout   => 2);
       
   140 
       
   141     # failure means the server is probably running already
       
   142     my $is_port_available = !defined $sock;
       
   143     $sock->close() if $sock;
       
   144     return $is_port_available;
       
   145 }
       
   146 
       
   147 sub new
       
   148 {
       
   149     my $invocant = shift;
       
   150     my $name     = shift;
       
   151     my $class    = ref($invocant) || $invocant;
       
   152     my $self     = {name => $name, stages => [], env => []};
       
   153     bless($self, $class);
       
   154     $self->new_stage();
       
   155     return $self;
       
   156 }
       
   157 
       
   158 sub set
       
   159 {
       
   160     my $self  = shift;
       
   161     my $var   = shift;
       
   162     my $value = shift;
       
   163 
       
   164     push(@{$self->{env}},
       
   165          {name  => $var,
       
   166           value => $value});
       
   167 }
       
   168 
       
   169 sub add
       
   170 {
       
   171     my $self = shift;
       
   172     push(@{$self->{stages}->[0]}, shift);
       
   173 }
       
   174 
       
   175 sub new_stage
       
   176 {
       
   177     my $self = shift;
       
   178     unshift(@{$self->{stages}}, []);
       
   179 }
       
   180 
       
   181 sub go
       
   182 {
       
   183     my $self       = shift;
       
   184     my $logfile    = shift;
       
   185     my $scriptfile = $self->{name} . ".xml";
       
   186 
       
   187     open(EBSSCRIPT, ">$scriptfile") or die("Can't open $scriptfile: $!");
       
   188     print EBSSCRIPT <<EOT;
       
   189 <?xml version="1.0"?>
       
   190 <?xml-stylesheet type="text/xsl" href="build.xsl"?>
       
   191 <!-- <!DOCTYPE BUILD SYSTEM "ebs.dtd"> -->
       
   192 <!DOCTYPE Build  [
       
   193   <!ELEMENT Product (Commands)>
       
   194   <!ATTLIST Product name CDATA #REQUIRED>
       
   195   <!ELEMENT Commands (Execute+ | SetEnv*)>
       
   196   <!ELEMENT Execute EMPTY>
       
   197   <!ATTLIST Execute ID CDATA #REQUIRED>
       
   198   <!ATTLIST Execute Stage CDATA #REQUIRED>
       
   199   <!ATTLIST Execute Component CDATA #REQUIRED>
       
   200   <!ATTLIST Execute Cwd CDATA #REQUIRED>
       
   201   <!ATTLIST Execute CommandLine CDATA #REQUIRED>
       
   202   <!ELEMENT SetEnv EMPTY>
       
   203   <!ATTLIST SetEnv Order ID #REQUIRED>
       
   204   <!ATTLIST SetEnv Name CDATA #REQUIRED>
       
   205   <!ATTLIST SetEnv Value CDATA #REQUIRED>
       
   206 ]>
       
   207 <Product name="zip">
       
   208   <Commands>
       
   209     <SetEnv Order="1" Name="PATH" Value="\\epoc32\\gcc\\bin;\\epoc32\\tools;%PATH%"/>
       
   210 EOT
       
   211 
       
   212     my $order = 2;
       
   213     foreach my $setenv (@{$self->{env}})
       
   214     {
       
   215         print EBSSCRIPT
       
   216           "<SetEnv Order=\"$order\" Name=\"$setenv->{name}\" Value=\"$setenv->{value}\"/>\n";
       
   217         $order++;
       
   218     }
       
   219 
       
   220     my $id       = 1;
       
   221     my $stage_id = 1;
       
   222     foreach my $stage (reverse @{$self->{stages}})
       
   223     {
       
   224         foreach my $cmd (@{$stage})
       
   225         {
       
   226             my $cmdline;
       
   227             $cmdline = $cmd->{commandline} if exists $cmd->{commandline};
       
   228             die("Undefined command line") unless $cmdline;
       
   229             my $cwd = "\\";
       
   230             $cwd = $cmd->{cwd} if exists $cmd->{cwd};
       
   231             my $component = $id;
       
   232             $component = $cmd->{component} if exists $cmd->{component};
       
   233 
       
   234             print EBSSCRIPT
       
   235               "<Execute ID=\"$id\" Stage=\"$stage_id\" Component=\"$component\" Cwd=\"$cwd\" CommandLine=\"$cmdline\"\/>\n";
       
   236             $id++;
       
   237         }
       
   238         $stage_id++;
       
   239     }
       
   240     print EBSSCRIPT <<EOT;
       
   241   </Commands>
       
   242 </Product>
       
   243 EOT
       
   244     close(EBSSCRIPT);
       
   245 
       
   246     my $status = run($scriptfile, $logfile);
       
   247     unlink($scriptfile);
       
   248     return $status;
       
   249 }
       
   250 
       
   251 1;
       
   252 
       
   253 __END__
       
   254 
       
   255 =head1 NAME
       
   256 
       
   257 BuildJob - Run BuildServer and BuildClients
       
   258 
       
   259 =head1 SYNOPSIS
       
   260 
       
   261  use BuildJob;
       
   262 
       
   263  BuildJob::run($data_source, $num_clients, $log_file);
       
   264 
       
   265 =head1 DESCRIPTION
       
   266 
       
   267 BuildJob runs a number of BuildClients and the BuildServer with the
       
   268 specified data source and log file.  If undefined, the number of
       
   269 clients defaults to the number of processors on the system and the log
       
   270 file name defaults to "build.log" in the current working directory.
       
   271 
       
   272 Any errors are communicated by a nonzero return code.
       
   273 
       
   274 BuildJob uses port 1973 for communications and looks for the build
       
   275 client and server scripts "buildclient.pl" and "buildserver.pl" in
       
   276 "\src\common\generic\tools\build".  Change these defaults by setting
       
   277 the following variables prior to calling "run":
       
   278 
       
   279  $EBS_PORT = 1973;
       
   280  $EBS_DIR = "\\src\\common\\generic\\tools\\build";
       
   281  $EBS_CLIENT = "buildclient.pl";
       
   282  $EBS_SERVER = "buildserver.pl";
       
   283 
       
   284 This module uses Win32::Job to start and stop the clients and server.
       
   285 
       
   286 If a BuildJob attempts to run on a system that is already running one
       
   287 on the same EBS_PORT, the job will not be run and the original job
       
   288 will continue unaffected.
       
   289 
       
   290 =head1 SEE ALSO
       
   291 
       
   292 L<BuildJob|scripts::BuildJob> is a wrapper to call this module from the command line.
       
   293 
       
   294 =cut