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 "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 |
|
17 |
|
18 use strict; |
|
19 use Getopt::Long; |
|
20 |
|
21 # TODO: |
|
22 # Improve performance by not accessing P4 for every source line in every MRP file |
|
23 # Handle situation where clean-src dir is missing by accessing MRP file from Perforce |
|
24 # Use autobuild database to get perforce information directly? |
|
25 # What should we do about change C reverts change B which reverted change A? |
|
26 # => Hope C has a good description, as this implementation will discard both A and B descriptions. |
|
27 |
|
28 #note: currently relies on existence of perforce report of previous build |
|
29 |
|
30 # Parameters passed to script |
|
31 my $Product = shift; # e.g. 9.3 |
|
32 my $Platform = shift; # e.g. cedar |
|
33 my $CurentBuild = shift; # e.g. 03935 |
|
34 my $CurrentCL = shift; # e.g. 775517 |
|
35 shift; # e.g. 03935_Symbian_OS_v9.3 |
|
36 my $CurrCodeLine = shift; # e.g. //epoc/master/ |
|
37 my $PreviousBuild = shift; # the build number of the previous release (eg 03925) |
|
38 my $LogDirs = shift; # e.g. \\builds01\devbuilds |
|
39 my $CleanSourceDir = shift; # e.g. M:\clean-src (synched file from perforce) |
|
40 |
|
41 my $debug = 0; # set to 1 to print debug logfile and progress information |
|
42 |
|
43 GetOptions( |
|
44 'v' => \$debug); |
|
45 |
|
46 # Derived parameters |
|
47 my $CurrBldName = "$CurentBuild\_Symbian_OS_v$Product"; # e.g. 03935_Symbian_OS_v9.3 |
|
48 |
|
49 |
|
50 # Global variables |
|
51 |
|
52 my $PreviousLogDir; # location of the logs associated with the previous release build |
|
53 my $PrevFileAndPath; # name of the previous release build's perforce report and its location |
|
54 my $PrevBldName; # e.g. 03925_Symbian_OS_v9.3 |
|
55 |
|
56 my $MainLineCL = 0; # the CL of the build from which the current build |
|
57 # was branched if it is on a delivery branch |
|
58 my $MainLineLine; # the codeline of the build from which the current |
|
59 # build was branched if on a delivery branch |
|
60 |
|
61 my %allDefects; # hash containing all of the defect fixes |
|
62 my %allBreaks; # hash contains all of the delivered breaks |
|
63 my %changeToComponents; # hash containing lists of components affected by a given change |
|
64 my %reverted; # hash of reverted changelists |
|
65 |
|
66 # Tidy up directories to ensure they are in a standard format |
|
67 $CurrCodeLine =~ s/[^\/]$/$&\//;# ensure a trailing fwdslash for codeline |
|
68 $LogDirs =~ s/\\$//; # ensure no trailing back slash for codeline |
|
69 $CleanSourceDir =~ s/\\$//; # ensure no trailing back slash for codeline |
|
70 |
|
71 # add information to the debug log if the debug flag is set |
|
72 if ($debug) |
|
73 { |
|
74 # Open an error log |
|
75 open ERRORLOG, "> Errorlog.txt"; |
|
76 print ERRORLOG <<"END"; |
|
77 Inputs: |
|
78 |
|
79 Product: $Product |
|
80 Platform: $Platform |
|
81 Current build: $CurentBuild |
|
82 Current build CL: $CurrentCL |
|
83 Current Build Name: $CurrBldName |
|
84 Previous Build: $PreviousBuild |
|
85 Log Directory: $LogDirs |
|
86 Clean Source Dir: $CleanSourceDir |
|
87 |
|
88 Errors: |
|
89 |
|
90 END |
|
91 } |
|
92 |
|
93 # If the previous release was from a delivery branch, then use the build from |
|
94 # from which the delivery branch was created by removing the letters (changes |
|
95 # on the files on the branch will also have been made on the main codeline) |
|
96 $PreviousBuild =~ s/[a-z]+(\.\d+)?//; |
|
97 |
|
98 # If the current build is on a delivery branch, then store the information about |
|
99 # the build from which it was branched as it will be necessary to get the descriptions |
|
100 # from perforce for versions on the branch and versions on the main codeline up to |
|
101 # where the branch was created separately |
|
102 |
|
103 if ($CurentBuild =~ /[a-z]+/) |
|
104 { |
|
105 my $MainLineBuild = $CurentBuild; |
|
106 $MainLineBuild =~ s/[a-z]+(\.\d+)?//; |
|
107 # There is insufficient information here anyway, e.g. if M09999 failed and we did |
|
108 # M09999.01 as the original candidate, then there is no way of telling the tool |
|
109 |
|
110 my $MainLineBldName = "$MainLineBuild\_Symbian_OS_v$Product"; |
|
111 my $MainLinePerforce = "$LogDirs\\$MainLineBldName\\logs\\$MainLineBuild\_$Product" . "PC_Perforce_report.html"; |
|
112 ($MainLineCL, $MainLineLine) = GetPrevCodelineandCL($MainLinePerforce, $Platform); |
|
113 } |
|
114 |
|
115 # Construct the previous build name |
|
116 $PrevBldName = "$PreviousBuild\_Symbian_OS_v$Product"; |
|
117 |
|
118 # Construct the name of the peforce report file for the previous external release |
|
119 # to look similar to this: 03925_9.3PC_Perforce_report.html |
|
120 my $PerforceReport = $PreviousBuild."_".$Product."PC_Perforce_report.html"; |
|
121 |
|
122 # Look for $PerforceReport in the build logs directory and the log archive directory |
|
123 $PreviousLogDir = "$LogDirs\\$PrevBldName\\logs\\"; |
|
124 |
|
125 $PrevFileAndPath = $PreviousLogDir.$PerforceReport; |
|
126 |
|
127 if (! -d $CleanSourceDir) |
|
128 { |
|
129 # if the report is found in neither directory then die |
|
130 if ($debug==1) |
|
131 { |
|
132 print ERRORLOG "Clean-src directory does not exist! ($CleanSourceDir)\n"; |
|
133 } |
|
134 die "ERROR: Clean-src directory does not exist"; |
|
135 } |
|
136 |
|
137 if (! -e $PrevFileAndPath) |
|
138 { |
|
139 $PreviousLogDir = "$LogDirs\\logs\\$PrevBldName\\"; |
|
140 $PrevFileAndPath = $PreviousLogDir.$PerforceReport; |
|
141 } |
|
142 if (! -e $PrevFileAndPath) |
|
143 { |
|
144 # if the report is found in neither directory then die |
|
145 if ($debug==1) |
|
146 { |
|
147 print ERRORLOG "Could not find Perforce report of previous external release! ($PrevFileAndPath)\n"; |
|
148 } |
|
149 die "ERROR: Cannot find previous Perforce report"; |
|
150 } |
|
151 |
|
152 # Parse the Perforce report to extract the previous build's change list and codeline path |
|
153 my ($PreviousCL, $PrevCodeLine) = GetPrevCodelineandCL($PrevFileAndPath, $Platform); |
|
154 |
|
155 # Create the path of the current build's log directory |
|
156 my $CurrentLogDir = "$LogDirs\\$CurrBldName\\logs\\"; |
|
157 |
|
158 # Obtain the component lists (arrays are passed back by reference, each element containing a component |
|
159 # name separated by one or more spaces from its associated mrp file as it appears in the component files) |
|
160 my ($GTprevCompList, $GTlatestCompList, |
|
161 $TVprevCompList, $TVlatestCompList) = GetGTandTVcomponentLists($CurrentLogDir, $PreviousLogDir); |
|
162 |
|
163 # Consolidate the lists of components in to two hashes: one for the current build and one for the previous |
|
164 # release build. These contain an index to distinguish between GT and TechView components and another index |
|
165 # with the names of the components. The mrp files are the elements being indexed. |
|
166 my ($PrevMRP, $CurrMRP) = CreateMRPLists($GTprevCompList, |
|
167 $GTlatestCompList, |
|
168 $TVprevCompList, |
|
169 $TVlatestCompList); |
|
170 |
|
171 # For each component, extract its source directories from its MRP file and add the information to a list |
|
172 my (@ComponentAndSource) = ProcessLists($Product, |
|
173 $PrevCodeLine, |
|
174 $Platform , |
|
175 $PrevMRP, |
|
176 $CurrMRP, |
|
177 $PreviousCL, |
|
178 $CleanSourceDir); |
|
179 |
|
180 # Put together the HTML file using the components list with their associated source files |
|
181 (my $ChangesFileHTML) = CreateReleaseNotes($Product, |
|
182 $CurrCodeLine, |
|
183 $MainLineLine, |
|
184 $PreviousCL, |
|
185 $CurrentCL, |
|
186 $MainLineCL, |
|
187 @ComponentAndSource); |
|
188 |
|
189 if ($debug) |
|
190 { |
|
191 close ERRORLOG; |
|
192 } |
|
193 |
|
194 print "FINISHED!! - $ChangesFileHTML\n"; |
|
195 |
|
196 exit; |
|
197 |
|
198 # |
|
199 # |
|
200 # Gets component lists from the builds' log directories. Reads the contents |
|
201 # of the files in to arrays and returns references to these arrays. |
|
202 # |
|
203 # Inputs: Current and previous build's logfile locations |
|
204 # Outputs: Arrays containing contents of previous and current GTcomponents.txt |
|
205 # and TVcomponents.txt files (list of components with their associated |
|
206 # mrp files). An example array elment might contain: |
|
207 # "ChatScripts \sf\os\unref\orphan\comtt\chatscripts\group\testtools_chatscripts.mrp" |
|
208 # |
|
209 sub GetGTandTVcomponentLists |
|
210 { |
|
211 my ($CurrLogPath, $PrevLogPath) = @_; |
|
212 |
|
213 # Data to return: array of refs to arrays of contents of files |
|
214 my @return; |
|
215 |
|
216 # Files to read from |
|
217 my @FileNames = ( |
|
218 $PrevLogPath."GTcomponents.txt", |
|
219 $CurrLogPath."GTcomponents.txt", |
|
220 $PrevLogPath."TechViewComponents.txt", |
|
221 $CurrLogPath."TechViewComponents.txt", |
|
222 ); |
|
223 |
|
224 foreach my $FileName (@FileNames) |
|
225 { |
|
226 if (!open(DAT, $FileName)) |
|
227 { |
|
228 if ($debug) |
|
229 { |
|
230 print ERRORLOG "Could not open $FileName!\n"; |
|
231 } |
|
232 die "ERROR: Could not open $FileName: $!"; |
|
233 } |
|
234 push @return, [<DAT>]; |
|
235 close DAT; |
|
236 } |
|
237 |
|
238 return @return; |
|
239 } |
|
240 |
|
241 # |
|
242 # |
|
243 # Creates two list of components and their associated mrp files - one for the previous |
|
244 # build and one for the current build |
|
245 # |
|
246 # Inputs: the contents of the GT and TV components files |
|
247 # Outputs: Two lists of components and their mrp files, one from the previous build |
|
248 # and one from the current build |
|
249 # |
|
250 # |
|
251 sub CreateMRPLists |
|
252 { |
|
253 # references to the contents of the components files |
|
254 my $GTprevFile = shift; |
|
255 my $GTlatestFile = shift; |
|
256 my $TVprevFile = shift; |
|
257 my $TVlatestFile = shift; |
|
258 |
|
259 my %PreviousMrps; #Hash of all Components and their MRP file locations for the previous build |
|
260 my %CurrentMrps; #Hash of all Components and their MRP file locations for the current build |
|
261 |
|
262 # Add mrp files to a hash indexed under GT or TV and by component names |
|
263 foreach my $case ( |
|
264 [\%PreviousMrps, 'GT', $GTprevFile], |
|
265 [\%CurrentMrps, 'GT', $GTlatestFile], |
|
266 [\%PreviousMrps, 'TV', $TVprevFile], |
|
267 [\%CurrentMrps, 'TV', $TVlatestFile], |
|
268 ) |
|
269 { |
|
270 foreach my $element (@{$case->[2]}) |
|
271 { |
|
272 if ( $element =~ /(.*)\s+(\\sf\\.*)$/ ) |
|
273 { |
|
274 ${$case->[0]}{$case->[1]}{uc($1)} = lc($2); |
|
275 } |
|
276 } |
|
277 } |
|
278 |
|
279 return \%PreviousMrps, \%CurrentMrps; |
|
280 } |
|
281 |
|
282 |
|
283 # |
|
284 # |
|
285 # Use the contents of the MRP files to create a single list containing the GT and TechView |
|
286 # components paired with associated source files. |
|
287 # |
|
288 # Inputs: Product, Codeline, Previous Codeline, Platform, Both lists of mrps, |
|
289 # Previous Changelist number, Build machine's clean source directory which |
|
290 # contains copies of the current build's mrp files. |
|
291 # Outputs: Array containing all components and their source locations |
|
292 # |
|
293 # |
|
294 sub ProcessLists |
|
295 { |
|
296 my $Product = shift; |
|
297 my $PrevCodeLine = shift; |
|
298 my $Platform = shift; |
|
299 my $PreviousMrps = shift; #Hash of MRPs at the Previous changelist number |
|
300 my $CurrentMrps = shift; #Hash of MRPs at the Current changelist number |
|
301 my $PrevCL = shift; |
|
302 my $CleanSourceDir = shift; |
|
303 |
|
304 my @PrevGTMrpComponents; #Array of GT Components at Previous changelist number |
|
305 my @CurrGTMrpComponents; #Array of GT Components at Current changelist number |
|
306 my @PrevTVMrpComponents; #Array of TV Components at Previous changelist number |
|
307 my @CurrTVMrpComponents; #Array of TV Components at Current changelist number |
|
308 |
|
309 # Isolate hashes |
|
310 my $CurrGT = $CurrentMrps->{'GT'}; |
|
311 my $CurrTV = $CurrentMrps->{'TV'}; |
|
312 my $PrevGT = $PreviousMrps->{'GT'}; |
|
313 my $PrevTV = $PreviousMrps->{'TV'}; |
|
314 |
|
315 # Append the location of the clean source directory for the current build |
|
316 # to all the mrp files. If a file appears only in the previous build (i.e. |
|
317 # a component has been removed) its location in perforce is later substituted |
|
318 # in place of this path |
|
319 foreach my $component (sort keys %$CurrGT) |
|
320 { |
|
321 $CurrGT->{$component} =~ s|\\sf|$CleanSourceDir|ig; |
|
322 $CurrGT->{$component} =~ s|\\|\/|g; |
|
323 push @CurrGTMrpComponents, "$component\t$CurrGT->{$component}"; |
|
324 } |
|
325 |
|
326 foreach my $component (sort keys %$CurrTV) |
|
327 { |
|
328 $CurrTV->{$component} =~ s|\\sf|$CleanSourceDir|ig; |
|
329 $CurrTV->{$component} =~ s|\\|\/|g; |
|
330 push @CurrTVMrpComponents, "$component\t$CurrTV->{$component}"; |
|
331 } |
|
332 |
|
333 foreach my $component (sort keys %$PrevGT) |
|
334 { |
|
335 $PrevGT->{$component} =~ s|\\sf|$CleanSourceDir|ig; |
|
336 $PrevGT->{$component} =~ s|\\|\/|g; |
|
337 push @PrevGTMrpComponents, "$component"."\t"."$PrevGT->{$component}"; |
|
338 } |
|
339 |
|
340 foreach my $component (sort keys %$PrevTV) |
|
341 { |
|
342 $PrevTV->{$component} =~ s|\\sf|$CleanSourceDir|ig; |
|
343 $PrevTV->{$component} =~ s|\\|\/|g; |
|
344 push @PrevTVMrpComponents, "$component"."\t"."$PrevTV->{$component}"; |
|
345 } |
|
346 |
|
347 # add any components that appear only in the previous build's list to the |
|
348 # current build's list with its location in perforce. |
|
349 foreach my $PrevGTComp(@PrevGTMrpComponents) |
|
350 { |
|
351 my $match = 0; |
|
352 #Compare component lists to ensure they contain the same components. |
|
353 foreach my $CurrGTComp(@CurrGTMrpComponents) |
|
354 { |
|
355 $match = 1 if($PrevGTComp eq $CurrGTComp); |
|
356 } |
|
357 |
|
358 #If a component is found in the Previous list which isn't in the Current list, |
|
359 #then insert it into the Current list with the previous build's path |
|
360 if($match == 0) |
|
361 { |
|
362 $PrevGTComp =~ s|\/|\\|g; |
|
363 $PrevGTComp =~ s|\Q$CleanSourceDir\E\\|$PrevCodeLine|ig; |
|
364 $PrevGTComp =~ s|\\|\/|g; |
|
365 push @CurrGTMrpComponents, $PrevGTComp; |
|
366 } |
|
367 } |
|
368 |
|
369 # add any components that appear only in the previous build's list to the |
|
370 # current build's list with its location in perforce. |
|
371 foreach my $PrevTVComp(@PrevTVMrpComponents) |
|
372 { |
|
373 my $match = 0; |
|
374 #Compare component lists to ensure they contain the same components. |
|
375 foreach my $CurrTVComp(@CurrTVMrpComponents) |
|
376 { |
|
377 $match = 1 if($PrevTVComp eq $CurrTVComp); |
|
378 } |
|
379 |
|
380 #If a component is found in the Previous list which isn't in the Current list, |
|
381 #then insert it into the Current list |
|
382 if($match == 0) |
|
383 { |
|
384 $PrevTVComp =~ s|$CleanSourceDir|$PrevCodeLine|ig; |
|
385 push @CurrTVMrpComponents, $PrevTVComp; |
|
386 } |
|
387 } |
|
388 |
|
389 # Combine current GT and TV components, with a boundary marker |
|
390 my @CurrMrpComponents = (@CurrGTMrpComponents, "**TECHVIEWCOMPONENTS**", @CurrTVMrpComponents); |
|
391 |
|
392 # Swap the back slashes for forward slashes |
|
393 $CleanSourceDir =~ s/\\/\//g; |
|
394 $PrevCodeLine =~ s/\\/\//g; |
|
395 |
|
396 #Use the MRP file for each component to obtain the source directory locations |
|
397 my @ComponentAndSource; |
|
398 foreach my $ComponentLine(@CurrMrpComponents) |
|
399 { |
|
400 #Array to hold mrp file contents |
|
401 my @MrpContents; |
|
402 |
|
403 # if the file is in the Clean Source Directory then read its contents from there |
|
404 if($ComponentLine =~ /.*(\Q$CleanSourceDir\E.*)/) |
|
405 { |
|
406 my $MrpFile = $1; |
|
407 $MrpFile =~ s/\.mrp.*$/\.mrp/; #drop any trailing spaces or tabs |
|
408 if (-e $MrpFile) |
|
409 { |
|
410 open(FILE, $MrpFile); |
|
411 @MrpContents=<FILE>; |
|
412 close FILE; |
|
413 } |
|
414 } |
|
415 elsif($ComponentLine =~ /.*(\Q$PrevCodeLine\E.*)/) |
|
416 { |
|
417 # If a component has been removed between the previous build and the current one then |
|
418 # its MRP file will only exist at the PrevCL |
|
419 @MrpContents = `p4 print -q $1...\@$PrevCL`; # access Perforce via system command |
|
420 } |
|
421 elsif($ComponentLine =~ /\*\*TECHVIEWCOMPONENTS\*\*/) |
|
422 { |
|
423 push @MrpContents, $ComponentLine; |
|
424 } |
|
425 |
|
426 #Construct an array containing components in uppercase followed by |
|
427 #all their sourcelines in lowercase |
|
428 foreach my $line(@MrpContents) |
|
429 { |
|
430 if($line =~ /^component\s+(.*)/i) |
|
431 { |
|
432 my $ComponentName = uc($1); |
|
433 push @ComponentAndSource, $ComponentName; |
|
434 } |
|
435 elsif($line =~ /^source\s+(.*)/i) |
|
436 { |
|
437 my $Source = lc($1); |
|
438 $Source =~ s/\\/\//g; |
|
439 $Source =~ s|/sf/||; |
|
440 $Source =~ s|/product/|$Platform/product|ig; |
|
441 push @ComponentAndSource, $Source; |
|
442 } |
|
443 elsif($line =~ /TECHVIEWCOMPONENTS/) |
|
444 { |
|
445 push @ComponentAndSource, $line; |
|
446 } |
|
447 } |
|
448 } |
|
449 return @ComponentAndSource; |
|
450 } |
|
451 |
|
452 # |
|
453 # Format the changes associated with a component |
|
454 # |
|
455 # Inputs: |
|
456 # reference to hash of change descriptions by changelist number, |
|
457 # component name, |
|
458 # reference to (formatted) list of names of changed components |
|
459 # reference to list of names of unchanged components |
|
460 # |
|
461 # Updates: |
|
462 # adds component name to changed or unchanged list, as appropriate |
|
463 # |
|
464 sub PrintComponentDescriptions(\%$\@\@) |
|
465 { |
|
466 my ($Descriptions,$CompName,$ChangedComponents,$UnchangedComponents) = @_; |
|
467 |
|
468 if (scalar keys %{$Descriptions} == 0) |
|
469 { |
|
470 # no changes in this component |
|
471 push @{$UnchangedComponents}, $CompName; |
|
472 return; |
|
473 } |
|
474 push @{$ChangedComponents}, "<a href=\"#$CompName\">$CompName</a>"; |
|
475 |
|
476 # Format the changes for this component |
|
477 |
|
478 my @CompLines = ("<h2><a name=\"$CompName\"/>$CompName</h2>"); |
|
479 foreach my $change (reverse sort keys %{$Descriptions}) |
|
480 { |
|
481 # Heading for the change description |
|
482 my $summary = shift @{$$Descriptions{$change}}; |
|
483 $summary =~ s/(on .*)\s+by.*//; |
|
484 $summary = "<a href=\"#$change\">Change $change</a> $1"; |
|
485 push @CompLines, "<p><a name=\"#$CompName $change\"/><b>$summary</b>"; |
|
486 # Body of the change description |
|
487 push @CompLines, "<pre>"; |
|
488 push @CompLines, |
|
489 grep { $_; } # ignore blank lines |
|
490 @{$$Descriptions{$change}}; |
|
491 push @CompLines, "</pre>"; |
|
492 |
|
493 # record the component in the cross-reference table |
|
494 push @{$changeToComponents{$change}}, "<a href=\"#$CompName $change\">$CompName</a>"; |
|
495 } |
|
496 |
|
497 &PrintLines(@CompLines); |
|
498 } |
|
499 |
|
500 # |
|
501 # |
|
502 # Creates the Release Notes html file by extracting the perforce descriptions for each |
|
503 # change that has been made to the components |
|
504 # |
|
505 # Inputs: Product, Source path of the product (i.e.//EPOC/master), the source path on the |
|
506 # main codeline if a delivery branch is being used, Previous changelist, |
|
507 # Current changelist, changelist from which the branch was made if a branch is being |
|
508 # used, array of components and their source. |
|
509 # Outputs: Release Notes html file. |
|
510 # |
|
511 # Algorithm: |
|
512 # Loop through the component&source array |
|
513 # Determine whether a boundary, component name, a source dir |
|
514 # If a source dir, query perforce to see what changelists have affected it in the period that we're interested in |
|
515 # If it has been affected, add the changelist to this component's changelists, unless it's already there |
|
516 # |
|
517 # This is rather inefficient :-( |
|
518 # |
|
519 sub CreateReleaseNotes |
|
520 { |
|
521 my $Product = shift; |
|
522 my $Srcpath = shift; |
|
523 my $MainLinePath = shift; |
|
524 my $PrevCL = shift; |
|
525 my $CurrentCL = shift; |
|
526 my $BranchCL = shift; |
|
527 my @ComponentAndSource = @_; |
|
528 |
|
529 #Reset all arrays to NULL |
|
530 my @PrevGTMrpComponents = (); |
|
531 my @CurrGTMrpComponents = (); |
|
532 my @PrevTVMrpComponents = (); |
|
533 my @CurrTVMrpComponents = (); |
|
534 my @CurrMrpComponents = (); |
|
535 my @Components = (); |
|
536 my @UnchangedComponents = (); |
|
537 my @ChangedComponents = (); |
|
538 my %changeProcessed = (); # hash of changelists processed for this component |
|
539 my %changeToDescription = (); # hash of descriptions for non-reverted changelists |
|
540 |
|
541 $PrevCL++; # increment changelist number so we don't include the very first submission |
|
542 # - it would have been picked up in the last run of this script |
|
543 |
|
544 my $ProductName = "Symbian_OS_v$Product\_Changes_Report"; |
|
545 |
|
546 open OUTFILE, "> $ProductName.html" or die "ERROR: Can't open $ProductName.html for output\n$!"; |
|
547 print OUTFILE <<HEADING_EOF; |
|
548 <html>\n\n<head>\n<title>$ProductName</title>\n</head>\n\n |
|
549 <body bgcolor="#ffffff" text="#000000" link="#5F9F9F" vlink="5F9F9F">\n |
|
550 <font face=verdana,arial,helvetica size=4>\n\n<hr>\n\n |
|
551 <a name="list"><h1><center>$ProductName</center></h1></a> |
|
552 <p><center>----------------------------------------</center>\n |
|
553 <h2><center><font color = "blue">GT Components</font></center></h2>\n |
|
554 HEADING_EOF |
|
555 |
|
556 my $CompName; |
|
557 my $dirCount = 0; |
|
558 my $spacer = "\n"; |
|
559 |
|
560 |
|
561 # Loop through the list of elements running perforce commands to obtain the descriptions |
|
562 # of any changes that have been made since the last release build |
|
563 foreach my $element(@ComponentAndSource) |
|
564 { |
|
565 # Is it the boundary? |
|
566 if($element =~ /\*\*TECHVIEWCOMPONENTS\*\*/) |
|
567 { |
|
568 # print out the accumulated GT component, if any |
|
569 &PrintComponentDescriptions(\%changeToDescription, $CompName, \@ChangedComponents, \@UnchangedComponents) if ($dirCount); |
|
570 $dirCount = 0; |
|
571 # no need to clear the hashes, because that will happen at the first TV component |
|
572 |
|
573 print OUTFILE "<h2><center><font color = \"blue\">Techview Components</font></center></h2>\n"; |
|
574 } |
|
575 # Is it a component name? |
|
576 elsif($element =~ /^([A-Z].*)/) |
|
577 { |
|
578 my $newName = $1; |
|
579 |
|
580 &PrintComponentDescriptions(\%changeToDescription, $CompName, \@ChangedComponents, \@UnchangedComponents) if ($dirCount); |
|
581 $dirCount = 0; |
|
582 |
|
583 $CompName = $newName; |
|
584 %changeProcessed = (); |
|
585 %changeToDescription = (); |
|
586 print "Processing $CompName...\n" if ($debug); |
|
587 } |
|
588 # Is it a source directory? |
|
589 elsif($element =~ /^([a-z].*?)\s*$/) |
|
590 { |
|
591 my $Topdir = $1; |
|
592 $dirCount++; |
|
593 |
|
594 # If it contains spaces, quote it |
|
595 if($Topdir =~ /.*\s+.*/) |
|
596 { |
|
597 $Topdir = "\"$Topdir\""; |
|
598 } |
|
599 |
|
600 # Find the changes that have affected this dir |
|
601 # (Changes come back in reverse chronological order) |
|
602 my @CompChange = `p4 changes -l $Srcpath$Topdir...\@$PrevCL,\@$CurrentCL`; |
|
603 |
|
604 # if the current release is on a branch then the p4 command also needs to be run |
|
605 # to capture changes on the codeline before the files were branched |
|
606 if ($MainLinePath) |
|
607 { |
|
608 # So far, @CompChange contains the info from the delivery |
|
609 # branch only |
|
610 # The earliest changelist on the delivery branch is the creation |
|
611 # of the branch, which is not interesting to anyone. |
|
612 # Hence, remove it here: |
|
613 |
|
614 # Work backwards from the end of the output chopping off the |
|
615 # last item till we've chopped off a changelist header |
|
616 my $choppedText; |
|
617 do |
|
618 { |
|
619 # Remove last line |
|
620 $choppedText = pop @CompChange; |
|
621 } |
|
622 until (!defined $choppedText || $choppedText =~ m/^Change\s\d+\s/); |
|
623 |
|
624 # Now append the earlier changes from the main line |
|
625 my @extrainfo = `p4 changes -l $MainLinePath$Topdir...\@$PrevCL,\@$BranchCL`; |
|
626 push @CompChange, @extrainfo; |
|
627 } |
|
628 |
|
629 # Normalise the output of P4 |
|
630 @CompChange = map { split "\n"; } @CompChange; |
|
631 |
|
632 # Extract change descriptions into a hash keyed on changelist number |
|
633 my %moreChangeDescriptions; |
|
634 my $change; |
|
635 foreach my $line (@CompChange) |
|
636 { |
|
637 if ($line =~ /^Change\s(\d+)\s/) |
|
638 { |
|
639 my $newchange = $1; |
|
640 $change = ""; |
|
641 # ignore if already processed for this component |
|
642 next if ($changeProcessed{$newchange}); |
|
643 $changeProcessed{$newchange} = 1; |
|
644 $change = $newchange; |
|
645 } |
|
646 next if (!$change); |
|
647 push @{$moreChangeDescriptions{$change}}, $line; |
|
648 } |
|
649 |
|
650 # Process changes (newest first), extracting the <EXTERNAL> section and |
|
651 # processing any reversion information |
|
652 foreach my $change (reverse sort keys %moreChangeDescriptions) |
|
653 { |
|
654 if ($reverted{$change}) |
|
655 { |
|
656 print "REMARK: $CompName - deleting description of reverted change $change\n"; |
|
657 delete $moreChangeDescriptions{$change}; |
|
658 next; |
|
659 } |
|
660 |
|
661 my @changeLines = @{$moreChangeDescriptions{$change}}; |
|
662 |
|
663 my @revisedLines = (); |
|
664 push @revisedLines, shift @changeLines; # keep the "Change" line |
|
665 |
|
666 my $line; |
|
667 while ($line = shift @changeLines) |
|
668 { |
|
669 last if ($line =~ /<EXTERNAL>$/); |
|
670 |
|
671 $line =~ s/^\t+//; |
|
672 $line =~ s/\&/&/g; |
|
673 $line =~ s/\</</g; |
|
674 $line =~ s/\>/>/g; |
|
675 $line =~ s/\"/"/g; # quote the " character as well |
|
676 |
|
677 push @revisedLines, $line; |
|
678 } |
|
679 |
|
680 if (scalar @changeLines == 0) |
|
681 { |
|
682 # consumed the whole description without seeing <EXTERNAL> |
|
683 # must be old format |
|
684 @{$changeToDescription{$change}} = @revisedLines; |
|
685 printf ".. unformatted change %d - %d lines\n", $change, scalar @changeLines if ($debug); |
|
686 next; |
|
687 } |
|
688 |
|
689 # Must be properly formatted change description |
|
690 # discard everything seen so far except the "Change" line. |
|
691 @revisedLines = (shift @revisedLines); |
|
692 |
|
693 # Process all lines starting with the first <EXTERNAL> section. |
|
694 while (($line =~ /<EXTERNAL>$/) || ($line = shift @changeLines)) |
|
695 { |
|
696 # Process an <EXTERNAL> section. |
|
697 while ($line =~ /<EXTERNAL>$/) |
|
698 { |
|
699 # Keep processing until an </EXTERNAL> line is encountered |
|
700 while(($line = shift @changeLines) && ($line !~ /<\/EXTERNAL>$/)) |
|
701 { |
|
702 $line =~ s/^\t+//; |
|
703 $line =~ s/\&/&/g; |
|
704 $line =~ s/\</</g; |
|
705 $line =~ s/\>/>/g; |
|
706 $line =~ s/\"/"/g; # quote the " character as well |
|
707 |
|
708 push @revisedLines, $line; |
|
709 |
|
710 # Opportunity to pick information out of changes as they go past |
|
711 if ($line =~ /^\s*((DEF|PDEF|INC)\d+):?\s/) |
|
712 { |
|
713 $allDefects{$1} = $line; |
|
714 next; |
|
715 } |
|
716 if ($line =~ /^(BR[0-9.]+)\s/) |
|
717 { |
|
718 $allBreaks{$1} = $line; |
|
719 next; |
|
720 } |
|
721 } |
|
722 push @revisedLines, $spacer; |
|
723 } |
|
724 } |
|
725 |
|
726 # update the description for this change |
|
727 @{$changeToDescription{$change}} = @revisedLines; |
|
728 printf ".. formatted change %d - %d external lines\n", $change, scalar @revisedLines if ($debug); |
|
729 |
|
730 # check for reversion information in rest of the formatted description |
|
731 # submission checker delivers one <detail reverts=""> line per list element |
|
732 foreach my $line (grep /<detail reverts=/, @changeLines) |
|
733 { |
|
734 if ($line =~ /<detail reverts=\s*\"(\d+)\"/) # changelist surrounded by " |
|
735 { |
|
736 my $oldchange = $1; |
|
737 print "REMARK: Change $change reverts $oldchange\n"; |
|
738 $reverted{$oldchange} = $change; |
|
739 } |
|
740 } |
|
741 } |
|
742 } |
|
743 } |
|
744 |
|
745 # print description of last component |
|
746 &PrintComponentDescriptions(\%changeToDescription, $CompName, \@ChangedComponents, \@UnchangedComponents) if ($dirCount); |
|
747 |
|
748 # Print additional tables of information |
|
749 &PrintLines("<h2>Changed Components</h2>\n<nobr>", join(",</nobr> \n<nobr>", sort @ChangedComponents), "</nobr>") if (@ChangedComponents); |
|
750 |
|
751 &PrintLines("<h2>Unchanged Components</h2>\n<nobr>", join(",</nobr> \n<nobr>", sort @UnchangedComponents), "</nobr>") if (@UnchangedComponents); |
|
752 |
|
753 if (scalar @ChangedComponents) |
|
754 { |
|
755 &PrintLines("<h2>Components affected by each change</h2>"); |
|
756 |
|
757 &PrintLines("<TABLE>\n"); |
|
758 foreach my $change (reverse sort keys %changeToComponents) |
|
759 { |
|
760 &PrintLines("<TR valign=\"top\"><TD><a name=\"#$change\"/>$change</TD><TD>", join(", ", @{$changeToComponents{$change}}), "</TD></TR>\n"); |
|
761 } |
|
762 &PrintLines("</TABLE>\n"); |
|
763 } |
|
764 |
|
765 my @allDefectTitles = (); |
|
766 foreach my $defect (sort keys %allDefects) |
|
767 { |
|
768 push @allDefectTitles,$allDefects{$defect}; |
|
769 } |
|
770 &PrintLines("<h2>List of Defect Fixes</h2>", "<pre>", @allDefectTitles, "</pre>") if (scalar @allDefectTitles); |
|
771 |
|
772 my @allBreakTitles = (); |
|
773 foreach my $break (sort keys %allBreaks) |
|
774 { |
|
775 push @allBreakTitles,$allBreaks{$break}; |
|
776 } |
|
777 &PrintLines("<h2>List of Breaks</h2>", "<pre>", @allBreakTitles, "</pre>") if (scalar @allBreakTitles); |
|
778 |
|
779 &PrintLines("</BODY></HTML>"); |
|
780 close OUTFILE; |
|
781 |
|
782 return "$ProductName.html"; |
|
783 } |
|
784 |
|
785 sub PrintLines |
|
786 { |
|
787 # Output field and record separators set (locally) to produce newlines |
|
788 # between items in list, and another newline on the end |
|
789 local $, = "\n"; |
|
790 local $\ = "\n"; |
|
791 print OUTFILE @_; |
|
792 } |
|
793 |
|
794 # |
|
795 # |
|
796 # Extracts the sourcecode path and changelist from a Perforce report file. |
|
797 # |
|
798 # Inputs: Location of a perforce report file, Platform (e.g. cedar) |
|
799 # Outputs: Source path of the product (e.g.//EPOC/master), changelist used in build |
|
800 # |
|
801 # |
|
802 sub GetPrevCodelineandCL |
|
803 { |
|
804 my $FileAndPath = shift; |
|
805 my $Platform = shift; |
|
806 |
|
807 my $LogFile; |
|
808 my $PrevCL; |
|
809 my $PrevCodeline; |
|
810 |
|
811 if (!open(DAT, $FileAndPath)) |
|
812 { |
|
813 print ERRORLOG "Could not open $FileAndPath!\n" if $debug; |
|
814 die "ERROR: Cannot open $FileAndPath: $!\n"; |
|
815 } |
|
816 { |
|
817 # Grab complete file in one string |
|
818 local $/ = undef; |
|
819 $LogFile = <DAT>; |
|
820 } |
|
821 close DAT; |
|
822 |
|
823 if ($LogFile =~ m{ClientSpec.*?<td[^>]*>.*?(//.+?)$Platform/.*?</tr>}s) |
|
824 { |
|
825 $PrevCodeline = $1; |
|
826 } |
|
827 |
|
828 if ($LogFile =~ m{Perforce Change[Ll]ist.*?<td[^>]*>(.*?)</tr>}s) |
|
829 { |
|
830 # Perforce changelist table data may either be in form of "nnnnnn" or "ssssss @ nnnnnn" |
|
831 # ssssss is the snapshot id, which may be a string of digits |
|
832 # nnnnnn is the changelist number |
|
833 # |
|
834 # Get the later string of digits |
|
835 ($PrevCL) = $1 =~ m{(\d+)\D*$}; |
|
836 } |
|
837 |
|
838 unless ($PrevCL) { die "ERROR: Unable to parse previous changelist from $FileAndPath\n"; } |
|
839 unless ($PrevCodeline) { die "ERROR: Unable to parse previous codeline from $FileAndPath\n"; } |
|
840 |
|
841 return $PrevCL, $PrevCodeline; |
|
842 } |
|
843 |
|
844 |
|