|
1 # Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 # All rights reserved. |
|
3 # This component and the accompanying materials are made available |
|
4 # under the terms of the License "Eclipse Public License v1.0" |
|
5 # which accompanies this distribution, and is available |
|
6 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 # |
|
8 # Initial Contributors: |
|
9 # Nokia Corporation - initial contribution. |
|
10 # |
|
11 # Contributors: |
|
12 # |
|
13 # Description: |
|
14 # |
|
15 # |
|
16 |
|
17 use strict; |
|
18 |
|
19 package CheckBc; |
|
20 |
|
21 |
|
22 # |
|
23 # Public. |
|
24 # |
|
25 |
|
26 sub New { |
|
27 my $pkg = shift; |
|
28 my $self = {}; |
|
29 bless $self, $pkg; |
|
30 my $bldInfDir1 = shift; |
|
31 my $bldInfDir2 = shift; |
|
32 Utils::AbsoluteFileName(\$bldInfDir1); |
|
33 Utils::AbsoluteFileName(\$bldInfDir2); |
|
34 $self->{verbose} = shift; |
|
35 $self->{compName} = shift; |
|
36 $self->{additionalHeaders} = shift; |
|
37 $self->{additionalIncludePaths} = shift; |
|
38 my $ignoreClasses = shift; |
|
39 foreach my $thisClass (@$ignoreClasses) { |
|
40 $self->{ignoreClasses}->{$thisClass} = 1; |
|
41 } |
|
42 $self->{ignoreR3Unused} = shift; |
|
43 $self->{bldInf1} = BldInf->New($bldInfDir1, $self->{verbose}); |
|
44 $self->{bldInf2} = BldInf->New($bldInfDir2, $self->{verbose}); |
|
45 return $self; |
|
46 } |
|
47 |
|
48 sub CheckAll { |
|
49 my $self = shift; |
|
50 my $passed = 1; |
|
51 unless ($self->CheckDefFiles()) { |
|
52 $passed = 0; |
|
53 } |
|
54 unless ($self->CheckClassSizes()) { |
|
55 $passed = 0; |
|
56 } |
|
57 unless ($self->CheckVTables()) { |
|
58 $passed = 0; |
|
59 } |
|
60 return $passed; |
|
61 } |
|
62 |
|
63 sub CheckDefFiles { |
|
64 my $self = shift; |
|
65 return $self->{bldInf1}->CheckDefFiles($self->{bldInf2}, $self->{ignoreR3Unused}); |
|
66 } |
|
67 |
|
68 sub CheckClassSizes { |
|
69 my $self = shift; |
|
70 my $classSizes1 = $self->GetClassSizes($self->{bldInf1}); |
|
71 my $classSizes2 = $self->GetClassSizes($self->{bldInf2}); |
|
72 return $classSizes1->Check($classSizes2); |
|
73 } |
|
74 |
|
75 sub CheckVTables { |
|
76 my $self = shift; |
|
77 my $vtable1 = $self->GetVTable($self->{bldInf1}); |
|
78 my $vtable2 = $self->GetVTable($self->{bldInf2}); |
|
79 return $vtable1->Check($vtable2); |
|
80 } |
|
81 |
|
82 |
|
83 # |
|
84 # Private. |
|
85 # |
|
86 |
|
87 sub GetClassSizes { |
|
88 my $self = shift; |
|
89 my $bldInf = shift; |
|
90 my $constructorsToCheck = $self->GetConstructorsToCheck($bldInf->ListConstructors()); |
|
91 my @headers; |
|
92 if ($self->{additionalHeaders}) { |
|
93 push (@headers, @{$self->{additionalHeaders}}); |
|
94 } |
|
95 foreach my $thisExport (@{$bldInf->ListExports()}) { |
|
96 if ($thisExport =~ /\.h$/i) { |
|
97 push (@headers, $thisExport); |
|
98 } |
|
99 } |
|
100 my $includes = $bldInf->ListIncludes(); |
|
101 if ($self->{additionalIncludePaths}) { |
|
102 push (@$includes, @{$self->{additionalIncludePaths}}); |
|
103 } |
|
104 return ClassSize->New($constructorsToCheck, \@headers, $includes, $self->{verbose}, $self->{compName}, $bldInf->{dir}); |
|
105 } |
|
106 |
|
107 sub GetVTable { |
|
108 my $self = shift; |
|
109 my $bldInf = shift; |
|
110 my $constructorsToCheck = $self->GetConstructorsToCheck($bldInf->ListConstructors()); |
|
111 return VTable->New($bldInf->{dir}, $constructorsToCheck, $self->{verbose}); |
|
112 } |
|
113 |
|
114 sub GetConstructorsToCheck { |
|
115 my $self = shift; |
|
116 my $constructors = shift; |
|
117 my @constructorsToCheck; |
|
118 foreach my $thisConstructor (@$constructors) { |
|
119 unless (exists $self->{ignoreClasses}->{$thisConstructor}) { |
|
120 push (@constructorsToCheck, $thisConstructor); |
|
121 } |
|
122 } |
|
123 return \@constructorsToCheck; |
|
124 } |
|
125 |
|
126 |
|
127 # |
|
128 # BldInf |
|
129 # |
|
130 |
|
131 package BldInf; |
|
132 |
|
133 |
|
134 # |
|
135 # Public. |
|
136 # |
|
137 |
|
138 sub New { |
|
139 my $pkg = shift; |
|
140 my $self = {}; |
|
141 bless $self, $pkg; |
|
142 $self->{dir} = shift; |
|
143 $self->{verbose} = shift; |
|
144 $self->Parse(); |
|
145 return $self; |
|
146 } |
|
147 |
|
148 sub CheckDefFiles { |
|
149 my $self = shift; |
|
150 my $other = shift; |
|
151 my $ignoreR3Unused = shift; |
|
152 my $passed = 1; |
|
153 foreach my $thisMmp (keys %{$self->{mmps}}) { |
|
154 if (exists $other->{mmps}->{$thisMmp}) { |
|
155 unless ($self->{mmps}->{$thisMmp}->CheckDefFile($other->{mmps}->{$thisMmp}, $ignoreR3Unused)) { |
|
156 $passed = 0; |
|
157 } |
|
158 } |
|
159 else { |
|
160 print "Mmp file \"$thisMmp\" missing for bld.inf \"$other->{dir}\"\n"; |
|
161 $passed = 0; |
|
162 } |
|
163 } |
|
164 return $passed; |
|
165 } |
|
166 |
|
167 sub ListConstructors { |
|
168 my $self = shift; |
|
169 my @constructors = (); |
|
170 foreach my $thisMmp (keys %{$self->{mmps}}) { |
|
171 push (@constructors, @{$self->{mmps}->{$thisMmp}->ListConstructors()}); |
|
172 } |
|
173 return \@constructors; |
|
174 } |
|
175 |
|
176 sub ListExports { |
|
177 my $self = shift; |
|
178 if (exists $self->{exports}) { |
|
179 return $self->{exports}; |
|
180 } |
|
181 return []; |
|
182 } |
|
183 |
|
184 sub ListIncludes { |
|
185 my $self = shift; |
|
186 my %includes = (); |
|
187 foreach my $thisMmp (keys %{$self->{mmps}}) { |
|
188 foreach my $thisInclude (@{$self->{mmps}->{$thisMmp}->ListIncludes()}) { |
|
189 $includes{$thisInclude} = 1; |
|
190 } |
|
191 } |
|
192 my @includes = keys %includes; |
|
193 return \@includes; |
|
194 } |
|
195 |
|
196 |
|
197 # |
|
198 # Private. |
|
199 # |
|
200 |
|
201 sub Parse { |
|
202 my $self = shift; |
|
203 if ($self->{verbose}) { print "Parsing $self->{dir}\\bld.inf...\n"; } |
|
204 Utils::PushDir($self->{dir}); |
|
205 my $fullName = "$self->{dir}\\bld.inf"; |
|
206 unless (open (BLDINF, "cpp -DARM -DMARM $fullName|")) { |
|
207 Utils::PopDir(); |
|
208 die "Error: Couldn't open \"cpp -DARM -DMARM $fullName\": $!\n"; |
|
209 } |
|
210 my $foundMmps = 0; |
|
211 my $foundExports = 0; |
|
212 my $doDie = 0; |
|
213 my $currentDir = $self->{dir}; |
|
214 while (my $line = <BLDINF>) { |
|
215 if ($line =~ /^# \d+ "(.*)" \d+?/) { |
|
216 my $newFile = $1; |
|
217 $newFile =~ s/\\\\/\\/g; |
|
218 $newFile =~ s/\\$//; |
|
219 Utils::AbsoluteFileName(\$newFile); |
|
220 ($currentDir) = Utils::SplitFileName($newFile); |
|
221 next; |
|
222 } |
|
223 if ($line =~ /^#/ or $line =~ /^\s*$/) { |
|
224 # Ignore lines starting with '#' or those filled with white space. |
|
225 next; |
|
226 } |
|
227 chomp $line; |
|
228 |
|
229 if ($line =~ /PRJ_MMPFILES/i) { |
|
230 $foundMmps = 1; |
|
231 $foundExports = 0; |
|
232 next; |
|
233 } |
|
234 elsif ($line =~ /PRJ_EXPORTS/i) { |
|
235 $foundMmps = 0; |
|
236 $foundExports = 1; |
|
237 next; |
|
238 } |
|
239 elsif ($line =~ /PRJ_/i) { |
|
240 $foundMmps = 0; |
|
241 $foundExports = 0; |
|
242 next; |
|
243 } |
|
244 if ($foundMmps) { |
|
245 if ($line =~ /makefile\s+(\S+)/i) { |
|
246 if ($self->{verbose}) { print "Info: \"makefile $1\" found in \"$self->{dir}\\bld.inf\", ignoring.\n"; } |
|
247 next; |
|
248 } |
|
249 |
|
250 $line =~ /\s*(\S+)/; |
|
251 my $mmpName = lc($1); |
|
252 if (not $mmpName =~ /\.mmp$/) { |
|
253 $mmpName .= '.mmp'; |
|
254 } |
|
255 unless (-e $mmpName) { |
|
256 if (-e "$currentDir\\$mmpName") { |
|
257 $mmpName = "$currentDir\\$mmpName"; |
|
258 } |
|
259 elsif (-e "$self->{dir}\\$mmpName") { |
|
260 $mmpName = "$self->{dir}\\$mmpName"; |
|
261 } |
|
262 else { |
|
263 print "Warning: Couldn't find location of \"$mmpName\n"; |
|
264 next; |
|
265 } |
|
266 } |
|
267 Utils::AbsoluteFileName(\$mmpName); |
|
268 (my $path, my $name, my $ext) = Utils::SplitFileName($mmpName); |
|
269 eval { |
|
270 $self->{mmps}->{lc("$name$ext")} = Mmp->New($mmpName, $self->{verbose}); |
|
271 }; |
|
272 if ($@) { |
|
273 $doDie = 1; |
|
274 print "$@"; |
|
275 } |
|
276 next; |
|
277 } |
|
278 elsif ($foundExports) { |
|
279 my $thisExport; |
|
280 if ($line =~ /^\s*\"([^\"]*)/) { |
|
281 $thisExport = $1; |
|
282 } |
|
283 elsif ($line =~ /\s*(\S+)/) { |
|
284 $thisExport = $1; |
|
285 } |
|
286 else { |
|
287 die; |
|
288 } |
|
289 unless (-e $thisExport) { |
|
290 if (-e "$currentDir\\$thisExport") { |
|
291 $thisExport = "$currentDir\\$thisExport"; |
|
292 } |
|
293 elsif (-e "$self->{dir}\\$thisExport") { |
|
294 $thisExport = "$self->{dir}\\$thisExport"; |
|
295 } |
|
296 else { |
|
297 print "Warning: Couldn't find location of \"$thisExport\n"; |
|
298 next; |
|
299 } |
|
300 } |
|
301 Utils::AbsoluteFileName(\$thisExport); |
|
302 push (@{$self->{exports}}, $thisExport); |
|
303 } |
|
304 } |
|
305 close (BLDINF); |
|
306 Utils::PopDir(); |
|
307 if ($doDie) { |
|
308 die "Aborting due to above errors\n"; |
|
309 } |
|
310 } |
|
311 |
|
312 |
|
313 # |
|
314 # Mmp |
|
315 # |
|
316 |
|
317 package Mmp; |
|
318 |
|
319 |
|
320 # |
|
321 # Public. |
|
322 # |
|
323 |
|
324 sub New { |
|
325 my $pkg = shift; |
|
326 my $self = {}; |
|
327 bless $self, $pkg; |
|
328 $self->{name} = shift; |
|
329 $self->{verbose} = shift; |
|
330 $self->Parse(); |
|
331 return $self; |
|
332 } |
|
333 |
|
334 sub CheckDefFile { |
|
335 my $self = shift; |
|
336 my $other = shift; |
|
337 my $ignoreR3Unused = shift; |
|
338 if ($self->{def}) { |
|
339 return $self->{def}->Check($other->{def}, $ignoreR3Unused); |
|
340 } |
|
341 return 1; |
|
342 } |
|
343 |
|
344 sub ListConstructors { |
|
345 my $self = shift; |
|
346 if ($self->{def}) { |
|
347 return $self->{def}->ListConstructors(); |
|
348 } |
|
349 return []; |
|
350 } |
|
351 |
|
352 sub ListIncludes { |
|
353 my $self = shift; |
|
354 if (exists $self->{includes}) { |
|
355 my @includes = keys %{$self->{includes}}; |
|
356 return \@includes; |
|
357 } |
|
358 return []; |
|
359 } |
|
360 |
|
361 |
|
362 # |
|
363 # Private. |
|
364 # |
|
365 |
|
366 sub Parse { |
|
367 my $self = shift; |
|
368 if ($self->{verbose}) { print "Parsing $self->{name}...\n"; } |
|
369 (my $path) = Utils::SplitFileName($self->{name}); |
|
370 $path =~ s/(.*)\\.*/$1/; # Extract path. |
|
371 Utils::PushDir($path); |
|
372 unless (open (MMP, "cpp -DARM -DMARM $self->{name}|")) { |
|
373 Utils::PopDir(); |
|
374 die "Error: Couldn't open \"cpp -DARM -DMARM $self->{name}\": $!\n"; |
|
375 } |
|
376 my $noStrictDef = 0; |
|
377 my $targetType = ''; |
|
378 while (my $line = <MMP>) { |
|
379 if ($line =~ /^#/ or $line =~ /^\s*$/) { |
|
380 # Ignore lines starting with '#' or those filled with white space. |
|
381 next; |
|
382 } |
|
383 chomp $line; |
|
384 if ($line =~ /^\s*targettype\s+(\S*)\s*$/i) { |
|
385 $targetType = $1; |
|
386 } |
|
387 elsif ($line =~ /^\s*deffile\s+(\S*)\s*$/i) { |
|
388 die if exists $self->{defFileName}; |
|
389 $self->{defFileName} = $1; |
|
390 } |
|
391 elsif ($line =~ /nostrictdef/i) { |
|
392 $noStrictDef = 1; |
|
393 } |
|
394 elsif ($line =~ /^\s*userinclude\s+(.+)/i) { |
|
395 my @userIncludes = split (/\s+/, $1); |
|
396 foreach my $thisUserInclude (@userIncludes) { |
|
397 $thisUserInclude =~ s/\+/$ENV{EPOCROOT}epoc32/; |
|
398 Utils::AbsoluteFileName(\$thisUserInclude); |
|
399 $self->{includes}->{lc($thisUserInclude)} = 1; |
|
400 } |
|
401 } |
|
402 elsif ($line =~ /^\s*systeminclude\s+(.+)/i) { |
|
403 my @systemIncludes = split (/\s+/, $1); |
|
404 foreach my $thisSystemInclude (@systemIncludes) { |
|
405 $thisSystemInclude =~ s/\+/$ENV{EPOCROOT}epoc32/; |
|
406 Utils::AbsoluteFileName(\$thisSystemInclude); |
|
407 $self->{includes}->{lc($thisSystemInclude)} = 1; |
|
408 } |
|
409 } |
|
410 } |
|
411 close (MMP); |
|
412 |
|
413 if ($targetType =~ /^(app|ani|ctl|ctpkg|epocexe|exe|exedll|fsy|kdll|kext|klib|ldd|lib|ecomiic|mda|mdl|notifier|opx|pdd|pdl|rdl|var|wlog)$/i) { |
|
414 # Don't bother looking for the deffile. |
|
415 Utils::PopDir(); |
|
416 return; |
|
417 } |
|
418 |
|
419 (my $mmpPath, my $mmpBase) = Utils::SplitFileName($self->{name}); |
|
420 if (exists $self->{defFileName}) { |
|
421 (my $path, my $base, my $ext) = Utils::SplitFileName($self->{defFileName}); |
|
422 if ($base eq '') { |
|
423 $base = $mmpBase; |
|
424 } |
|
425 if ($ext eq '') { |
|
426 $ext = '.def'; |
|
427 } |
|
428 if ($path eq '') { |
|
429 $path = $mmpPath; |
|
430 } |
|
431 unless ($noStrictDef) { |
|
432 $base .= 'u'; |
|
433 } |
|
434 unless (-e "$path$base$ext") { |
|
435 $path = "$path..\\bmarm\\"; |
|
436 } |
|
437 unless (-e "$path$base$ext") { |
|
438 $path = $mmpPath . $path; |
|
439 } |
|
440 $self->{defFileName} = "$path$base$ext"; |
|
441 Utils::AbsoluteFileName(\$self->{defFileName}); |
|
442 } |
|
443 else { |
|
444 # Assume default. |
|
445 $self->{defFileName} = $mmpBase; |
|
446 unless ($noStrictDef) { |
|
447 $self->{defFileName} .= 'u'; |
|
448 } |
|
449 $self->{defFileName} .= '.def'; |
|
450 $self->AddDefaultDefFilePath(); |
|
451 } |
|
452 |
|
453 if ($self->{defFileName}) { |
|
454 $self->{def} = Def->New($self->{defFileName}, $self->{verbose}); |
|
455 } |
|
456 |
|
457 Utils::PopDir(); |
|
458 } |
|
459 |
|
460 sub AddDefaultDefFilePath { |
|
461 my $self = shift; |
|
462 (my $path) = Utils::SplitFileName($self->{name}); |
|
463 $self->{defFileName} = "$path\\..\\bmarm\\$self->{defFileName}"; |
|
464 if (-e $self->{defFileName}) { |
|
465 Utils::AbsoluteFileName(\$self->{defFileName}); |
|
466 } |
|
467 else { |
|
468 print "Warning: Unable to find def file in \"$self->{name}\"\n"; |
|
469 delete $self->{defFileName}; |
|
470 } |
|
471 } |
|
472 |
|
473 |
|
474 # |
|
475 # Def |
|
476 # |
|
477 |
|
478 package Def; |
|
479 |
|
480 |
|
481 # |
|
482 # Public. |
|
483 # |
|
484 |
|
485 sub New { |
|
486 my $pkg = shift; |
|
487 my $self = {}; |
|
488 bless $self, $pkg; |
|
489 $self->{name} = shift; |
|
490 $self->{verbose} = shift; |
|
491 $self->Parse(); |
|
492 $self->DemangleNames(); |
|
493 return $self; |
|
494 } |
|
495 |
|
496 sub Check { |
|
497 my $self = shift; |
|
498 my $other = shift; |
|
499 my $ignoreR3Unused = shift; |
|
500 if ($self->{verbose}) { print "Checking DEF file \"$self->{name}\" against \"$other->{name}\"...\n"; } |
|
501 my $passed = 1; |
|
502 if (exists $self->{data}) { |
|
503 for (my $ii = 0; $ii < scalar(@{$self->{data}}); ++$ii) { |
|
504 my $ordinal = $ii + 1; |
|
505 if ($ii >= scalar @{$other->{data}}) { |
|
506 print "Failure reason: \"$self->{name}\" has more exports than \"$other->{name}\"\n"; |
|
507 $passed = 0; |
|
508 last; |
|
509 } |
|
510 my $selfRaw = $self->{data}->[$ii]->{raw}; |
|
511 my $otherRaw = $other->{data}->[$ii]->{raw}; |
|
512 if ($ignoreR3Unused) { |
|
513 $selfRaw =~ s/R3UNUSED //; |
|
514 $otherRaw =~ s/R3UNUSED //; |
|
515 } |
|
516 unless ($selfRaw eq $otherRaw) { |
|
517 $passed = 0; |
|
518 print "Failure reason: Def file mismatch between \"$self->{name}\" and \"$other->{name}\" at $ordinal\n"; |
|
519 if ($self->{verbose}) { |
|
520 print "\t$self->{data}->[$ii]->{raw}\n\t$other->{data}->[$ii]->{raw}\n"; |
|
521 } |
|
522 } |
|
523 } |
|
524 } |
|
525 return $passed; |
|
526 } |
|
527 |
|
528 sub ListConstructors { |
|
529 my $self = shift; |
|
530 my @constructors = (); |
|
531 if (exists $self->{data}) { |
|
532 my $ordinal = 0; |
|
533 foreach my $thisEntry (@{$self->{data}}) { |
|
534 $ordinal++; |
|
535 die unless (exists $thisEntry->{function}); |
|
536 if ($thisEntry->{function} =~ /(.+)::(.+)\(/) { |
|
537 if ($1 eq $2) { |
|
538 push (@constructors, $1); |
|
539 } |
|
540 } |
|
541 } |
|
542 } |
|
543 return \@constructors; |
|
544 } |
|
545 |
|
546 |
|
547 # |
|
548 # Private. |
|
549 # |
|
550 |
|
551 sub Parse { |
|
552 my $self = shift; |
|
553 open (DEF, $self->{name}) or die "Error: Couldn't open \"$self->{name}\" for reading: $!\n"; |
|
554 my $lineNum = 0; |
|
555 while (my $thisLine = <DEF>) { |
|
556 ++$lineNum; |
|
557 chomp $thisLine; |
|
558 if ($thisLine =~ /^(EXPORTS|;|\s*$)/) { |
|
559 next; |
|
560 } |
|
561 my $entry = {}; |
|
562 $entry->{raw} = $thisLine; |
|
563 |
|
564 push (@{$self->{data}}, $entry); |
|
565 } |
|
566 close (DEF); |
|
567 } |
|
568 |
|
569 sub DemangleNames { |
|
570 my $self = shift; |
|
571 open (FILT, "type $self->{name} | c++filt |") or die "Error: Couldn't open \"type $self->{name} | c++filt |\": $!\n"; |
|
572 my $lineNum = 0; |
|
573 while (my $line = <FILT>) { |
|
574 ++$lineNum; |
|
575 chomp $line; |
|
576 next if ($line =~ /^(EXPORT|;|\s*$)/); |
|
577 if ($line =~ /^\s+(\"(.+)\"|(.+)) @ (\d+)/) { |
|
578 my $function; |
|
579 if ($2) { |
|
580 $function = $2; |
|
581 } |
|
582 else { |
|
583 die unless $3; |
|
584 $function = $3; |
|
585 } |
|
586 my $ordinal = $4; |
|
587 $self->{data}->[$ordinal - 1]->{function} = $function; |
|
588 } |
|
589 else { |
|
590 die "Error: Unable to parse c++filt output for \"$self->{name}\" at line $lineNum\n"; |
|
591 } |
|
592 } |
|
593 close (FILT); |
|
594 } |
|
595 |
|
596 |
|
597 # |
|
598 # ClassSize |
|
599 # |
|
600 |
|
601 package ClassSize; |
|
602 |
|
603 |
|
604 # |
|
605 # Public. |
|
606 # |
|
607 |
|
608 sub New { |
|
609 my $pkg = shift; |
|
610 my $self = {}; |
|
611 bless $self, $pkg; |
|
612 $self->{classes} = shift; |
|
613 $self->{headers} = shift; |
|
614 $self->{includes} = shift; |
|
615 $self->{verbose} = shift; |
|
616 $self->{compName} = shift; |
|
617 $self->{bldInfDir} = shift; |
|
618 if (scalar @{$self->{classes}} > 0) { |
|
619 $self->GetClassSizes(); |
|
620 } |
|
621 return $self; |
|
622 } |
|
623 |
|
624 sub Check { |
|
625 my $self = shift; |
|
626 my $other = shift; |
|
627 if ($self->{verbose}) { print "Comparing class sizes of \"$self->{bldInfDir}\" against \"$other->{bldInfDir}\"..\n"; } |
|
628 my $passed = 1; |
|
629 foreach my $thisClass (keys %{$self->{classSizes}}) { |
|
630 if ($self->{verbose}) { print "Examining class sizes of \"$thisClass\"...\n"; } |
|
631 unless (exists $other->{classSizes}->{$thisClass}) { |
|
632 print "Failure reason: \"$thisClass\" not found (possibly renamed)\n"; |
|
633 $passed = 0; |
|
634 next; |
|
635 } |
|
636 unless ($self->{classSizes}->{$thisClass} == $other->{classSizes}->{$thisClass}) { |
|
637 $passed = 0; |
|
638 print "Failure reason: Class \"$thisClass\" has changed size from $self->{classSizes}->{$thisClass} to $other->{classSizes}->{$thisClass}\n"; |
|
639 } |
|
640 } |
|
641 return $passed; |
|
642 } |
|
643 |
|
644 |
|
645 # |
|
646 # Private. |
|
647 # |
|
648 |
|
649 sub GetClassSizes { |
|
650 my $self = shift; |
|
651 eval { |
|
652 $self->GenerateCode(); |
|
653 $self->CompileCode(); |
|
654 $self->GetOutput(); |
|
655 }; |
|
656 $self->CleanUp(); |
|
657 if ($@) { |
|
658 die $@; |
|
659 } |
|
660 } |
|
661 |
|
662 sub GenerateCode { |
|
663 my $self = shift; |
|
664 open (CODE, '>__ClassSize.cpp') or die "Error: Couldn't open \"__ClassSize.cpp\" for writing: $!\n"; |
|
665 print CODE "#include <stdio.h>\n"; |
|
666 print CODE "#include <e32std.h>\n"; |
|
667 print CODE "#include <e32def.h>\n"; |
|
668 print CODE "#include <e32base.h>\n"; |
|
669 foreach my $thisHeader (@{$self->{headers}}) { |
|
670 print CODE "#include <$thisHeader>\n"; |
|
671 } |
|
672 print CODE "int main(int argc, char* argv[]) {\n"; |
|
673 foreach my $thisClass (@{$self->{classes}}) { |
|
674 print CODE "\tprintf(\"$thisClass\\t%d\\n\", sizeof($thisClass));\n"; |
|
675 } |
|
676 print CODE "\treturn 0; }\n"; |
|
677 close (CODE); |
|
678 } |
|
679 |
|
680 sub CompileCode { |
|
681 my $self = shift; |
|
682 my $command = 'cl '; |
|
683 foreach my $thisInclude (@{$self->{includes}}) { |
|
684 $command .= " /I$thisInclude"; |
|
685 } |
|
686 $command .= " /D__VC32__ /D__WINS__ /D__SYMBIAN32__ /DWIN32 /D_WINDOWS /D_UNICODE __ClassSize.cpp"; |
|
687 unless ($self->{verbose}) { |
|
688 $command .= ' /nologo 2>&1 > NUL'; |
|
689 } |
|
690 if (system ($command)) { |
|
691 if (exists $self->{compName} and $self->{compName}) { |
|
692 rename ("__ClassSize.cpp", "$self->{compName}.cpp"); |
|
693 } |
|
694 else { |
|
695 rename ("__ClassSize.cpp", "unknown.cpp"); |
|
696 } |
|
697 die "Error: Problem executing \"$command\"\n"; |
|
698 } |
|
699 } |
|
700 |
|
701 sub GetOutput { |
|
702 my $self = shift; |
|
703 open (OUTPUT, '__ClassSize.exe|') or die "Error: Couldn't run \"__ClassSize.exe\": $!\n"; |
|
704 while (my $thisLine = <OUTPUT>) { |
|
705 chomp $thisLine; |
|
706 next if ($thisLine =~ /^\s*$/); |
|
707 if ($thisLine =~ /^(\S+)\t(\d+)$/) { |
|
708 $self->{classSizes}->{$1} = $2; |
|
709 } |
|
710 else { |
|
711 die "Error: Problem parsing output of \"__ClassSize.exe\"\n"; |
|
712 } |
|
713 } |
|
714 close (OUTPUT); |
|
715 } |
|
716 |
|
717 sub CleanUp { |
|
718 my $self = shift; |
|
719 DeleteFile('__ClassSize.cpp'); |
|
720 DeleteFile('__ClassSize.obj'); |
|
721 DeleteFile('__ClassSize.exe'); |
|
722 } |
|
723 |
|
724 sub DeleteFile { |
|
725 my $file = shift; |
|
726 if (-e $file) { |
|
727 unlink ($file) or die "Error: Couldn't delete \"$file\"\n"; |
|
728 } |
|
729 } |
|
730 |
|
731 |
|
732 # |
|
733 # VTable |
|
734 # |
|
735 |
|
736 package VTable; |
|
737 |
|
738 |
|
739 # |
|
740 # Public. |
|
741 # |
|
742 |
|
743 sub New { |
|
744 my $pkg = shift; |
|
745 my $self = {}; |
|
746 bless $self, $pkg; |
|
747 $self->{bldInfDir} = shift; |
|
748 my $classes = shift; |
|
749 foreach my $class (@$classes) { |
|
750 $self->{classes}->{$class} = 1; |
|
751 } |
|
752 $self->{verbose} = shift; |
|
753 |
|
754 Utils::PushDir($self->{bldInfDir}); |
|
755 eval { |
|
756 $self->BuildAssemblerListings(); |
|
757 $self->ParseAssemblerListings(); |
|
758 $self->DeleteAssemblerListings(); |
|
759 }; |
|
760 Utils::PopDir(); |
|
761 if ($@) { |
|
762 die $@; |
|
763 } |
|
764 return $self; |
|
765 } |
|
766 |
|
767 sub Check { |
|
768 my $self = shift; |
|
769 my $other = shift; |
|
770 if ($self->{verbose}) { print "Comparing vtable layout of \"$self->{bldInfDir}\" against \"$other->{bldInfDir}\"..\n"; } |
|
771 my $passed = 1; |
|
772 foreach my $class (keys %{$self->{vtables}}) { |
|
773 if (exists $other->{vtables}->{$class}) { |
|
774 if ($self->{verbose}) { print "Examining vtable of class \"$class\"...\n"; } |
|
775 for (my $ii = 0; $ii < scalar (@{$self->{vtables}->{$class}}); ++$ii) { |
|
776 my $thisVTableEntry = $self->{vtables}->{$class}->[$ii]; |
|
777 if ($ii >= scalar (@{$other->{vtables}->{$class}})) { |
|
778 print "Failure reason: Unexpected vtable entry \"$thisVTableEntry\"\n"; |
|
779 $passed = 0; |
|
780 last; |
|
781 } |
|
782 my $otherVTableEntry = $other->{vtables}->{$class}->[$ii]; |
|
783 if ($thisVTableEntry eq $otherVTableEntry) { |
|
784 if ($self->{verbose}) { print "\tMatched vtable entry \"$thisVTableEntry\"\n"; } |
|
785 } |
|
786 else { |
|
787 print "Failure reason: Mismatched vtable entries in class \"$class\"\n\t$thisVTableEntry\n\t$otherVTableEntry\n"; |
|
788 $passed = 0; |
|
789 } |
|
790 } |
|
791 } |
|
792 else { |
|
793 print "Failure reason: Vtable for \"$class\" missing from $other->{bldInfDir}\n"; |
|
794 $passed = 0; |
|
795 } |
|
796 } |
|
797 return $passed; |
|
798 } |
|
799 |
|
800 |
|
801 |
|
802 # |
|
803 # Private. |
|
804 # |
|
805 |
|
806 sub BuildAssemblerListings { |
|
807 my $self = shift; |
|
808 if ($self->{verbose}) { print "Calling \"bldmake bldfiles\" in \"$self->{bldInfDir}\"\n"; } |
|
809 open (BLDMAKE, "bldmake bldfiles 2>&1 |") or die "Error: Couldn't run \"bldmake bldfiles\" in \"$self->{bldInfDir}\": $!\n"; |
|
810 while (my $line = <BLDMAKE>) { |
|
811 if ($line) { |
|
812 if ($self->{verbose}) { print "\t$line"; } |
|
813 die "Error: Problem running \"bldmake bldfiles\" in \"$self->{bldInfDir}\"\n"; |
|
814 } |
|
815 } |
|
816 close (BLDMAKE); |
|
817 |
|
818 if ($self->{verbose}) { print "Calling \"abld makefile arm4\" in \"$self->{bldInfDir}\"\n"; } |
|
819 open (ABLD, "abld makefile arm4 2>&1 |") or die "Error: Couldn't run \"abld makefile arm4\" in \"$self->{bldInfDir}\": $!\n"; |
|
820 while (my $line = <ABLD>) { |
|
821 if ($line) { |
|
822 if ($self->{verbose}) { print "\t$line"; } |
|
823 } |
|
824 } |
|
825 close (ABLD); |
|
826 |
|
827 if ($self->{verbose}) { print "Calling \"abld listing arm4 urel\" in \"$self->{bldInfDir}\"\n"; } |
|
828 open (ABLD, "abld listing arm4 urel 2>&1 |") or die "Error: Couldn't run \"abld listing arm4 urel\" in \"$self->{bldInfDir}\": $!\n"; |
|
829 while (my $line = <ABLD>) { |
|
830 if ($line) { |
|
831 if ($self->{verbose}) { print "\t$line"; } |
|
832 if ($line =~ /^Created (.*)/) { |
|
833 my $listingFile = $1; |
|
834 push (@{$self->{listingFiles}}, $listingFile); |
|
835 } |
|
836 } |
|
837 } |
|
838 close (ABLD); |
|
839 } |
|
840 |
|
841 sub ParseAssemblerListings { |
|
842 my $self = shift; |
|
843 foreach my $listing (@{$self->{listingFiles}}) { |
|
844 open (LISTING, $listing) or die "Error: Couldn't open \"$listing\" for reading: $!\n"; |
|
845 while (my $line = <LISTING>) { |
|
846 if ($line =~ /^\s.\d+\s+__vt_\d+(\D+):$/) { # If start of vtable section. |
|
847 my $class = $1; |
|
848 if (exists $self->{classes}->{$class}) { # If one of the classes we're interested in. |
|
849 while (my $line2 = <LISTING>) { |
|
850 if ($line2 =~ /^\s.\d+\s[\da-fA-F]{4}\s[\da-fA-F]{8}\s+\.word\s+(.*)/) { # If this is a valid vtable entry. |
|
851 my $vtableEntry = $1; |
|
852 push (@{$self->{vtables}->{$class}}, $vtableEntry); |
|
853 } |
|
854 else { |
|
855 last; |
|
856 } |
|
857 } |
|
858 } |
|
859 } |
|
860 } |
|
861 close (LISTING); |
|
862 } |
|
863 } |
|
864 |
|
865 sub DeleteAssemblerListings { |
|
866 my $self = shift; |
|
867 foreach my $listing (@{$self->{listingFiles}}) { |
|
868 unlink $listing or die "Error: Unable to delete \"$listing\": $!\n"; |
|
869 } |
|
870 } |
|
871 |
|
872 |
|
873 # |
|
874 # Utils. |
|
875 # |
|
876 |
|
877 package Utils; |
|
878 |
|
879 use File::Basename; |
|
880 use Cwd 'abs_path', 'cwd'; |
|
881 use Win32; |
|
882 |
|
883 sub AbsoluteFileName { |
|
884 my $fileName = shift; |
|
885 unless (-e $$fileName) { |
|
886 die "Error: \"$$fileName\" does not exist\n"; |
|
887 } |
|
888 (my $base, my $path) = fileparse($$fileName); |
|
889 my $absPath = abs_path($path); |
|
890 $$fileName = $absPath; |
|
891 unless ($$fileName =~ /[\\\/]$/) { |
|
892 $$fileName .= "\\"; |
|
893 } |
|
894 $$fileName .= $base; |
|
895 TidyFileName($fileName); |
|
896 } |
|
897 |
|
898 sub SplitFileName { |
|
899 my $fileName = shift; |
|
900 my $path = ''; |
|
901 my $base = ''; |
|
902 my $ext = ''; |
|
903 |
|
904 if ($fileName =~ /\\?([^\\]*?)(\.[^\\\.]*)?$/) { |
|
905 $base = $1; |
|
906 } |
|
907 if ($fileName =~ /^(.*\\)/) { |
|
908 $path = $1; |
|
909 } |
|
910 if ($fileName =~ /(\.[^\\\.]*)$/o) { |
|
911 $ext = $1; |
|
912 } |
|
913 |
|
914 die unless ($fileName eq "$path$base$ext"); |
|
915 return ($path, $base, $ext); |
|
916 } |
|
917 |
|
918 sub TidyFileName { |
|
919 my $a = shift; |
|
920 $$a =~ s/\//\\/g; # Change forward slashes to back slashes. |
|
921 $$a =~ s/\\\.\\/\\/g; # Change "\.\" into "\". |
|
922 |
|
923 if ($$a =~ /^\\\\/) { # Test for UNC paths. |
|
924 $$a =~ s/\\\\/\\/g; # Change "\\" into "\". |
|
925 $$a =~ s/^\\/\\\\/; # Add back a "\\" at the start so that it remains a UNC path. |
|
926 } |
|
927 else { |
|
928 $$a =~ s/\\\\/\\/g; # Change "\\" into "\". |
|
929 } |
|
930 } |
|
931 |
|
932 my @dirStack; |
|
933 |
|
934 sub PushDir { |
|
935 my $dir = shift; |
|
936 my $cwd = cwd(); |
|
937 chdir ($dir) or die "Error: Couldn't change working directory to \"$dir\": $!\n"; |
|
938 push (@dirStack, $cwd); |
|
939 } |
|
940 |
|
941 sub PopDir { |
|
942 if (scalar @dirStack > 0) { |
|
943 my $dir = pop @dirStack; |
|
944 chdir ($dir) or die "Error: Couldn't change working directory to \"$dir\": $!\n"; |
|
945 } |
|
946 else { |
|
947 die "Error: Directory stack empty"; |
|
948 } |
|
949 } |
|
950 |
|
951 |
|
952 1; |
|
953 |
|
954 =head1 NAME |
|
955 |
|
956 CheckBc.pm - A module that runs some simple tests to see if one component source tree is backwards compatible another. |
|
957 |
|
958 =head1 SYNOPSIS |
|
959 |
|
960 my $checkBc = CheckBc->New('\branch1\comp\group', '\branch2\comp\group', 0); |
|
961 unless ($checkBc->CheckAll()) { |
|
962 print "Check failed\n"; |
|
963 } |
|
964 |
|
965 =head1 DESCRIPTION |
|
966 |
|
967 C<CheckBc> does the following checks to see if a backwards compatibility breaking change has been introduced: |
|
968 |
|
969 =over 4 |
|
970 |
|
971 =item 1 |
|
972 |
|
973 Compares the ARM F<.def> files to ensure that only new lines have been added to the end of the file. |
|
974 |
|
975 =item 2 |
|
976 |
|
977 Compares the sizes of any classes that have an exported C++ constructor. This is done by compiling some generated C++ code that uses the C<sizeof> operator to print the relevant class sizes to C<STDOUT>. Compilation is done using the MSVC++ compiler. |
|
978 |
|
979 =item 3 |
|
980 |
|
981 Compares the v-table layouts of any classes that have an exported C++ constructor. This is done by compiling each source code set to ARM4 assembler listings, comparing the v-table sections. |
|
982 |
|
983 =back |
|
984 |
|
985 =head1 LIMITATIONS |
|
986 |
|
987 =over 4 |
|
988 |
|
989 =item 1 |
|
990 |
|
991 The component's headers must compile using Microsoft's Visual C++ compiler. |
|
992 |
|
993 =item 2 |
|
994 |
|
995 The component's exported headers must compile when they are all #include'd into a single F<.cpp> file. If this is not the case, then additional headers and include paths can be passed into the constructor. |
|
996 |
|
997 =item 3 |
|
998 |
|
999 Declarations of the component's exported C++ constructors must be found in one of the exported headers. |
|
1000 |
|
1001 =item 4 |
|
1002 |
|
1003 F<.def> file lines are expected to be identical. This can lead to checks failing falsely because, for example, the name of a function may be changed without breaking BC provided the F<.def> file is carefully edited. |
|
1004 |
|
1005 =item 5 |
|
1006 |
|
1007 The components must compile as ARM4. This is likely to mean that each set of source code needs to be accompanied with a suitable F<\epoc32> tree that allows it to be built. The simplest way to acheive this is to prepare a pair of subst'd drives. |
|
1008 |
|
1009 =back |
|
1010 |
|
1011 =head1 KNOWN BUGS |
|
1012 |
|
1013 F<bld.inf>, F<.mmp> and F<.def> file parsing is probably not as industrial strength as it should be. |
|
1014 |
|
1015 =head1 COPYRIGHT |
|
1016 |
|
1017 Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
1018 All rights reserved. |
|
1019 This component and the accompanying materials are made available |
|
1020 under the terms of the License "Eclipse Public License v1.0" |
|
1021 which accompanies this distribution, and is available |
|
1022 at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
1023 |
|
1024 Initial Contributors: |
|
1025 Nokia Corporation - initial contribution. |
|
1026 |
|
1027 Contributors: |
|
1028 |
|
1029 Description: |
|
1030 |
|
1031 |
|
1032 =cut |