buildframework/helium/tools/common/packages/BuildJob.pm
changeset 1 be27ed110b50
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buildframework/helium/tools/common/packages/BuildJob.pm	Wed Oct 28 14:39:48 2009 +0000
@@ -0,0 +1,294 @@
+#
+# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+#
+#!perl -w
+# ==============================================================================
+#  %name:          BuildJob.pm %
+#  Part of:        Juno Build Tools
+#  Requires:       Symbian OS build tools
+#
+#  %derived_by:    hasegawa %
+#  %version:	   to1r1103#4.1.2 %
+#  %date_modified: Thu Oct  5 15:41:26 2006 %
+#
+#  See POD text at the end of this file for usage and other details.
+# ==============================================================================
+
+package BuildJob;
+use strict;
+use warnings;
+use Win32::Job;
+use Config;
+use Exporter;
+
+our @ISA       = qw(Exporter);
+our @EXPORT_OK = qw(EBS_PORT EBS_DIR EBS_CLIENT EBS_SERVER);
+
+our $EPOCROOT = $ENV{EPOCROOT} || "\\";
+our $BUILD_DRIVE = $ENV{BUILD_DRIVE};
+
+my @EBS_PATHS = ("epoc32\\tools\\build", "src\\common\\generic\\tools\\build");
+
+$BuildJob::EBS_PORT   = 1973;
+$BuildJob::EBS_DIR    = "";
+$BuildJob::EBS_CLIENT = "buildclient.pl";
+$BuildJob::EBS_SERVER = "buildserver.pl";
+
+sub find_ebs
+{
+    return 1 if -d "$BuildJob::EBS_DIR\\$BuildJob::EBS_CLIENT";
+    foreach (map("${BuildJob::BUILD_DRIVE}${BuildJob::EPOCROOT}$_", @EBS_PATHS))
+    {
+        $BuildJob::EBS_DIR = $_ and return 1 if -f "$_\\$BuildJob::EBS_CLIENT";
+    }
+    return 0;
+}
+
+sub run
+{
+    my $data_source = shift;
+    return 0 unless ($data_source and -e $data_source);
+
+    # finding free port.
+    my $offset = 0;
+    my $trials = 10;
+    $BuildJob::EBS_PORT = $BuildJob::EBS_PORT + int(rand(1000));
+    for(;$offset < $trials; $offset++) {
+      if (!is_buildjob_running($BuildJob::EBS_PORT + $offset)) {
+        $BuildJob::EBS_PORT = $BuildJob::EBS_PORT + $offset;
+        last;
+      }
+    }
+    warn("Can't start build job, is one already running?\n") and return 0 if ($offset > $trials); 
+    print "Using port: $BuildJob::EBS_PORT\n";
+    
+    my $log_file = shift;
+    $log_file = "build.log" unless $log_file;
+    my $num_clients = shift;
+
+    # default number of clients is the number of processors
+    $num_clients = $ENV{NUMBER_OF_PROCESSORS} unless ($num_clients);
+
+    # if we can't find the number of processors, just run one client
+    $num_clients = 1 unless ($num_clients);
+
+    find_ebs()
+      or die("Can't find EBS scripts; looked in: "
+             . join(",", map("${BuildJob::EPOCROOT}$_", @EBS_PATHS)));
+
+    my $job = Win32::Job->new();
+    for (my $i = 0; $i < $num_clients; $i++)
+    {
+        $job->spawn(
+            $Config{perlpath},
+            "perl $BuildJob::EBS_DIR\\$BuildJob::EBS_CLIENT -d localhost:$BuildJob::EBS_PORT -w 2 -c EBS_CLIENT$i",
+            {new_console => 1, window_attr => 'minimized'});
+    }
+    my $server_pid = $job->spawn($Config{perlpath},
+                                 "perl $BuildJob::EBS_DIR\\$BuildJob::EBS_SERVER -d $data_source -p $BuildJob::EBS_PORT -l $log_file"
+                                );
+
+    my $job_failure = 0;
+    {
+        # kill the job if we receive a sigint
+        local $SIG{INT} = sub {$job->kill();};
+
+        # start job with no timeout (a null watchdog run every 60s)
+        # and complete when the first process terminates
+        $job_failure = !$job->watch(sub {return 0}, 60, 0);
+    }
+
+    my $job_status = $job->status();
+    $job_failure ||= ($job_status->{$server_pid}{exitcode} != 0);
+
+    if ($job_failure)
+    {
+        print(STDERR "Abnormal job termination:\nProcess\tExit Code\n");
+        foreach my $pid (keys %$job_status)
+        {
+            print(  STDERR (($pid == $server_pid) ? "server" : "client") . "\t"
+                  . $job_status->{$pid}{exitcode}
+                  . "\n");
+        }
+    }
+    return !$job_failure;
+}
+
+sub is_buildjob_running
+{
+    my $port = shift;
+    use IO::Socket::INET;
+
+    # try to open a socket on the build server port
+    my $sock =
+      IO::Socket::INET->new(LocalAddr => '127.0.0.1',
+                            LocalPort => $port,
+                            Proto     => 'tcp',
+                            Timeout   => 2);
+
+    # failure means the server is probably running already
+    my $is_port_available = !defined $sock;
+    $sock->close() if $sock;
+    return $is_port_available;
+}
+
+sub new
+{
+    my $invocant = shift;
+    my $name     = shift;
+    my $class    = ref($invocant) || $invocant;
+    my $self     = {name => $name, stages => [], env => []};
+    bless($self, $class);
+    $self->new_stage();
+    return $self;
+}
+
+sub set
+{
+    my $self  = shift;
+    my $var   = shift;
+    my $value = shift;
+
+    push(@{$self->{env}},
+         {name  => $var,
+          value => $value});
+}
+
+sub add
+{
+    my $self = shift;
+    push(@{$self->{stages}->[0]}, shift);
+}
+
+sub new_stage
+{
+    my $self = shift;
+    unshift(@{$self->{stages}}, []);
+}
+
+sub go
+{
+    my $self       = shift;
+    my $logfile    = shift;
+    my $scriptfile = $self->{name} . ".xml";
+
+    open(EBSSCRIPT, ">$scriptfile") or die("Can't open $scriptfile: $!");
+    print EBSSCRIPT <<EOT;
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="build.xsl"?>
+<!-- <!DOCTYPE BUILD SYSTEM "ebs.dtd"> -->
+<!DOCTYPE Build  [
+  <!ELEMENT Product (Commands)>
+  <!ATTLIST Product name CDATA #REQUIRED>
+  <!ELEMENT Commands (Execute+ | SetEnv*)>
+  <!ELEMENT Execute EMPTY>
+  <!ATTLIST Execute ID CDATA #REQUIRED>
+  <!ATTLIST Execute Stage CDATA #REQUIRED>
+  <!ATTLIST Execute Component CDATA #REQUIRED>
+  <!ATTLIST Execute Cwd CDATA #REQUIRED>
+  <!ATTLIST Execute CommandLine CDATA #REQUIRED>
+  <!ELEMENT SetEnv EMPTY>
+  <!ATTLIST SetEnv Order ID #REQUIRED>
+  <!ATTLIST SetEnv Name CDATA #REQUIRED>
+  <!ATTLIST SetEnv Value CDATA #REQUIRED>
+]>
+<Product name="zip">
+  <Commands>
+    <SetEnv Order="1" Name="PATH" Value="\\epoc32\\gcc\\bin;\\epoc32\\tools;%PATH%"/>
+EOT
+
+    my $order = 2;
+    foreach my $setenv (@{$self->{env}})
+    {
+        print EBSSCRIPT
+          "<SetEnv Order=\"$order\" Name=\"$setenv->{name}\" Value=\"$setenv->{value}\"/>\n";
+        $order++;
+    }
+
+    my $id       = 1;
+    my $stage_id = 1;
+    foreach my $stage (reverse @{$self->{stages}})
+    {
+        foreach my $cmd (@{$stage})
+        {
+            my $cmdline;
+            $cmdline = $cmd->{commandline} if exists $cmd->{commandline};
+            die("Undefined command line") unless $cmdline;
+            my $cwd = "\\";
+            $cwd = $cmd->{cwd} if exists $cmd->{cwd};
+            my $component = $id;
+            $component = $cmd->{component} if exists $cmd->{component};
+
+            print EBSSCRIPT
+              "<Execute ID=\"$id\" Stage=\"$stage_id\" Component=\"$component\" Cwd=\"$cwd\" CommandLine=\"$cmdline\"\/>\n";
+            $id++;
+        }
+        $stage_id++;
+    }
+    print EBSSCRIPT <<EOT;
+  </Commands>
+</Product>
+EOT
+    close(EBSSCRIPT);
+
+    my $status = run($scriptfile, $logfile);
+    unlink($scriptfile);
+    return $status;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+BuildJob - Run BuildServer and BuildClients
+
+=head1 SYNOPSIS
+
+ use BuildJob;
+
+ BuildJob::run($data_source, $num_clients, $log_file);
+
+=head1 DESCRIPTION
+
+BuildJob runs a number of BuildClients and the BuildServer with the
+specified data source and log file.  If undefined, the number of
+clients defaults to the number of processors on the system and the log
+file name defaults to "build.log" in the current working directory.
+
+Any errors are communicated by a nonzero return code.
+
+BuildJob uses port 1973 for communications and looks for the build
+client and server scripts "buildclient.pl" and "buildserver.pl" in
+"\src\common\generic\tools\build".  Change these defaults by setting
+the following variables prior to calling "run":
+
+ $EBS_PORT = 1973;
+ $EBS_DIR = "\\src\\common\\generic\\tools\\build";
+ $EBS_CLIENT = "buildclient.pl";
+ $EBS_SERVER = "buildserver.pl";
+
+This module uses Win32::Job to start and stop the clients and server.
+
+If a BuildJob attempts to run on a system that is already running one
+on the same EBS_PORT, the job will not be run and the original job
+will continue unaffected.
+
+=head1 SEE ALSO
+
+L<BuildJob|scripts::BuildJob> is a wrapper to call this module from the command line.
+
+=cut