|
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 |