1########################################################################### 2# A module to compare operating systems 3# 4# Copyright (C) 2009-2011 Institute for System Programming, RAS 5# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) 6# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory 7# 8# Written by Andrey Ponomarenko 9# 10# This library is free software; you can redistribute it and/or 11# modify it under the terms of the GNU Lesser General Public 12# License as published by the Free Software Foundation; either 13# version 2.1 of the License, or (at your option) any later version. 14# 15# This library is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18# Lesser General Public License for more details. 19# 20# You should have received a copy of the GNU Lesser General Public 21# License along with this library; if not, write to the Free Software 22# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 23# MA 02110-1301 USA 24########################################################################### 25use strict; 26use File::Temp qw(tempdir); 27use Cwd qw(abs_path cwd); 28 29loadModule("ElfTools"); 30loadModule("ABIDump"); 31 32my %SysDesc; 33my %NonPrefix; 34 35sub cmpSystems($$) 36{ # -cmp-systems option handler 37 # should be used with -d1 and -d2 options 38 my ($SPath1, $SPath2) = @_; 39 40 if(not $SPath1) { 41 exitStatus("Error", "the option -d1 should be specified"); 42 } 43 elsif(not -d $SPath1) { 44 exitStatus("Access_Error", "can't access directory \'".$SPath1."\'"); 45 } 46 elsif(not -d $SPath1."/abi_dumps") { 47 exitStatus("Access_Error", "can't access directory \'".$SPath1."/abi_dumps\'"); 48 } 49 if(not $SPath2) { 50 exitStatus("Error", "the option -d2 should be specified"); 51 } 52 elsif(not -d $SPath2) { 53 exitStatus("Access_Error", "can't access directory \'".$SPath2."\'"); 54 } 55 elsif(not -d $SPath2."/abi_dumps") { 56 exitStatus("Access_Error", "can't access directory \'".$SPath2."/abi_dumps\'"); 57 } 58 # sys_dumps/<System>/<Arch>/... 59 my $SystemName1 = getFilename(getDirname($SPath1)); 60 my $SystemName2 = getFilename(getDirname($SPath2)); 61 62 my $SystemName1_P = $SystemName1; 63 my $SystemName2_P = $SystemName2; 64 65 $SystemName1=~s/_/ /g; 66 $SystemName2=~s/_/ /g; 67 68 # sys_dumps/<System>/<Arch>/... 69 my $ArchName = getFilename($SPath1); 70 if($ArchName ne getFilename($SPath2)) { 71 exitStatus("Error", "can't compare systems of different CPU architecture"); 72 } 73 74 my $TmpDir = $In::Opt{"Tmp"}; 75 76 if(my $OStarget_Dump = readFile($SPath1."/target.txt")) 77 { # change target 78 setTarget($OStarget_Dump); 79 } 80 81 my $GroupByHeaders = 0; 82 if(my $Mode = readFile($SPath1."/mode.txt")) 83 { # change mode 84 if($Mode eq "headers-only") 85 { # -headers-only mode 86 $In::Opt{"CheckHeadersOnly"} = 1; 87 $GroupByHeaders = 1; 88 } 89 if($Mode eq "group-by-headers") { 90 $GroupByHeaders = 1; 91 } 92 } 93 my $SYS_REPORT_PATH = "sys_compat_reports/".$SystemName1_P."_to_".$SystemName2_P."/$ArchName"; 94 rmtree($SYS_REPORT_PATH); 95 my (%LibSoname1, %LibSoname2) = (); 96 foreach (split(/\n/, readFile($SPath1."/sonames.txt"))) 97 { 98 if(my ($LFName, $Soname) = split(/;/, $_)) 99 { 100 if($In::Opt{"Target"} eq "symbian") { 101 $Soname=~s/\{.+\}//; 102 } 103 $LibSoname1{$LFName} = $Soname; 104 } 105 } 106 foreach (split(/\n/, readFile($SPath2."/sonames.txt"))) 107 { 108 if(my ($LFName, $Soname) = split(/;/, $_)) 109 { 110 if($In::Opt{"Target"} eq "symbian") { 111 $Soname=~s/\{.+\}//; 112 } 113 $LibSoname2{$LFName} = $Soname; 114 } 115 } 116 my (%LibV1, %LibV2) = (); 117 foreach (split(/\n/, readFile($SPath1."/versions.txt"))) 118 { 119 if(my ($LFName, $V) = split(/;/, $_)) { 120 $LibV1{$LFName} = $V; 121 } 122 } 123 foreach (split(/\n/, readFile($SPath2."/versions.txt"))) 124 { 125 if(my ($LFName, $V) = split(/;/, $_)) { 126 $LibV2{$LFName} = $V; 127 } 128 } 129 my @Dumps1 = cmdFind($SPath1."/abi_dumps","f","*.abi",1); 130 my @Dumps2 = cmdFind($SPath2."/abi_dumps","f","*.abi",1); 131 132 my (%LibVers1, %LibVers2) = (); 133 my (%ShortNames1, %ShortNames2) = (); 134 foreach my $DPath (@Dumps1) 135 { 136 if(my $Name = isDump($DPath)) 137 { 138 my ($Soname, $V) = ($LibSoname1{$Name}, $LibV1{$Name}); 139 if(not $V) { 140 $V = libPart($Name, "version"); 141 } 142 if($GroupByHeaders) { 143 $Soname = $Name; 144 } 145 $LibVers1{$Soname}{$V} = $DPath; 146 $ShortNames1{libPart($Soname, "short")}{$Soname} = 1; 147 } 148 } 149 foreach my $DPath (@Dumps2) 150 { 151 if(my $Name = isDump($DPath)) 152 { 153 my ($Soname, $V) = ($LibSoname2{$Name}, $LibV2{$Name}); 154 if(not $V) { 155 $V = libPart($Name, "version"); 156 } 157 if($GroupByHeaders) { 158 $Soname = $Name; 159 } 160 $LibVers2{$Soname}{$V} = $DPath; 161 $ShortNames2{libPart($Soname, "short")}{$Soname} = 1; 162 } 163 } 164 my (%Added, %Removed) = (); 165 my (%ChangedSoname, %TestResults) = (); 166 my (%AddedShort, %RemovedShort) = (); 167 if(not $GroupByHeaders) 168 { 169 my %ChangedSoname_Safe = (); 170 foreach my $LName (sort keys(%LibSoname2)) 171 { # libcurl.so.3 -> libcurl.so.4 (search for SONAME by the file name) 172 # OS #1 => OS #2 173 if(defined $LibVers2{$LName}) 174 { # already registered 175 next; 176 } 177 my $Soname = $LibSoname2{$LName}; 178 if(defined $LibVers2{$Soname} 179 and defined $LibVers1{$LName}) 180 { 181 $LibVers2{$LName} = $LibVers2{$Soname}; 182 $ChangedSoname_Safe{$Soname}=$LName; 183 } 184 } 185 foreach my $LName (sort keys(%LibSoname1)) 186 { # libcurl.so.3 -> libcurl.so.4 (search for SONAME by the file name) 187 # OS #1 <= OS #2 188 if(defined $LibVers1{$LName}) 189 { # already registered 190 next; 191 } 192 my $Soname = $LibSoname1{$LName}; 193 if(defined $LibVers1{$Soname} 194 and defined $LibVers2{$LName}) { 195 $LibVers1{$LName} = $LibVers1{$Soname}; 196 } 197 } 198 if(not $GroupByHeaders) { 199 printMsg("INFO", "Checking added/removed libs"); 200 } 201 foreach my $LName (sort {lc($a) cmp lc($b)} keys(%LibVers1)) 202 { # removed libs 203 if(not isTargetLib($LName)) { 204 next; 205 } 206 if(not defined $LibVers1{$LName}) { 207 next; 208 } 209 my @Versions1 = keys(%{$LibVers1{$LName}}); 210 if($#Versions1>=1) 211 { # should be only one version 212 next; 213 } 214 if(not defined $LibVers2{$LName} 215 or not keys(%{$LibVers2{$LName}})) 216 { # removed library 217 if(not $LibSoname2{$LName}) 218 { 219 my $LSName = libPart($LName, "short"); 220 $RemovedShort{$LSName}{$LName} = 1; 221 my $V = $Versions1[0]; 222 $Removed{$LName}{"version"} = $V; 223 224 my $ListPath = "info/$LName/symbols.html"; 225 my $FV = $SystemName1; 226 if($V) { 227 $FV = $V."-".$FV; 228 } 229 createSymbolsList($LibVers1{$LName}{$V}, 230 $SYS_REPORT_PATH."/".$ListPath, $LName, $FV, $ArchName); 231 $Removed{$LName}{"list"} = $ListPath; 232 } 233 } 234 } 235 foreach my $LName (sort {lc($a) cmp lc($b)} keys(%LibVers2)) 236 { # added libs 237 if(not isTargetLib($LName)) { 238 next; 239 } 240 if(not defined $LibVers2{$LName}) { 241 next; 242 } 243 my @Versions2 = keys(%{$LibVers2{$LName}}); 244 if($#Versions2>=1) 245 { # should be only one version 246 next; 247 } 248 if($ChangedSoname_Safe{$LName}) 249 { # changed soname but added the symbolic link for old-version library 250 next; 251 } 252 if(not defined $LibVers1{$LName} 253 or not keys(%{$LibVers1{$LName}})) 254 { # added library 255 if(not $LibSoname1{$LName}) 256 { 257 my $LSName = libPart($LName, "short"); 258 $AddedShort{$LSName}{$LName} = 1; 259 my $V = $Versions2[0]; 260 $Added{$LName}{"version"} = $V; 261 262 my $ListPath = "info/$LName/symbols.html"; 263 my $FV = $SystemName2; 264 if($V) { 265 $FV = $V."-".$FV; 266 } 267 createSymbolsList($LibVers2{$LName}{$V}, 268 $SYS_REPORT_PATH."/".$ListPath, $LName, $FV, $ArchName); 269 $Added{$LName}{"list"} = $ListPath; 270 } 271 } 272 } 273 foreach my $LSName (keys(%AddedShort)) 274 { # changed SONAME 275 my @AddedSonames = sort keys(%{$AddedShort{$LSName}}); 276 next if($#AddedSonames!=0); 277 278 if(defined $RemovedShort{$LSName}) 279 { # removed old soname 280 my @RemovedSonames = sort keys(%{$RemovedShort{$LSName}}); 281 $ChangedSoname{$AddedSonames[0]} = $RemovedSonames[0]; 282 $ChangedSoname{$RemovedSonames[0]} = $AddedSonames[0]; 283 } 284 elsif(defined $ShortNames1{$LSName}) 285 { # saved old soname 286 my @Sonames = sort keys(%{$ShortNames1{$LSName}}); 287 $ChangedSoname{$AddedSonames[0]} = $Sonames[0]; 288 $ChangedSoname{$Sonames[0]} = $AddedSonames[0]; 289 } 290 } 291 } 292 293 my %SONAME_Changed = (); 294 my %SONAME_Added = (); 295 296 foreach my $LName (sort {lc($a) cmp lc($b)} keys(%LibVers1)) 297 { 298 if(not isTargetLib($LName)) { 299 next; 300 } 301 my @Versions1 = keys(%{$LibVers1{$LName}}); 302 if(not @Versions1 or $#Versions1>=1) 303 { # should be only one version 304 next; 305 } 306 my $LV1 = $Versions1[0]; 307 my $DPath1 = $LibVers1{$LName}{$LV1}; 308 my @Versions2 = keys(%{$LibVers2{$LName}}); 309 if($#Versions2>=1) 310 { # should be only one version 311 next; 312 } 313 my ($LV2, $LName2, $DPath2) = (); 314 my $LName_Short = libPart($LName, "name+ext"); 315 if($LName2 = $ChangedSoname{$LName}) 316 { # changed SONAME 317 @Versions2 = keys(%{$LibVers2{$LName2}}); 318 if(not @Versions2 or $#Versions2>=1) { 319 next; 320 } 321 $LV2 = $Versions2[0]; 322 $DPath2 = $LibVers2{$LName2}{$LV2}; 323 324 if(defined $LibVers2{$LName}) 325 { # show old soname in the table 326 $TestResults{$LName}{"v1"} = $LV1; 327 $TestResults{$LName}{"v2"} = $LV1; 328 } 329 330 if(defined $LibVers2{$LName}) 331 { # do not count results 332 $SONAME_Added{$LName_Short} = 1; 333 } 334 $SONAME_Changed{$LName_Short} = 1; 335 $LName = $LName_Short; 336 } 337 elsif(@Versions2) 338 { 339 $LV2 = $Versions2[0]; 340 $DPath2 = $LibVers2{$LName}{$LV2}; 341 } 342 else 343 { # removed 344 next; 345 } 346 my $ACC_compare = "perl $0 -l $LName -d1 \"$DPath1\" -d2 \"$DPath2\""; 347 348 my $BinReportPath = "compat_reports/$LName/abi_compat_report.html"; 349 my $SrcReportPath = "compat_reports/$LName/src_compat_report.html"; 350 my $BinReportPath_Full = $SYS_REPORT_PATH."/".$BinReportPath; 351 my $SrcReportPath_Full = $SYS_REPORT_PATH."/".$SrcReportPath; 352 353 if($In::Opt{"BinOnly"}) 354 { 355 $ACC_compare .= " -binary"; 356 $ACC_compare .= " -bin-report-path \"$BinReportPath_Full\""; 357 } 358 if($In::Opt{"SrcOnly"}) 359 { 360 $ACC_compare .= " -source"; 361 $ACC_compare .= " -src-report-path \"$SrcReportPath_Full\""; 362 } 363 364 if($In::Opt{"CheckHeadersOnly"}) { 365 $ACC_compare .= " -headers-only"; 366 } 367 if($GroupByHeaders) { 368 $ACC_compare .= " -component header"; 369 } 370 371 if($In::Opt{"DisableConstantsCheck"}) { 372 $ACC_compare .= " -disable-constants-check"; 373 } 374 375 $ACC_compare .= " -skip-added-constants"; 376 $ACC_compare .= " -skip-removed-constants"; 377 378 if($In::Opt{"Quiet"}) 379 { # quiet mode 380 $ACC_compare .= " -quiet"; 381 } 382 if($In::Opt{"LogMode"} eq "n") { 383 $ACC_compare .= " -logging-mode n"; 384 } 385 elsif($In::Opt{"Quiet"}) { 386 $ACC_compare .= " -logging-mode a"; 387 } 388 if($In::Opt{"Debug"}) 389 { # debug mode 390 $ACC_compare .= " -debug"; 391 printMsg("INFO", "$ACC_compare"); 392 } 393 printMsg("INFO_C", "Checking $LName: "); 394 system($ACC_compare." 1>$TmpDir/null 2>$TmpDir/$LName.stderr"); 395 if(-s "$TmpDir/$LName.stderr") 396 { 397 my $ErrorLog = readFile("$TmpDir/$LName.stderr"); 398 chomp($ErrorLog); 399 printMsg("INFO", "Failed ($ErrorLog)"); 400 } 401 else 402 { 403 printMsg("INFO", "Ok"); 404 if($In::Opt{"BinOnly"}) 405 { 406 $TestResults{$LName}{"Binary"} = readAttributes($BinReportPath_Full, 0); 407 $TestResults{$LName}{"Binary"}{"path"} = $BinReportPath; 408 } 409 if($In::Opt{"SrcOnly"}) 410 { 411 $TestResults{$LName}{"Source"} = readAttributes($SrcReportPath_Full, 0); 412 $TestResults{$LName}{"Source"}{"path"} = $SrcReportPath; 413 } 414 $TestResults{$LName}{"v1"} = $LV1; 415 $TestResults{$LName}{"v2"} = $LV2; 416 } 417 418 my $HP1 = $SPath1."/headers/".$LName; 419 my $HP2 = $SPath2."/headers/".$LName; 420 421 if(-d $HP1 422 and -d $HP2 423 and my $RfcDiff = getCmdPath("rfcdiff")) 424 { 425 my @Headers1 = cmdFind($HP1,"f"); 426 my @Headers2 = cmdFind($HP2,"f"); 427 428 my (%Files1, %Files2) = (); 429 430 foreach my $P (@Headers1) { 431 $Files1{getFilename($P)} = $P; 432 } 433 434 foreach my $P (@Headers2) { 435 $Files2{getFilename($P)} = $P; 436 } 437 438 my $Diff = ""; 439 foreach my $N (sort {lc($a) cmp lc($b)} keys(%Files1)) 440 { 441 my $Path1 = $Files1{$N}; 442 my $Path2 = undef; 443 444 if(defined $Files2{$N}) { 445 $Path2 = $Files2{$N}; 446 } 447 else { 448 next; 449 } 450 451 if(-s $Path1 == -s $Path2) 452 { 453 if(readFile($Path1) eq readFile($Path2)) { 454 next; 455 } 456 } 457 458 my $DiffOut = $TmpDir."/rfcdiff"; 459 460 if(-e $DiffOut) { 461 unlink($DiffOut); 462 } 463 464 my $Cmd_R = $RfcDiff." --width 80 --stdout \"$Path1\" \"$Path2\" >$DiffOut 2>/dev/null"; 465 qx/$Cmd_R/; # execute 466 467 if(-s $DiffOut) 468 { 469 my $Content = readFile($DiffOut); 470 if(length($Content)<3500 and $Content=~/The files are identical|No changes|Failed to create/i) { 471 next; 472 } 473 474 $Content=~s/<\!--(.|\n)+?-->\s*//g; 475 $Content=~s/\A((.|\n)+<body\s*>)((.|\n)+)(<\/body>(.|\n)+)\Z/$3/; 476 $Content=~s/(<td colspan=\"5\"[^>]*>)(.+)(<\/td>)/$1$3/; 477 $Content=~s/(<table) /$1 class='diff_tbl' /g; 478 479 $Content=~s&<td class="lineno" valign="top"></td>&&g; 480 $Content=~s&<td class="lineno"></td>&&g; 481 $Content=~s&<th></th>&&g; 482 $Content=~s&<td></td>&&g; 483 484 $Content=~s/(\Q$N\E)( )/$1 ($LV1-$SystemName1)$2/; 485 $Content=~s/(\Q$N\E)( )/$1 ($LV2-$SystemName2)$2/; 486 487 if($Diff) { 488 $Diff .= "<br/><br/>\n"; 489 } 490 $Diff .= $Content; 491 } 492 } 493 494 if($Diff) 495 { 496 my $Title = $LName.": headers diff between $LV1-$SystemName1 and $LV2-$SystemName2 versions"; 497 my $Keywords = $LName.", header, diff"; 498 my $Description = "Diff for header files between $LV1-$SystemName1 and $LV2-$SystemName2 versions of $LName"; 499 my $Styles = readModule("Styles", "HeadersDiff.css"); 500 501 my $Link = "This html diff was produced by <a href='http://tools.ietf.org/tools/rfcdiff/'>rfcdiff</a> 1.41."; 502 503 $Diff .= "<br/>"; 504 $Diff .= "<div style='width:100%;' align='left'>$Link</div>\n"; 505 506 $Diff = "<h1>Headers diff for <span style='color:Blue;'>$LName</span> between <span style='color:Red;'>$LV1-$SystemName1</span> and <span style='color:Red;'>$LV2-$SystemName2</span> versions</h1><br/><br/>".$Diff; 507 508 $Diff = "<table width='100%' cellpadding='0' cellspacing='0'><tr><td>$Diff</td></tr></table>"; 509 510 $Diff = composeHTML_Head($Title, $Keywords, $Description, $Styles, "", 1)."\n<body>\n$Diff\n</body>\n</html>\n"; 511 512 my $Output = $SYS_REPORT_PATH."/headers_diff/$LName"; 513 writeFile($Output."/diff.html", $Diff); 514 } 515 } 516 } 517 518 my %TOTAL = (); 519 foreach my $LName (keys(%TestResults)) 520 { 521 if($SONAME_Changed{$LName}) { 522 next; 523 } 524 foreach my $Comp ("Binary", "Source") 525 { 526 if(not defined $TestResults{$LName}{$Comp}) { 527 next; 528 } 529 foreach my $Kind (keys(%{$TestResults{$LName}{$Comp}})) 530 { 531 if($Kind=~/_problems_(high|medium|low)/) { 532 $TOTAL{$LName}{$Comp} += $TestResults{$LName}{$Comp}{$Kind}; 533 } 534 } 535 } 536 } 537 538 my %META_DATA = (); 539 my %STAT = (); 540 foreach my $Comp ("Binary", "Source") 541 { 542 $STAT{$Comp}{"total"} = keys(%TestResults) - keys(%SONAME_Changed); 543 $STAT{$Comp}{"added"} = keys(%Added); 544 $STAT{$Comp}{"removed"} = keys(%Removed); 545 546 foreach ("added", "removed") 547 { 548 my $Kind = $_."_interfaces"; 549 foreach my $LName (keys(%TestResults)) 550 { 551 next if($SONAME_Changed{$LName}); 552 $STAT{$Comp}{$Kind} += $TestResults{$LName}{$Comp}{$_}; 553 } 554 push(@{$META_DATA{$Comp}}, $Kind.":".$STAT{$Comp}{$Kind}); 555 } 556 foreach my $T ("type", "interface") 557 { 558 foreach my $S ("high", "medium", "low") 559 { 560 my $Kind = $T."_problems_".$S; 561 foreach my $LName (keys(%TestResults)) 562 { 563 next if($SONAME_Changed{$LName}); 564 $STAT{$Comp}{$Kind} += $TestResults{$LName}{$Comp}{$Kind}; 565 } 566 push(@{$META_DATA{$Comp}}, $Kind.":".$STAT{$Comp}{$Kind}); 567 } 568 } 569 foreach my $LName (keys(%TestResults)) 570 { 571 next if($SONAME_Changed{$LName}); 572 foreach ("affected", "changed_constants") { 573 $STAT{$Comp}{$_} += $TestResults{$LName}{$Comp}{$_}; 574 } 575 if(not defined $STAT{$Comp}{"verdict"} 576 and $TestResults{$LName}{$Comp}{"verdict"} eq "incompatible") { 577 $STAT{$Comp}{"verdict"} = "incompatible"; 578 } 579 } 580 if(not defined $STAT{$Comp}{"verdict"}) { 581 $STAT{$Comp}{"verdict"} = "compatible"; 582 } 583 if($STAT{$Comp}{"total"}) { 584 $STAT{$Comp}{"affected"} /= $STAT{$Comp}{"total"}; 585 } 586 else { 587 $STAT{$Comp}{"affected"} = 0; 588 } 589 $STAT{$Comp}{"affected"} = showNum($STAT{$Comp}{"affected"}); 590 if($STAT{$Comp}{"verdict"}>1) { 591 $STAT{$Comp}{"verdict"} = 1; 592 } 593 push(@{$META_DATA{$Comp}}, "changed_constants:".$STAT{$Comp}{"changed_constants"}); 594 push(@{$META_DATA{$Comp}}, "tool_version:".dumpVersion("perl $0")); 595 foreach ("removed", "added", "total", "affected", "verdict") { 596 @{$META_DATA{$Comp}} = ($_.":".$STAT{$Comp}{$_}, @{$META_DATA{$Comp}}); 597 } 598 } 599 600 my $SONAME_Title = "SONAME"; 601 if($In::Opt{"Target"} eq "windows") { 602 $SONAME_Title = "DLL"; 603 } 604 elsif($In::Opt{"Target"} eq "symbian") { 605 $SONAME_Title = "DSO"; 606 } 607 if($GroupByHeaders) 608 { # show the list of headers 609 $SONAME_Title = "Header File"; 610 } 611 612 my $SYS_REPORT = "<h1>"; 613 614 if($In::Opt{"BinOnly"} 615 and $In::Opt{"SrcOnly"}) { 616 $SYS_REPORT .= "API compatibility"; 617 } 618 elsif($In::Opt{"BinOnly"}) { 619 $SYS_REPORT .= "Binary compatibility"; 620 } 621 elsif($In::Opt{"SrcOnly"}) { 622 $SYS_REPORT .= "Source compatibility"; 623 } 624 625 $SYS_REPORT .= " report between <span style='color:Blue;'>$SystemName1</span> and <span style='color:Blue;'>$SystemName2</span>"; 626 $SYS_REPORT .= " on <span style='color:Blue;'>".showArch($ArchName)."</span>\n"; 627 628 $SYS_REPORT .= "</h1>"; 629 $SYS_REPORT .= "<br/>\n"; 630 631 # legend 632 my $LEGEND = "<table class='legend'><tr>\n"; 633 $LEGEND .= "<td class='new' width='70px' style='text-align:left'>ADDED</td>\n"; 634 $LEGEND .= "<td class='passed' width='70px' style='text-align:left'>COMPATIBLE</td>\n"; 635 $LEGEND .= "</tr><tr>\n"; 636 $LEGEND .= "<td class='warning' style='text-align:left'>WARNING</td>\n"; 637 $LEGEND .= "<td class='failed' style='text-align:left'>INCOMPATIBLE</td>\n"; 638 $LEGEND .= "</tr></table>\n"; 639 640 $SYS_REPORT .= $LEGEND; 641 $SYS_REPORT .= "<br/>\n"; 642 643 my $Columns = 2; 644 645 my $Total = (keys(%TestResults) + keys(%Added) + keys(%Removed) - keys(%SONAME_Changed)); 646 my $HDiff = $SYS_REPORT_PATH."/headers_diff"; 647 648 $SYS_REPORT .= "<table class='summary'>\n"; 649 $SYS_REPORT .= "<tr>\n"; 650 $SYS_REPORT .= "<th rowspan='2'>$SONAME_Title<sup>$Total</sup></th>\n"; 651 if(not $GroupByHeaders) { 652 $SYS_REPORT .= "<th colspan='2'>Version</th>\n"; 653 } 654 if($In::Opt{"BinOnly"} 655 and $In::Opt{"SrcOnly"}) { 656 $SYS_REPORT .= "<th colspan='2'>Compatibility</th>\n"; 657 } 658 else { 659 $SYS_REPORT .= "<th rowspan='2'>Compatibility</th>\n"; 660 } 661 $SYS_REPORT .= "<th rowspan='2'>Added<br/>Symbols</th>\n"; 662 $SYS_REPORT .= "<th rowspan='2'>Removed<br/>Symbols</th>\n"; 663 if(-d $HDiff) 664 { 665 $SYS_REPORT .= "<th rowspan='2'>Headers<br/>Diff</th>\n"; 666 $Columns += 1; 667 } 668 $SYS_REPORT .= "</tr>\n"; 669 670 $SYS_REPORT .= "<tr>\n"; 671 if(not $GroupByHeaders) { 672 $SYS_REPORT .= "<th class='ver'>$SystemName1</th><th class='ver'>$SystemName2</th>\n"; 673 } 674 if($In::Opt{"BinOnly"} 675 and $In::Opt{"SrcOnly"}) { 676 $SYS_REPORT .= "<th>Binary</th><th>Source</th>\n"; 677 } 678 $SYS_REPORT .= "</tr>\n"; 679 my %RegisteredPairs = (); 680 681 foreach my $LName (sort {lc($a) cmp lc($b)} (keys(%TestResults), keys(%Added), keys(%Removed))) 682 { 683 next if($SONAME_Changed{$LName}); 684 my $LName_Short = libPart($LName, "name+ext"); 685 my $Anchor = $LName; 686 $Anchor=~s/\+/p/g; # anchor for libFLAC++ is libFLACpp 687 $Anchor=~s/\~/-/g; # libqttracker.so.1~6 688 689 $SYS_REPORT .= "<tr>\n"; 690 $SYS_REPORT .= "<td class='object'>$LName<a name=\'$Anchor\'></a></td>\n"; 691 if(defined $Removed{$LName}) { 692 $SYS_REPORT .= "<td class='failed ver'>".printVer($Removed{$LName}{"version"})."</td>\n"; 693 } 694 elsif(defined $Added{$LName}) { 695 $SYS_REPORT .= "<td class='new'><a href='".$Added{$LName}{"list"}."'>added</a></td>\n"; 696 } 697 elsif(not $GroupByHeaders) 698 { 699 $SYS_REPORT .= "<td class='ver'>".printVer($TestResults{$LName}{"v1"})."</td>\n"; 700 } 701 my $SONAME_report = "<td colspan=\'$Columns\' rowspan='2'>\n"; 702 if($In::Opt{"BinOnly"} 703 and $In::Opt{"SrcOnly"}) { 704 $SONAME_report .= "SONAME has been changed (see <a href='".$TestResults{$LName_Short}{"Binary"}{"path"}."'>binary</a> and <a href='".$TestResults{$LName_Short}{"Source"}{"path"}."'>source</a> compatibility reports)\n"; 705 } 706 elsif($In::Opt{"BinOnly"}) { 707 $SONAME_report .= "SONAME has been <a href='".$TestResults{$LName_Short}{"Binary"}{"path"}."'>changed</a>\n"; 708 } 709 elsif($In::Opt{"SrcOnly"}) { 710 $SONAME_report .= "SONAME has been <a href='".$TestResults{$LName_Short}{"Source"}{"path"}."'>changed</a>\n"; 711 } 712 $SONAME_report .= "</td>\n"; 713 714 if(defined $Added{$LName}) 715 { # added library 716 $SYS_REPORT .= "<td class='new ver'>".printVer($Added{$LName}{"version"})."</td>\n"; 717 $SYS_REPORT .= "<td class='passed'>100%</td>\n" if($In::Opt{"BinOnly"}); 718 $SYS_REPORT .= "<td class='passed'>100%</td>\n" if($In::Opt{"SrcOnly"}); 719 if($RegisteredPairs{$LName}) { 720 # do nothing 721 } 722 elsif(my $To = $ChangedSoname{$LName}) 723 { 724 $RegisteredPairs{$To}=1; 725 $SYS_REPORT .= $SONAME_report; 726 } 727 else 728 { 729 foreach (1 .. $Columns) { 730 $SYS_REPORT .= "<td>N/A</td>\n"; # colspan='5' 731 } 732 } 733 $SYS_REPORT .= "</tr>\n"; 734 next; 735 } 736 elsif(defined $Removed{$LName}) 737 { # removed library 738 $SYS_REPORT .= "<td class='failed'><a href='".$Removed{$LName}{"list"}."'>removed</a></td>\n"; 739 $SYS_REPORT .= "<td class='failed'>0%</td>\n" if($In::Opt{"BinOnly"}); 740 $SYS_REPORT .= "<td class='failed'>0%</td>\n" if($In::Opt{"SrcOnly"}); 741 if($RegisteredPairs{$LName}) { 742 # do nothing 743 } 744 elsif(my $To = $ChangedSoname{$LName}) 745 { 746 $RegisteredPairs{$To}=1; 747 $SYS_REPORT .= $SONAME_report; 748 } 749 else 750 { 751 foreach (1 .. $Columns) { 752 $SYS_REPORT .= "<td>N/A</td>\n"; # colspan='5' 753 } 754 } 755 $SYS_REPORT .= "</tr>\n"; 756 next; 757 } 758 elsif(defined $ChangedSoname{$LName}) 759 { # added library 760 $SYS_REPORT .= "<td class='ver'>".printVer($TestResults{$LName}{"v2"})."</td>\n"; 761 $SYS_REPORT .= "<td class='passed'>100%</td>\n" if($In::Opt{"BinOnly"}); 762 $SYS_REPORT .= "<td class='passed'>100%</td>\n" if($In::Opt{"SrcOnly"}); 763 if($RegisteredPairs{$LName}) { 764 # do nothing 765 } 766 elsif(my $To = $ChangedSoname{$LName}) 767 { 768 $RegisteredPairs{$To}=1; 769 $SYS_REPORT .= $SONAME_report; 770 } 771 else 772 { 773 foreach (1 .. $Columns) { 774 $SYS_REPORT .= "<td>N/A</td>\n"; # colspan='5' 775 } 776 } 777 $SYS_REPORT .= "</tr>\n"; 778 next; 779 } 780 elsif(not $GroupByHeaders) 781 { 782 $SYS_REPORT .= "<td class='ver'>".printVer($TestResults{$LName}{"v2"})."</td>\n"; 783 } 784 785 my $BinCompatReport = $TestResults{$LName}{"Binary"}{"path"}; 786 my $SrcCompatReport = $TestResults{$LName}{"Source"}{"path"}; 787 788 if($In::Opt{"BinOnly"}) 789 { 790 if($TestResults{$LName}{"Binary"}{"verdict"} eq "compatible") 791 { 792 my $Cl = "passed"; 793 if($TOTAL{$LName}{"Binary"}) { 794 $Cl = "warning"; 795 } 796 $SYS_REPORT .= "<td class=\'$Cl\'><a href=\'$BinCompatReport\'>100%</a></td>\n"; 797 } 798 else 799 { 800 my $Compatible = 100 - $TestResults{$LName}{"Binary"}{"affected"}; 801 my $Cl = "incompatible"; 802 if($Compatible>=90) { 803 $Cl = "warning"; 804 } 805 elsif($Compatible>=80) { 806 $Cl = "almost_compatible"; 807 } 808 $SYS_REPORT .= "<td class=\'$Cl\'><a href=\'$BinCompatReport\'>$Compatible%</a></td>\n"; 809 } 810 } 811 if($In::Opt{"SrcOnly"}) 812 { 813 if($TestResults{$LName}{"Source"}{"verdict"} eq "compatible") 814 { 815 my $Cl = "passed"; 816 if($TOTAL{$LName}{"Source"}) { 817 $Cl = "warning"; 818 } 819 $SYS_REPORT .= "<td class=\'$Cl\'><a href=\'$SrcCompatReport\'>100%</a></td>\n"; 820 } 821 else 822 { 823 my $Compatible = 100 - $TestResults{$LName}{"Source"}{"affected"}; 824 my $Cl = "incompatible"; 825 if($Compatible>=90) { 826 $Cl = "warning"; 827 } 828 elsif($Compatible>=80) { 829 $Cl = "almost_compatible"; 830 } 831 $SYS_REPORT .= "<td class=\'$Cl\'><a href=\'$SrcCompatReport\'>$Compatible%</a></td>\n"; 832 } 833 } 834 if($In::Opt{"BinOnly"}) 835 { # show added/removed symbols at binary level 836 # for joined and -binary-only reports 837 my $AddedSym=""; 838 if(my $Count = $TestResults{$LName}{"Binary"}{"added"}) { 839 $AddedSym="<a href='$BinCompatReport\#Added'>$Count new</a>"; 840 } 841 if($AddedSym) { 842 $SYS_REPORT.="<td class='new'>$AddedSym</td>\n"; 843 } 844 else { 845 $SYS_REPORT.="<td class='passed'>0</td>\n"; 846 } 847 my $RemovedSym=""; 848 if(my $Count = $TestResults{$LName}{"Binary"}{"removed"}) { 849 $RemovedSym="<a href='$BinCompatReport\#Removed'>$Count removed</a>"; 850 } 851 if($RemovedSym) { 852 $SYS_REPORT.="<td class='failed'>$RemovedSym</td>\n"; 853 } 854 else { 855 $SYS_REPORT.="<td class='passed'>0</td>\n"; 856 } 857 } 858 elsif($In::Opt{"SrcOnly"}) 859 { 860 my $AddedSym=""; 861 if(my $Count = $TestResults{$LName}{"Source"}{"added"}) { 862 $AddedSym="<a href='$SrcCompatReport\#Added'>$Count new</a>"; 863 } 864 if($AddedSym) { 865 $SYS_REPORT.="<td class='new'>$AddedSym</td>\n"; 866 } 867 else { 868 $SYS_REPORT.="<td class='passed'>0</td>\n"; 869 } 870 my $RemovedSym=""; 871 if(my $Count = $TestResults{$LName}{"Source"}{"removed"}) { 872 $RemovedSym="<a href='$SrcCompatReport\#Removed'>$Count removed</a>"; 873 } 874 if($RemovedSym) { 875 $SYS_REPORT.="<td class='failed'>$RemovedSym</td>\n"; 876 } 877 else { 878 $SYS_REPORT.="<td class='passed'>0</td>\n"; 879 } 880 } 881 882 if(-d $HDiff) 883 { 884 if(-d $HDiff."/".$LName) { 885 $SYS_REPORT .= "<td><a href=\'headers_diff/$LName/diff.html\'>diff</a></td>\n"; 886 } 887 elsif(defined $Added{$LName} or defined $Removed{$LName}) { 888 $SYS_REPORT .= "<td>N/A</td>\n"; 889 } 890 else { 891 $SYS_REPORT .= "<td>Empty</td>\n"; 892 } 893 } 894 895 $SYS_REPORT .= "</tr>\n"; 896 } 897 898 $SYS_REPORT .= "</table>"; 899 900 my $Title = "$SystemName1 vs $SystemName2 compatibility report"; 901 my $Keywords = "compatibility, $SystemName1, $SystemName2, API, changes"; 902 my $Description = "API compatibility report between $SystemName1 and $SystemName2 on ".showArch($ArchName); 903 my $Styles = readModule("Styles", "CmpSystems.css"); 904 905 $SYS_REPORT = composeHTML_Head($Title, $Keywords, $Description, $Styles, "", 1)."\n<body>\n<div>".$SYS_REPORT."</div>\n"; 906 $SYS_REPORT .= "<br/><br/>\n"; 907 $SYS_REPORT .= getReportFooter(); 908 $SYS_REPORT .= "</body></html>\n"; 909 910 if($In::Opt{"SrcOnly"}) { 911 $SYS_REPORT = "<!-\- kind:source;".join(";", @{$META_DATA{"Source"}})." -\->\n".$SYS_REPORT; 912 } 913 if($In::Opt{"BinOnly"}) { 914 $SYS_REPORT = "<!-\- kind:binary;".join(";", @{$META_DATA{"Binary"}})." -\->\n".$SYS_REPORT; 915 } 916 my $REPORT_PATH = $SYS_REPORT_PATH."/"; 917 if($In::Opt{"BinOnly"} and $In::Opt{"SrcOnly"}) { 918 $REPORT_PATH .= "compat_report.html"; 919 } 920 elsif($In::Opt{"BinOnly"}) { 921 $REPORT_PATH .= "abi_compat_report.html"; 922 } 923 elsif($In::Opt{"SrcOnly"}) { 924 $REPORT_PATH .= "src_compat_report.html"; 925 } 926 writeFile($REPORT_PATH, $SYS_REPORT); 927 printMsg("INFO", "\nSee detailed report:\n $REPORT_PATH"); 928} 929 930sub printVer($) 931{ 932 if($_[0] eq "") { 933 return 0; 934 } 935 return $_[0]; 936} 937 938sub getPrefix_S($) 939{ 940 my $Prefix = getPrefix($_[0]); 941 if(not $Prefix or defined $NonPrefix{lc($Prefix)}) { 942 return "NONE"; 943 } 944 return $Prefix; 945} 946 947sub readSysDesc($) 948{ 949 my $Content = $_[0]; 950 951 $Content=~s/\/\*(.|\n)+?\*\///g; 952 $Content=~s/<\!--(.|\n)+?-->//g; 953 954 $SysDesc{"Name"} = parseTag(\$Content, "name"); 955 956 if(not $SysDesc{"Name"}) { 957 exitStatus("Error", "system name is not specified (<name> section)"); 958 } 959 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "libs"))) 960 { # target libs 961 if(not -e $Path) { 962 exitStatus("Access_Error", "can't access \'$Path\'"); 963 } 964 $Path = getAbsPath($Path); 965 $SysDesc{"Libs"}{$Path} = 1; 966 } 967 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs"))) 968 { # target libs 969 if(not -d $Path) { 970 exitStatus("Access_Error", "can't access directory \'$Path\'"); 971 } 972 $Path = getAbsPath($Path); 973 $SysDesc{"SearchLibs"}{$Path} = 1; 974 } 975 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_libs"))) 976 { # skip libs 977 $SysDesc{"SkipLibs"}{$Path} = 1; 978 } 979 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "headers"))) 980 { 981 if(not -e $Path) { 982 exitStatus("Access_Error", "can't access \'$Path\'"); 983 } 984 $Path = getAbsPath($Path); 985 $SysDesc{"Headers"}{$Path} = 1; 986 } 987 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers"))) 988 { 989 if(not -d $Path) { 990 exitStatus("Access_Error", "can't access directory \'$Path\'"); 991 } 992 $Path = getAbsPath($Path); 993 $SysDesc{"SearchHeaders"}{$Path} = 1; 994 } 995 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools"))) 996 { 997 if(not -d $Path) { 998 exitStatus("Access_Error", "can't access directory \'$Path\'"); 999 } 1000 $Path = getAbsPath($Path); 1001 $SysDesc{"Tools"}{$Path} = 1; 1002 1003 $In::Opt{"TargetTools"}{$Path} = 1; 1004 push_U($In::Opt{"SysPaths"}{"bin"}, $Path); 1005 } 1006 foreach my $Op (split(/\s*\n\s*/, parseTag(\$Content, "gcc_options"))) { 1007 $SysDesc{"GccOpts"}{$Op} = 1; 1008 } 1009 if($SysDesc{"CrossPrefix"} = parseTag(\$Content, "cross_prefix")) 1010 { # <cross_prefix> section of XML descriptor 1011 $In::Opt{"CrossPrefix"} = $SysDesc{"CrossPrefix"}; 1012 } 1013 elsif($In::Opt{"CrossPrefix"}) 1014 { # -cross-prefix option 1015 $SysDesc{"CrossPrefix"} = $In::Opt{"CrossPrefix"}; 1016 } 1017 $SysDesc{"Defines"} = parseTag(\$Content, "defines"); 1018 if($SysDesc{"Image"} = parseTag(\$Content, "image")) 1019 { # <image> 1020 # FIXME: isn't implemented yet 1021 if(not -f $SysDesc{"Image"}) { 1022 exitStatus("Access_Error", "can't access \'".$SysDesc{"Image"}."\'"); 1023 } 1024 } 1025} 1026 1027sub readSysDesc_P($) 1028{ 1029 my $Path = $_[0]; 1030 my $Content = readFile($Path); 1031 my %Tags = ( 1032 "headers" => "mf", 1033 "skip_headers" => "mf", 1034 "skip_including" => "mf", 1035 "skip_include_paths" => "mf", 1036 "skip_libs" => "mf", 1037 "include_preamble" => "mf", 1038 "add_include_paths" => "mf", 1039 "gcc_options" => "m", 1040 "skip_symbols" => "m", 1041 "skip_types" => "m", 1042 "ignore_symbols" => "h", 1043 "non_prefix" => "h", 1044 "defines" => "s", 1045 "cxx_incompatible" => "s" 1046 ); 1047 my %DInfo = (); 1048 foreach my $Tag (keys(%Tags)) 1049 { 1050 if(my $TContent = parseTag(\$Content, $Tag)) 1051 { 1052 if($Tags{$Tag}=~/m/) 1053 { # multi-line (+order) 1054 my @Items = split(/\s*\n\s*/, $TContent); 1055 $DInfo{$Tag} = []; 1056 foreach my $Item (@Items) 1057 { 1058 if($Tags{$Tag}=~/f/) { 1059 $Item = pathFmt($Item); 1060 } 1061 push(@{$DInfo{$Tag}}, $Item); 1062 } 1063 1064 } 1065 elsif($Tags{$Tag}=~/s/) 1066 { # single element 1067 $DInfo{$Tag} = $TContent; 1068 } 1069 else 1070 { # hash array 1071 my @Items = split(/\s*\n\s*/, $TContent); 1072 foreach my $Item (@Items) { 1073 $DInfo{$Tag}{$Item}=1; 1074 } 1075 } 1076 } 1077 } 1078 1079 if(defined $DInfo{"non_self_compiled"}) 1080 { # support for old ABI dumps 1081 $DInfo{"skip_including"} = $DInfo{"non_self_compiled"}; 1082 } 1083 1084 return \%DInfo; 1085} 1086 1087sub readSysInfo() 1088{ 1089 my $TargetSysInfo = $In::Opt{"TargetSysInfo"}; 1090 1091 # Library Specific Info 1092 my %SysInfo = (); 1093 if(-d $TargetSysInfo."/descriptors/") 1094 { 1095 foreach my $DPath (cmdFind($TargetSysInfo."/descriptors/","f","",1)) 1096 { 1097 my $LSName = getFilename($DPath); 1098 $LSName=~s/\.xml\Z//; 1099 $SysInfo{$LSName} = readSysDesc_P($DPath); 1100 } 1101 } 1102 else { 1103 printMsg("WARNING", "can't find \'$TargetSysInfo/descriptors\'"); 1104 } 1105 1106 # Exceptions 1107 if(checkGcc("4.4")) 1108 { # exception for libstdc++ 1109 $SysInfo{"libstdc++"}{"gcc_options"} = ["-std=c++0x"]; 1110 } 1111 if($In::Opt{"Target"} eq "symbian") 1112 { # exception for libstdcpp 1113 $SysInfo{"libstdcpp"}{"defines"} = "namespace std { struct nothrow_t {}; }"; 1114 } 1115 if($SysDesc{"Name"}=~/maemo/i) 1116 { # GL/gl.h: No such file 1117 $SysInfo{"libSDL"}{"skip_headers"}=["SDL_opengl.h"]; 1118 } 1119 1120 # Common Info 1121 my $SysCInfo = {}; 1122 if(-f $TargetSysInfo."/common.xml") { 1123 $SysCInfo = readSysDesc_P($TargetSysInfo."/common.xml"); 1124 } 1125 else { 1126 printMsg("Module_Error", "can't find \'$TargetSysInfo/common.xml\'"); 1127 } 1128 1129 my @CompilerOpts = (); 1130 if($SysDesc{"Name"}=~/maemo|meego/i) { 1131 push(@CompilerOpts, "-DMAEMO_CHANGES", "-DM_APPLICATION_NAME=\\\"app\\\""); 1132 } 1133 if(my @Opts = keys(%{$SysDesc{"GccOpts"}})) { 1134 push(@CompilerOpts, @Opts); 1135 } 1136 if(@CompilerOpts) 1137 { 1138 if(not $SysCInfo->{"gcc_options"}) { 1139 $SysCInfo->{"gcc_options"} = []; 1140 } 1141 push(@{$SysCInfo->{"gcc_options"}}, @CompilerOpts); 1142 } 1143 return (\%SysInfo, $SysCInfo); 1144} 1145 1146sub dumpSystem() 1147{ # -dump-system option handler 1148 # should be used with -sysroot and -cross-gcc options 1149 my $TmpDir = $In::Opt{"Tmp"}; 1150 my $LibExt = $In::Opt{"Ext"}; 1151 1152 my $SysName_P = $SysDesc{"Name"}; 1153 $SysName_P=~s/ /_/g; 1154 1155 my $SystemRoot = $In::Opt{"SystemRoot"}; 1156 1157 my $SYS_DUMP_PATH = "sys_dumps/".$SysName_P."/".getArch_GCC(1); 1158 if(not $In::Opt{"TargetLib"}) { 1159 rmtree($SYS_DUMP_PATH); 1160 } 1161 my (@SystemLibs, @SysHeaders) = (); 1162 1163 foreach my $Path (keys(%{$SysDesc{"Libs"}})) 1164 { 1165 if(not -e $Path) { 1166 exitStatus("Access_Error", "can't access \'$Path\'"); 1167 } 1168 if(-d $Path) 1169 { 1170 if(my @SubLibs = findLibs($Path,"",1)) { 1171 push(@SystemLibs, @SubLibs); 1172 } 1173 $SysDesc{"SearchLibs"}{$Path} = 1; 1174 } 1175 else 1176 { # single file 1177 push(@SystemLibs, $Path); 1178 $SysDesc{"SearchLibs"}{getDirname($Path)} = 1; 1179 } 1180 } 1181 foreach my $Path (keys(%{$SysDesc{"Headers"}})) 1182 { 1183 if(not -e $Path) { 1184 exitStatus("Access_Error", "can't access \'$Path\'"); 1185 } 1186 if(-d $Path) 1187 { 1188 if(my @SubHeaders = cmdFind($Path,"f","","")) { 1189 push(@SysHeaders, @SubHeaders); 1190 } 1191 $SysDesc{"SearchHeaders"}{$Path}=1; 1192 } 1193 else 1194 { # single file 1195 push(@SysHeaders, $Path); 1196 $SysDesc{"SearchHeaders"}{getDirname($Path)} = 1; 1197 } 1198 } 1199 my $GroupByHeaders = 0; 1200 if($In::Opt{"CheckHeadersOnly"}) 1201 { # -headers-only 1202 $GroupByHeaders = 1; 1203 # @SysHeaders = optimize_set(@SysHeaders); 1204 } 1205 elsif($SysDesc{"Image"}) 1206 { # one big image 1207 $GroupByHeaders = 1; 1208 @SystemLibs = ($SysDesc{"Image"}); 1209 } 1210 writeFile($SYS_DUMP_PATH."/target.txt", $In::Opt{"Target"}); 1211 my (%SysLib_Symbols, %SymbolGroup, %Symbol_SysHeaders, 1212 %SysHeader_Symbols, %SysLib_SysHeaders) = (); 1213 my (%Skipped, %Failed) = (); 1214 my (%SysHeaderDir_Used, %SysHeaderDir_SysHeaders) = (); 1215 my (%SymbolCounter, %TotalLibs) = (); 1216 my (%PrefixToLib, %LibPrefix, %PrefixSymbols) = (); 1217 1218 my %Glibc = map {$_=>1} ( 1219 "libc", 1220 "libpthread" 1221 ); 1222 my ($SysInfo, $SysCInfo) = readSysInfo(); 1223 1224 foreach (keys(%{$SysCInfo->{"non_prefix"}})) 1225 { 1226 $NonPrefix{$_} = 1; 1227 $NonPrefix{$_."_"} = 1; 1228 $NonPrefix{"_".$_} = 1; 1229 $NonPrefix{"_".$_."_"} = 1; 1230 } 1231 1232 if(not $GroupByHeaders) 1233 { 1234 if($In::Opt{"Debug"}) { 1235 printMsg("INFO", localtime(time)); 1236 } 1237 printMsg("INFO", "Indexing sonames ...\n"); 1238 } 1239 my (%LibSoname, %SysLibVersion) = (); 1240 my %DevelPaths = map {$_=>1} @SystemLibs; 1241 foreach my $Path (sort keys(%{$SysDesc{"SearchLibs"}})) 1242 { 1243 foreach my $LPath (findLibs($Path,"",1)) { 1244 $DevelPaths{$LPath} = 1; 1245 } 1246 } 1247 foreach my $LPath (keys(%DevelPaths)) 1248 { # register SONAMEs 1249 my $LName = getFilename($LPath); 1250 if(not isTargetLib($LName)) { 1251 next; 1252 } 1253 if($In::Opt{"Target"}=~/\A(linux|macos|freebsd|solaris)\Z/ 1254 and $LName!~/\Alib/) { 1255 next; 1256 } 1257 if(my $Soname = getSONAME($LPath)) 1258 { 1259 if($In::Opt{"Target"} eq "symbian") 1260 { 1261 if($Soname=~/[\/\\]/) 1262 { # L://epoc32/release/armv5/lib/gfxtrans{000a0000}.dso 1263 $Soname = getFilename($Soname); 1264 } 1265 $Soname = lc($Soname); 1266 } 1267 if(not defined $LibSoname{$LName}) { 1268 $LibSoname{$LName}=$Soname; 1269 } 1270 if(-l $LPath and my $Path = realpath_F($LPath)) 1271 { 1272 my $Name = getFilename($Path); 1273 if(not defined $LibSoname{$Name}) { 1274 $LibSoname{$Name}=$Soname; 1275 } 1276 } 1277 } 1278 else 1279 { # windows and others 1280 $LibSoname{$LName}=$LName; 1281 } 1282 } 1283 my $SONAMES = ""; 1284 foreach (sort {lc($a) cmp lc($b)} keys(%LibSoname)) { 1285 $SONAMES .= $_.";".$LibSoname{$_}."\n"; 1286 } 1287 if(not $GroupByHeaders) { 1288 writeFile($SYS_DUMP_PATH."/sonames.txt", $SONAMES); 1289 } 1290 foreach my $LPath (sort keys(%DevelPaths)) 1291 { # register VERSIONs 1292 my $LName = getFilename($LPath); 1293 if(not isTargetLib($LName) 1294 and not isTargetLib($LibSoname{$LName})) { 1295 next; 1296 } 1297 if(my $BV = getBinVer($LPath)) 1298 { # binary version 1299 $SysLibVersion{$LName} = $BV; 1300 } 1301 elsif(my $PV = libPart($LName, "version")) 1302 { # source version 1303 $SysLibVersion{$LName} = $PV; 1304 } 1305 elsif(my $SV = libPart(getSONAME($LPath), "version")) 1306 { # soname version 1307 $SysLibVersion{$LName} = $SV; 1308 } 1309 elsif($LName=~/(\d[\d\.\-\_]*)\.$LibExt\Z/) 1310 { # libfreebl3.so 1311 if($1 ne 32 and $1 ne 64) { 1312 $SysLibVersion{$LName} = $1; 1313 } 1314 } 1315 } 1316 my $VERSIONS = ""; 1317 foreach (sort {lc($a) cmp lc($b)} keys(%SysLibVersion)) { 1318 $VERSIONS .= $_.";".$SysLibVersion{$_}."\n"; 1319 } 1320 if(not $GroupByHeaders) { 1321 writeFile($SYS_DUMP_PATH."/versions.txt", $VERSIONS); 1322 } 1323 1324 # create target list 1325 my @SkipLibs = keys(%{$SysDesc{"SkipLibs"}}); 1326 if(my $CSkip = $SysCInfo->{"skip_libs"}) { 1327 push(@SkipLibs, @{$CSkip}); 1328 } 1329 if(@SkipLibs and not $In::Opt{"TargetLib"}) 1330 { 1331 my %SkipLibs = map {$_ => 1} @SkipLibs; 1332 my @Target = (); 1333 foreach my $LPath (@SystemLibs) 1334 { 1335 my $LName = getFilename($LPath); 1336 my $LName_Short = libPart($LName, "name+ext"); 1337 if(not defined $SkipLibs{$LName_Short} 1338 and not defined $SkipLibs{$LName} 1339 and not checkList($LPath, \@SkipLibs)) { 1340 push(@Target, $LName); 1341 } 1342 } 1343 addTargetLibs(\@Target); 1344 } 1345 1346 my %SysLibs = (); 1347 foreach my $LPath (sort @SystemLibs) 1348 { 1349 my $LName = getFilename($LPath); 1350 my $LSName = libPart($LName, "short"); 1351 my $LRelPath = cutPrefix($LPath, $SystemRoot); 1352 if(not isTargetLib($LName)) { 1353 next; 1354 } 1355 if($In::Opt{"Target"}=~/\A(linux|macos|freebsd|solaris)\Z/ 1356 and $LName!~/\Alib/) { 1357 next; 1358 } 1359 if($In::Opt{"Target"} eq "symbian") 1360 { 1361 if(my $V = libPart($LName, "version")) 1362 { # skip qtcore.dso 1363 # register qtcore{00040604}.dso 1364 delete($SysLibs{getDirname($LPath)."\\".$LSName.".".$LibExt}); 1365 my $MV = libPart($LibSoname{$LSName.".".$LibExt}, "version"); 1366 if($MV and $V ne $MV) 1367 { # skip other versions: 1368 # qtcore{00040700}.dso 1369 # qtcore{00040702}.dso 1370 next; 1371 } 1372 } 1373 } 1374 if(-l $LPath) 1375 { # symlinks 1376 if(my $Path = realpath_F($LPath)) { 1377 $SysLibs{$Path} = 1; 1378 } 1379 } 1380 elsif(-f $LPath) 1381 { 1382 if($Glibc{$LSName} 1383 and -T $LPath) 1384 { # GNU ld scripts (libc.so, libpthread.so) 1385 my @Candidates = cmdFind($SystemRoot."/lib","",$LSName.".".$LibExt."*","1"); 1386 if(@Candidates) 1387 { 1388 my $Candidate = $Candidates[0]; 1389 if(-l $Candidate 1390 and my $Path = realpath_F($Candidate)) { 1391 $Candidate = $Path; 1392 } 1393 $SysLibs{$Candidate} = 1; 1394 } 1395 } 1396 else { 1397 $SysLibs{$LPath} = 1; 1398 } 1399 } 1400 } 1401 @SystemLibs = (); # clear memory 1402 1403 if(not keys(%SysLibs)) { 1404 exitStatus("Error", "can't find libraries"); 1405 } 1406 1407 if(not $In::Opt{"CheckHeadersOnly"}) 1408 { 1409 if($In::Opt{"Debug"}) { 1410 printMsg("INFO", localtime(time)); 1411 } 1412 if($SysDesc{"Image"}) { 1413 printMsg("INFO", "Reading symbols from image ...\n"); 1414 } 1415 else { 1416 printMsg("INFO", "Reading symbols from libraries ...\n"); 1417 } 1418 } 1419 1420 my %Syms = (); 1421 my @AllSyms = {}; 1422 my %ShortestNames = (); 1423 1424 foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) 1425 { 1426 my $LRelPath = cutPrefix($LPath, $SystemRoot); 1427 my $LName = getFilename($LPath); 1428 1429 $ShortestNames{$LPath} = libPart($LName, "shortest"); 1430 1431 my $Res = readSymbols_Lib(1, $LPath, 0, "-Weak", 0, 0); 1432 1433 if(not keys(%{$Res}) and $In::Opt{"TargetLib"}) { 1434 exitStatus("Error", "can't find exported symbols in the library"); 1435 } 1436 1437 $Syms{$LPath} = $Res->{$LName}; 1438 push(@AllSyms, keys(%{$Syms{$LPath}})); 1439 } 1440 1441 my $Translate = translateSymbols(@AllSyms, 1); 1442 1443 my %DupSymbols = (); 1444 1445 foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) 1446 { 1447 my $LRelPath = cutPrefix($LPath, $SystemRoot); 1448 my $LName = getFilename($LPath); 1449 foreach my $Symbol (keys(%{$Syms{$LPath}})) 1450 { 1451 $Symbol=~s/[\@\$]+(.*)\Z//g; 1452 if($Symbol=~/\A(_Z|\?)/) 1453 { 1454 if(isPrivateData($Symbol)) { 1455 next; 1456 } 1457 if($Symbol=~/(C1|C2|D0|D1|D2)E/) 1458 { # do NOT analyze constructors 1459 # and destructors 1460 next; 1461 } 1462 my $Unmangled = $Translate->{$Symbol}; 1463 $Unmangled=~s/<.+>//g; 1464 if($Unmangled=~/\A([\w:]+)/) 1465 { # cut out the parameters 1466 my @Elems = split(/::/, $1); 1467 my ($Class, $Short) = ("", ""); 1468 $Short = $Elems[$#Elems]; 1469 if($#Elems>=1) 1470 { 1471 $Class = $Elems[$#Elems-1]; 1472 pop(@Elems); 1473 } 1474 # the short and class name should be 1475 # matched in one header file 1476 $SymbolGroup{$LRelPath}{$Class} = $Short; 1477 foreach my $Sym (@Elems) 1478 { 1479 if($SysCInfo->{"ignore_symbols"}{$Symbol}) 1480 { # do NOT match this symbol 1481 next; 1482 } 1483 $SysLib_Symbols{$LPath}{$Sym} = 1; 1484 if(my $Prefix = getPrefix_S($Sym)) 1485 { 1486 $PrefixToLib{$Prefix}{$LName} += 1; 1487 $LibPrefix{$LPath}{$Prefix} += 1; 1488 $PrefixSymbols{$LPath}{$Prefix}{$Sym} = 1; 1489 } 1490 $SymbolCounter{$Sym}{$LPath} = 1; 1491 1492 if(my @Libs = keys(%{$SymbolCounter{$Sym}})) 1493 { 1494 if($#Libs>=1) 1495 { 1496 foreach (@Libs) { 1497 $DupSymbols{$_}{$Sym} = 1; 1498 } 1499 } 1500 } 1501 } 1502 } 1503 } 1504 else 1505 { 1506 if($SysCInfo->{"ignore_symbols"}{$Symbol}) 1507 { # do NOT match this symbol 1508 next; 1509 } 1510 $SysLib_Symbols{$LPath}{$Symbol} = 1; 1511 if(my $Prefix = getPrefix_S($Symbol)) 1512 { 1513 $PrefixToLib{$Prefix}{$LName} += 1; 1514 $LibPrefix{$LPath}{$Prefix} += 1; 1515 $PrefixSymbols{$LPath}{$Prefix}{$Symbol} = 1; 1516 } 1517 $SymbolCounter{$Symbol}{$LPath} = 1; 1518 1519 if(my @Libs = keys(%{$SymbolCounter{$Symbol}})) 1520 { 1521 if($#Libs>=1) 1522 { 1523 foreach (@Libs) { 1524 $DupSymbols{$_}{$Symbol} = 1; 1525 } 1526 } 1527 } 1528 } 1529 } 1530 } 1531 1532 %Syms = (); 1533 %{$Translate} = (); 1534 1535 # remove minor symbols 1536 foreach my $LPath (keys(%SysLib_Symbols)) 1537 { 1538 my $SName = $ShortestNames{$LPath}; 1539 my $Count = keys(%{$SysLib_Symbols{$LPath}}); 1540 my %Prefixes = %{$LibPrefix{$LPath}}; 1541 my @Prefixes = sort {$Prefixes{$b}<=>$Prefixes{$a}} keys(%Prefixes); 1542 1543 if($#Prefixes>=1) 1544 { 1545 my $MaxPrefix = $Prefixes[0]; 1546 if($MaxPrefix eq "NONE") { 1547 $MaxPrefix = $Prefixes[1]; 1548 } 1549 my $Max = $Prefixes{$MaxPrefix}; 1550 my $None = $Prefixes{"NONE"}; 1551 1552 next if($None*100/$Count>=50); 1553 next if($None>=$Max); 1554 1555 foreach my $Prefix (@Prefixes) 1556 { 1557 next if($Prefix eq $MaxPrefix); 1558 my $Num = $Prefixes{$Prefix}; 1559 my $Rm = 0; 1560 1561 if($Prefix eq "NONE") { 1562 $Rm = 1; 1563 } 1564 else 1565 { 1566 if($Num*100/$Max<5) { 1567 $Rm = 1; 1568 } 1569 } 1570 1571 if($Rm) 1572 { 1573 next if($Prefix=~/\Q$MaxPrefix\E/i); 1574 next if($MaxPrefix=~/\Q$Prefix\E/i); 1575 next if($Prefix=~/\Q$SName\E/i); 1576 1577 foreach my $Symbol (keys(%{$PrefixSymbols{$LPath}{$Prefix}})) { 1578 delete($SysLib_Symbols{$LPath}{$Symbol}); 1579 } 1580 } 1581 } 1582 } 1583 } 1584 1585 %PrefixSymbols = (); # free memory 1586 1587 if(not $In::Opt{"CheckHeadersOnly"}) { 1588 writeFile($SYS_DUMP_PATH."/debug/symbols.txt", Dumper(\%SysLib_Symbols)); 1589 } 1590 1591 my (%DupLibs, %VersionedLibs) = (); 1592 foreach my $LPath (sort keys(%DupSymbols)) 1593 { # match duplicated libs 1594 # libmenu contains all symbols from libmenuw 1595 my @Syms = keys(%{$SysLib_Symbols{$LPath}}); 1596 next if($#Syms==-1); 1597 if($#Syms+1==keys(%{$DupSymbols{$LPath}})) { 1598 $DupLibs{$LPath} = 1; 1599 } 1600 } 1601 foreach my $Prefix (keys(%PrefixToLib)) 1602 { 1603 my @Libs = keys(%{$PrefixToLib{$Prefix}}); 1604 @Libs = sort {$PrefixToLib{$Prefix}{$b}<=>$PrefixToLib{$Prefix}{$a}} @Libs; 1605 $PrefixToLib{$Prefix} = $Libs[0]; 1606 } 1607 1608 my %PackageFile = (); # to improve results 1609 my %FilePackage = (); 1610 my %LibraryFile = (); 1611 1612 if(0) 1613 { 1614 if($In::Opt{"Debug"}) { 1615 printMsg("INFO", localtime(time)); 1616 } 1617 printMsg("INFO", "Reading info from packages ...\n"); 1618 if(my $Urpmf = getCmdPath("urpmf")) 1619 { # Mandriva, ROSA 1620 my $Out = $TmpDir."/urpmf.out"; 1621 system("urpmf : >\"$Out\""); 1622 open(FILE, $Out); 1623 while(<FILE>) 1624 { 1625 chomp($_); 1626 if(my $M = index($_, ":")) 1627 { 1628 my $Pkg = substr($_, 0, $M); 1629 my $File = substr($_, $M+1); 1630 $PackageFile{$Pkg}{$File} = 1; 1631 $FilePackage{$File} = $Pkg; 1632 } 1633 } 1634 close(FILE); 1635 } 1636 } 1637 1638 if(keys(%FilePackage)) 1639 { 1640 foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) 1641 { 1642 my $LName = getFilename($LPath); 1643 my $LDir = getDirname($LPath); 1644 my $LName_Short = libPart($LName, "name+ext"); 1645 1646 my $Pkg = $FilePackage{$LDir."/".$LName_Short}; 1647 if(not $Pkg) 1648 { 1649 my $RPkg = $FilePackage{$LPath}; 1650 if(defined $PackageFile{$RPkg."-devel"}) { 1651 $Pkg = $RPkg."-devel"; 1652 } 1653 if($RPkg=~s/[\d\.]+\Z//g) 1654 { 1655 if(defined $PackageFile{$RPkg."-devel"}) { 1656 $Pkg = $RPkg."-devel"; 1657 } 1658 } 1659 } 1660 if($Pkg) 1661 { 1662 foreach (keys(%{$PackageFile{$Pkg}})) 1663 { 1664 if(index($_, "/usr/include/")==0) { 1665 $LibraryFile{$LPath}{$_} = 1; 1666 } 1667 } 1668 } 1669 1670 $LName_Short=~s/\.so\Z/.a/; 1671 if($Pkg = $FilePackage{$LDir."/".$LName_Short}) 1672 { # headers for static library 1673 foreach (keys(%{$PackageFile{$Pkg}})) 1674 { 1675 if(index($_, "/usr/include/")==0) { 1676 $LibraryFile{$LPath}{$_} = 1; 1677 } 1678 } 1679 } 1680 } 1681 } 1682 1683 my %HeaderFile_Path = (); 1684 1685 if($In::Opt{"Debug"}) { 1686 printMsg("INFO", localtime(time)); 1687 } 1688 printMsg("INFO", "Reading symbols from headers ...\n"); 1689 foreach my $HPath (@SysHeaders) 1690 { 1691 $HPath = pathFmt($HPath); 1692 if(isElf($HPath)) 1693 { # skip ELF files 1694 next; 1695 } 1696 my $HRelPath = cutPrefix($HPath, $SystemRoot); 1697 my ($HDir, $HName) = sepPath($HRelPath); 1698 if(isNotHeader($HName)) 1699 { # have a wrong extension: .gch, .in 1700 next; 1701 } 1702 if($HName=~/~\Z/) 1703 { # reserved copy 1704 next; 1705 } 1706 if(index($HRelPath, "/_gen")!=-1) 1707 { # telepathy-1.0/telepathy-glib/_gen 1708 # telepathy-1.0/libtelepathy/_gen-tp-constants-deprecated.h 1709 next; 1710 } 1711 if(index($HRelPath, "include/linux/")!=-1) 1712 { # kernel-space headers 1713 next; 1714 } 1715 if(index($HRelPath, "include/asm/")!=-1) 1716 { # asm headers 1717 next; 1718 } 1719 if(index($HRelPath, "/microb-engine/")!=-1) 1720 { # MicroB engine (Maemo 4) 1721 next; 1722 } 1723 if($HRelPath=~/\Wprivate(\W|\Z)/) 1724 { # private directories (include/tcl-private, ...) 1725 next; 1726 } 1727 if(index($HRelPath, "/lib/")!=-1) 1728 { 1729 if(not isHeaderFile($HName)) 1730 { # without or with a wrong extension 1731 # under the /lib directory 1732 next; 1733 } 1734 } 1735 my $Content = readFile($HPath); 1736 $Content=~s/\/\*(.|\n)+?\*\///g; 1737 $Content=~s/\/\/.*?\n//g; # remove comments 1738 $Content=~s/#\s*define[^\n\\]*(\\\n[^\n\\]*)+\n*//g; # remove defines 1739 $Content=~s/#[^\n]*?\n//g; # remove directives 1740 $Content=~s/(\A|\n)class\s+\w+;\n//g; # remove forward declarations 1741 # FIXME: try to add preprocessing stage 1742 foreach my $Symbol (split(/\W+/, $Content)) 1743 { 1744 next if(not $Symbol); 1745 $Symbol_SysHeaders{$Symbol}{$HRelPath} = 1; 1746 $SysHeader_Symbols{$HRelPath}{$Symbol} = 1; 1747 } 1748 $SysHeaderDir_SysHeaders{$HDir}{$HName} = 1; 1749 $HeaderFile_Path{getFilename($HRelPath)}{$HRelPath} = 1; 1750 } 1751 1752 # writeFile($SYS_DUMP_PATH."/debug/headers.txt", Dumper(\%SysHeader_Symbols)); 1753 1754 my %SkipDHeaders = ( 1755 # header files, that should be in the <skip_headers> section 1756 # but should be matched in the algorithm 1757 # MeeGo 1.2 Harmattan 1758 "libtelepathy-qt4" => ["TelepathyQt4/_gen", "client.h", 1759 "TelepathyQt4/*-*", "debug.h", "global.h", 1760 "properties.h", "Channel", "channel.h", "message.h"], 1761 ); 1762 filterFormat(\%SkipDHeaders); 1763 if(not $GroupByHeaders) 1764 { 1765 if($In::Opt{"Debug"}) { 1766 printMsg("INFO", localtime(time)); 1767 } 1768 printMsg("INFO", "Matching symbols ...\n"); 1769 } 1770 1771 foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) 1772 { # matching 1773 my $LName = getFilename($LPath); 1774 } 1775 1776 foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) 1777 { # matching 1778 my $LName = getFilename($LPath); 1779 my $LName_Short = libPart($LName, "name"); 1780 my $LRelPath = cutPrefix($LPath, $SystemRoot); 1781 my $LSName = libPart($LName, "short"); 1782 my $SName = $ShortestNames{$LPath}; 1783 1784 my @TryNames = (); # libX-N.so.M 1785 1786 if(my $Ver = $SysLibVersion{$LName}) 1787 { # libX-N-M 1788 if($LSName."-".$Ver ne $LName_Short) 1789 { 1790 push(@TryNames, $LName_Short."-".$Ver); 1791 #while($Ver=~s/\.\d+\Z//) { # partial versions 1792 # push(@TryNames, $LName_Short."-".$Ver); 1793 #} 1794 } 1795 } 1796 push(@TryNames, $LName_Short); # libX-N 1797 if($LSName ne $LName_Short) 1798 { # libX 1799 push(@TryNames, $LSName); 1800 } 1801 1802 if($LRelPath=~/\/debug\//) 1803 { # debug libs 1804 $Skipped{$LRelPath} = 1; 1805 next; 1806 } 1807 $TotalLibs{$LRelPath} = 1; 1808 $SysLib_SysHeaders{$LRelPath} = (); 1809 1810 my (%SymbolDirs, %SymbolFiles) = (); 1811 1812 foreach my $Symbol (sort {length($b) cmp length($a)} 1813 sort keys(%{$SysLib_Symbols{$LPath}})) 1814 { 1815 if($SysCInfo->{"ignore_symbols"}{$Symbol}) { 1816 next; 1817 } 1818 if(not $DupLibs{$LPath} 1819 and not $VersionedLibs{$LPath} 1820 and keys(%{$SymbolCounter{$Symbol}})>=2 1821 and my $Prefix = getPrefix_S($Symbol)) 1822 { # duplicated symbols 1823 if($PrefixToLib{$Prefix} 1824 and $PrefixToLib{$Prefix} ne $LName 1825 and not $Glibc{$LSName}) { 1826 next; 1827 } 1828 } 1829 if(length($Symbol)<=2) { 1830 next; 1831 } 1832 if($Symbol!~/[A-Z_0-9]/ 1833 and length($Symbol)<10 1834 and keys(%{$Symbol_SysHeaders{$Symbol}})>=3) 1835 { # undistinguished symbols 1836 # FIXME: improve this filter 1837 # signalfd (libc.so) 1838 # regcomp (libpcreposix.so) 1839 next; 1840 } 1841 if($Symbol=~/\A(_M_|_Rb_|_S_)/) 1842 { # _M_insert, _Rb_tree, _S_destroy_c_locale 1843 next; 1844 } 1845 if($Symbol=~/\A[A-Z][a-z]+\Z/) 1846 { # Clone, Initialize, Skip, Unlock, Terminate, Chunk 1847 next; 1848 } 1849 if($Symbol=~/\A[A-Z][A-Z]\Z/) 1850 { # BC, PC, UP, SP 1851 next; 1852 } 1853 if($Symbol=~/_t\Z/) 1854 { # pthread_mutex_t, wchar_t 1855 next; 1856 } 1857 my @SymHeaders = keys(%{$Symbol_SysHeaders{$Symbol}}); 1858 @SymHeaders = sort {lc($a) cmp lc($b)} @SymHeaders; # sort by name 1859 @SymHeaders = sort {length(getDirname($a))<=>length(getDirname($b))} @SymHeaders; # sort by length 1860 if(length($SName)>=3) 1861 { # sort candidate headers by name 1862 @SymHeaders = sort {$b=~/\Q$SName\E/i<=>$a=~/\Q$SName\E/i} @SymHeaders; 1863 } 1864 else 1865 { # libz, libX11 1866 @SymHeaders = sort {$b=~/lib\Q$SName\E/i<=>$a=~/lib\Q$SName\E/i} @SymHeaders; 1867 @SymHeaders = sort {$b=~/\Q$SName\Elib/i<=>$a=~/\Q$SName\Elib/i} @SymHeaders; 1868 } 1869 @SymHeaders = sort {$b=~/\Q$LSName\E/i<=>$a=~/\Q$LSName\E/i} @SymHeaders; 1870 @SymHeaders = sort {$SymbolDirs{getDirname($b)}<=>$SymbolDirs{getDirname($a)}} @SymHeaders; 1871 @SymHeaders = sort {$SymbolFiles{getFilename($b)}<=>$SymbolFiles{getFilename($a)}} @SymHeaders; 1872 foreach my $HRelPath (@SymHeaders) 1873 { 1874 my $HDir = getDirname($HRelPath); 1875 my $HName = getFilename($HRelPath); 1876 1877 if(my $Group = $SymbolGroup{$LRelPath}{$Symbol}) 1878 { 1879 if(not $SysHeader_Symbols{$HRelPath}{$Group}) { 1880 next; 1881 } 1882 } 1883 my $Filter = 0; 1884 foreach (@TryNames) 1885 { 1886 if(my $Filt = $SysInfo->{$_}{"headers"}) 1887 { # search for specified headers 1888 if(not checkList($HRelPath, $Filt)) 1889 { 1890 $Filter = 1; 1891 last; 1892 } 1893 } 1894 if(my $Filt = $SysInfo->{$_}{"skip_headers"}) 1895 { # do NOT search for some headers 1896 if(checkList($HRelPath, $Filt)) 1897 { 1898 $Filter = 1; 1899 last; 1900 } 1901 } 1902 if(my $Filt = $SysInfo->{$_}{"skip_including"}) 1903 { # do NOT search for some headers 1904 if(checkList($HRelPath, $Filt)) 1905 { 1906 $SymbolDirs{$HDir}+=1; 1907 $SymbolFiles{$HName}+=1; 1908 $Filter = 1; 1909 last; 1910 } 1911 } 1912 } 1913 if($Filter) { 1914 next; 1915 } 1916 if(my $Filt = $SysCInfo->{"skip_headers"}) 1917 { # do NOT search for some headers 1918 if(checkList($HRelPath, $Filt)) { 1919 next; 1920 } 1921 } 1922 if(my $Filt = $SysCInfo->{"skip_including"}) 1923 { # do NOT search for some headers 1924 if(checkList($HRelPath, $Filt)) { 1925 next; 1926 } 1927 } 1928 1929 #if(defined $LibraryFile{$LRelPath}) 1930 #{ # skip wrongly matched headers 1931 # if(not defined $LibraryFile{$LRelPath}{$HRelPath}) 1932 # { 1933 # next; 1934 # } 1935 #} 1936 1937 $SysLib_SysHeaders{$LRelPath}{$HRelPath} = $Symbol; 1938 1939 $SysHeaderDir_Used{$HDir}{$LName_Short} = 1; 1940 $SysHeaderDir_Used{getDirname($HDir)}{$LName_Short} = 1; 1941 1942 $SymbolDirs{$HDir} += 1; 1943 $SymbolFiles{$HName} +=1 ; 1944 1945 # select one header for one symbol 1946 last; 1947 } 1948 } 1949 1950 if(keys(%{$SysLib_Symbols{$LPath}}) 1951 and not $SysInfo->{$_}{"headers"}) 1952 { # try to match by name of the header 1953 if(length($SName)>=3) 1954 { 1955 my @Paths = (); 1956 foreach my $Path (keys(%{$HeaderFile_Path{$SName.".h"}}), keys(%{$HeaderFile_Path{$LSName.".h"}})) 1957 { 1958 my $Dir = getDirname($Path); 1959 if(defined $SymbolDirs{$Dir} or $Dir eq "/usr/include") { 1960 push(@Paths, $Path); 1961 } 1962 } 1963 if($#Paths==0) 1964 { 1965 my $Path = $Paths[0]; 1966 if(not defined $SysLib_SysHeaders{$LRelPath}{$Path}) { 1967 $SysLib_SysHeaders{$LRelPath}{$Path} = "by name ($LSName)"; 1968 } 1969 } 1970 } 1971 } 1972 1973 if(not keys(%{$SysLib_SysHeaders{$LRelPath}})) 1974 { 1975 foreach (@TryNames) 1976 { 1977 if(my $List = $SysInfo->{$_}{"headers"}) 1978 { 1979 foreach my $HName (@{$List}) 1980 { 1981 next if($HName=~/[\*\/\\]/); 1982 if(my $HPath = selectSystemHeader($HName, 1)) 1983 { 1984 my $HRelPath = cutPrefix($HPath, $SystemRoot); 1985 $SysLib_SysHeaders{$LRelPath}{$HRelPath} = "by descriptor"; 1986 } 1987 } 1988 } 1989 } 1990 } 1991 1992 if(not keys(%{$SysLib_SysHeaders{$LRelPath}})) { 1993 $Failed{$LRelPath} = 1; 1994 } 1995 } 1996 1997 if(not $GroupByHeaders) 1998 { # matching results 1999 writeFile($SYS_DUMP_PATH."/debug/match.txt", Dumper(\%SysLib_SysHeaders)); 2000 writeFile($SYS_DUMP_PATH."/debug/skipped.txt", join("\n", sort keys(%Skipped))); 2001 writeFile($SYS_DUMP_PATH."/debug/failed.txt", join("\n", sort keys(%Failed))); 2002 } 2003 (%SysLib_Symbols, %SymbolGroup, %Symbol_SysHeaders, %SysHeader_Symbols) = (); # free memory 2004 if($GroupByHeaders) 2005 { 2006 if($SysDesc{"Image"} and not $In::Opt{"CheckHeadersOnly"}) { 2007 @SysHeaders = keys(%{$SysLib_SysHeaders{$SysDesc{"Image"}}}); 2008 } 2009 %SysLib_SysHeaders = (); 2010 foreach my $Path (@SysHeaders) 2011 { 2012 if(my $Skip = $SysCInfo->{"skip_headers"}) 2013 { # do NOT search for some headers 2014 if(checkList($Path, $Skip)) { 2015 next; 2016 } 2017 } 2018 if(my $Skip = $SysCInfo->{"skip_including"}) 2019 { # do NOT search for some headers 2020 if(checkList($Path, $Skip)) { 2021 next; 2022 } 2023 } 2024 $SysLib_SysHeaders{$Path}{$Path} = 1; 2025 } 2026 if($In::Opt{"CheckHeadersOnly"}) { 2027 writeFile($SYS_DUMP_PATH."/mode.txt", "headers-only"); 2028 } 2029 else { 2030 writeFile($SYS_DUMP_PATH."/mode.txt", "group-by-headers"); 2031 } 2032 } 2033 @SysHeaders = (); # clear memory 2034 2035 if($In::Opt{"Debug"}) { 2036 printMsg("INFO", localtime(time)); 2037 } 2038 printMsg("INFO", "Generating XML descriptors ..."); 2039 my %Generated = (); 2040 my %CxxIncompat_L = (); 2041 foreach my $LRelPath (keys(%SysLib_SysHeaders)) 2042 { 2043 my $LName = getFilename($LRelPath); 2044 my $DPath = $SYS_DUMP_PATH."/descriptors/$LName.xml"; 2045 unlink($DPath); 2046 if(my @LibHeaders = keys(%{$SysLib_SysHeaders{$LRelPath}})) 2047 { 2048 my $LSName = libPart($LName, "short"); 2049 my $LName_Short = libPart($LName, "name"); 2050 my $LName_Shortest = libPart($LName, "shortest"); 2051 if($GroupByHeaders) 2052 { # header short name 2053 $LSName = $LName; 2054 $LSName=~s/\.(.+?)\Z//; 2055 } 2056 2057 my (%DirsHeaders, %Includes, %MainDirs) = (); 2058 foreach my $HRelPath (@LibHeaders) 2059 { 2060 my $Dir = getDirname($HRelPath); 2061 $DirsHeaders{$Dir}{$HRelPath} = 1; 2062 2063 if($Dir=~/\/\Q$LName_Shortest\E(\/|\Z)/i 2064 or $Dir=~/\/\Q$LName_Short\E(\/|\Z)/i) 2065 { 2066 if(getFilename($Dir) ne "include") 2067 { # except /usr/include 2068 $MainDirs{$Dir} += 1; 2069 } 2070 } 2071 } 2072 2073 if($#LibHeaders==0) 2074 { # one header at all 2075 $Includes{$LibHeaders[0]} = 1; 2076 } 2077 else 2078 { 2079 foreach my $Dir (keys(%DirsHeaders)) 2080 { 2081 if(keys(%MainDirs) and not defined $MainDirs{$Dir}) 2082 { # search in /X/ dir for libX headers 2083 if(getFilename($Dir) ne "include") 2084 { # except /usr/include 2085 next; 2086 } 2087 } 2088 my $DirPart = 0; 2089 my $TotalHeaders = keys(%{$SysHeaderDir_SysHeaders{$Dir}}); 2090 if($TotalHeaders) { 2091 $DirPart = (keys(%{$DirsHeaders{$Dir}})*100)/$TotalHeaders; 2092 } 2093 my $Neighbourhoods = keys(%{$SysHeaderDir_Used{$Dir}}); 2094 if($Neighbourhoods==1) 2095 { # one lib in this directory 2096 if(getFilename($Dir) ne "include" 2097 and $DirPart>=5) 2098 { # complete directory 2099 $Includes{$Dir} = 1; 2100 } 2101 else 2102 { # list of headers 2103 foreach (keys(%{$DirsHeaders{$Dir}})) { 2104 $Includes{$_} = 1; 2105 } 2106 } 2107 } 2108 elsif((keys(%{$DirsHeaders{$Dir}})*100)/($#LibHeaders+1)>5) 2109 { # remove 5% divergence 2110 if(getFilename($Dir) ne "include" 2111 and $DirPart>=50) 2112 { # complete directory if more than 50% 2113 $Includes{$Dir} = 1; 2114 } 2115 else 2116 { # list of headers 2117 foreach (keys(%{$DirsHeaders{$Dir}})) { 2118 $Includes{$_} = 1; 2119 } 2120 } 2121 } 2122 else 2123 { # noise 2124 foreach (keys(%{$DirsHeaders{$Dir}})) 2125 { # NOTE: /usr/include/libX.h 2126 if(/\Q$LName_Shortest\E/i) { 2127 $Includes{$_} = 1; 2128 } 2129 } 2130 } 2131 } 2132 } 2133 if($GroupByHeaders) 2134 { # one header in one ABI dump 2135 %Includes = ($LibHeaders[0] => 1); 2136 } 2137 my $LVersion = $SysLibVersion{$LName}; 2138 if($LVersion) 2139 { # append by system name 2140 $LVersion .= "-".$SysDesc{"Name"}; 2141 } 2142 else { 2143 $LVersion = $SysDesc{"Name"}; 2144 } 2145 my @Content = ("<version>\n $LVersion\n</version>"); 2146 2147 my @IncHeaders = sort keys(%Includes); 2148 2149 # sort files up 2150 @IncHeaders = sort {$b=~/\.h\Z/<=>$a=~/\.h\Z/} @IncHeaders; 2151 2152 # sort by name 2153 @IncHeaders = sort {sortHeaders($a, $b)} @IncHeaders; 2154 2155 # sort by library name 2156 sortByWord(\@IncHeaders, libPart($LName, "shortest")); 2157 2158 if(isAbsPath($IncHeaders[0]) or -f $IncHeaders[0]) { 2159 push(@Content, "<headers>\n ".join("\n ", @IncHeaders)."\n</headers>"); 2160 } 2161 else { 2162 push(@Content, "<headers>\n {RELPATH}/".join("\n {RELPATH}/", @IncHeaders)."\n</headers>"); 2163 } 2164 if($GroupByHeaders) 2165 { 2166 if($SysDesc{"Image"}) { 2167 push(@Content, "<libs>\n ".$SysDesc{"Image"}."\n</libs>"); 2168 } 2169 } 2170 else 2171 { 2172 if(isAbsPath($LRelPath) or -f $LRelPath) { 2173 push(@Content, "<libs>\n $LRelPath\n</libs>"); 2174 } 2175 else { 2176 push(@Content, "<libs>\n {RELPATH}/$LRelPath\n</libs>"); 2177 } 2178 } 2179 2180 # system 2181 if(my @SearchHeaders = sort keys(%{$SysDesc{"SearchHeaders"}})) { 2182 push(@Content, "<search_headers>\n ".join("\n ", @SearchHeaders)."\n</search_headers>"); 2183 } 2184 if(my @SearchLibs = sort keys(%{$SysDesc{"SearchLibs"}})) { 2185 push(@Content, "<search_libs>\n ".join("\n ", @SearchLibs)."\n</search_libs>"); 2186 } 2187 if(my @Tools = sort keys(%{$SysDesc{"Tools"}})) { 2188 push(@Content, "<tools>\n ".join("\n ", @Tools)."\n</tools>"); 2189 } 2190 if(my $Prefix = $SysDesc{"CrossPrefix"}) { 2191 push(@Content, "<cross_prefix>\n $Prefix\n</cross_prefix>"); 2192 } 2193 2194 # library 2195 my (@Skip, @SkipInc, @AddIncPath, @SkipIncPath, 2196 @SkipTypes, @SkipSymb, @Preamble, @Defines, @CompilerOpts) = (); 2197 2198 my @TryNames = (); 2199 if(my $Ver = $SysLibVersion{$LName}) 2200 { 2201 if($LSName."-".$Ver ne $LName_Short) { 2202 push(@TryNames, $LName_Short."-".$Ver); 2203 } 2204 } 2205 push(@TryNames, $LName_Short); 2206 if($LSName ne $LName_Short) { 2207 push(@TryNames, $LSName); 2208 } 2209 2210 # common 2211 if(my $List = $SysCInfo->{"include_preamble"}) { 2212 push(@Preamble, @{$List}); 2213 } 2214 if(my $List = $SysCInfo->{"skip_headers"}) { 2215 @Skip = (@Skip, @{$List}); 2216 } 2217 if(my $List = $SysCInfo->{"skip_including"}) { 2218 @SkipInc = (@SkipInc, @{$List}); 2219 } 2220 if(my $List = $SysCInfo->{"skip_symbols"}) { 2221 push(@SkipSymb, @{$List}); 2222 } 2223 if(my $List = $SysCInfo->{"gcc_options"}) { 2224 push(@CompilerOpts, @{$List}); 2225 } 2226 if($SysCInfo->{"defines"}) { 2227 push(@Defines, $SysCInfo->{"defines"}); 2228 } 2229 2230 # particular 2231 foreach (@TryNames) 2232 { 2233 if(my $List = $SysInfo->{$_}{"include_preamble"}) { 2234 push(@Preamble, @{$List}); 2235 } 2236 if(my $List = $SysInfo->{$_}{"skip_headers"}) { 2237 @Skip = (@Skip, @{$List}); 2238 } 2239 if(my $List = $SysInfo->{$_}{"skip_including"}) { 2240 @SkipInc = (@SkipInc, @{$List}); 2241 } 2242 if(my $List = $SysInfo->{$_}{"add_include_paths"}) { 2243 @AddIncPath = (@AddIncPath, @{$List}); 2244 } 2245 if(my $List = $SysInfo->{$_}{"skip_include_paths"}) { 2246 @SkipIncPath = (@SkipIncPath, @{$List}); 2247 } 2248 if(my $List = $SysInfo->{$_}{"skip_symbols"}) { 2249 push(@SkipSymb, @{$List}); 2250 } 2251 if(my $List = $SysInfo->{$_}{"skip_types"}) { 2252 @SkipTypes = (@SkipTypes, @{$List}); 2253 } 2254 if(my $List = $SysInfo->{$_}{"gcc_options"}) { 2255 push(@CompilerOpts, @{$List}); 2256 } 2257 if(my $List = $SysInfo->{$_}{"defines"}) { 2258 push(@Defines, $List); 2259 } 2260 if($SysInfo->{$_}{"cxx_incompatible"}) { 2261 $CxxIncompat_L{$LName} = 1; 2262 } 2263 } 2264 2265 # common other 2266 if($LSName=~/\AlibX\w+\Z/) 2267 { # add Xlib.h for libXt, libXaw, libXext and others 2268 push(@Preamble, "Xlib.h", "X11/Intrinsic.h"); 2269 } 2270 if($SkipDHeaders{$LSName}) { 2271 @SkipInc = (@SkipInc, @{$SkipDHeaders{$LSName}}); 2272 } 2273 if($SysDesc{"Defines"}) { 2274 push(@Defines, $SysDesc{"Defines"}); 2275 } 2276 2277 # add sections 2278 if(@Preamble) { 2279 push(@Content, "<include_preamble>\n ".join("\n ", @Preamble)."\n</include_preamble>"); 2280 } 2281 if(@Skip) { 2282 push(@Content, "<skip_headers>\n ".join("\n ", @Skip)."\n</skip_headers>"); 2283 } 2284 if(@SkipInc) { 2285 push(@Content, "<skip_including>\n ".join("\n ", @SkipInc)."\n</skip_including>"); 2286 } 2287 if(@AddIncPath) { 2288 push(@Content, "<add_include_paths>\n ".join("\n ", @AddIncPath)."\n</add_include_paths>"); 2289 } 2290 if(@SkipIncPath) { 2291 push(@Content, "<skip_include_paths>\n ".join("\n ", @SkipIncPath)."\n</skip_include_paths>"); 2292 } 2293 if(@SkipSymb) { 2294 push(@Content, "<skip_symbols>\n ".join("\n ", @SkipSymb)."\n</skip_symbols>"); 2295 } 2296 if(@SkipTypes) { 2297 push(@Content, "<skip_types>\n ".join("\n ", @SkipTypes)."\n</skip_types>"); 2298 } 2299 if(@CompilerOpts) { 2300 push(@Content, "<gcc_options>\n ".join("\n ", @CompilerOpts)."\n</gcc_options>"); 2301 } 2302 if(@Defines) { 2303 push(@Content, "<defines>\n ".join("\n ", @Defines)."\n</defines>"); 2304 } 2305 2306 writeFile($DPath, join("\n\n", @Content)); 2307 $Generated{$LRelPath} = 1; 2308 2309 # save header files to create visual diff later 2310 my $HSDir = $SYS_DUMP_PATH."/headers/".$LName; 2311 rmtree($HSDir); 2312 mkpath($HSDir); 2313 foreach my $H_P (@IncHeaders) 2314 { 2315 if(-f $H_P) { 2316 copy($H_P, $HSDir); 2317 } 2318 } 2319 } 2320 } 2321 printMsg("INFO", "Created descriptors: ".keys(%Generated)." ($SYS_DUMP_PATH/descriptors/)\n"); 2322 2323 if($In::Opt{"Debug"}) { 2324 printMsg("INFO", localtime(time)); 2325 } 2326 printMsg("INFO", "Dumping ABIs:"); 2327 my %Dumped = (); 2328 my @Descriptors = cmdFind($SYS_DUMP_PATH."/descriptors","f","*.xml","1"); 2329 if(-d $SYS_DUMP_PATH."/descriptors" and $#Descriptors==-1) { 2330 printMsg("ERROR", "internal problem with \'find\' utility"); 2331 } 2332 foreach my $DPath (sort {lc($a) cmp lc($b)} @Descriptors) 2333 { 2334 my $DName = getFilename($DPath); 2335 my $LName = ""; 2336 if($DName=~/\A(.+).xml\Z/) { 2337 $LName = $1; 2338 } 2339 else { 2340 next; 2341 } 2342 if(not isTargetLib($LName) 2343 and not isTargetLib($LibSoname{$LName})) { 2344 next; 2345 } 2346 $DPath = cutPrefix($DPath, $In::Opt{"OrigDir"}); 2347 my $ACC_dump = "perl $0"; 2348 if($GroupByHeaders) 2349 { # header name is going here 2350 $ACC_dump .= " -l $LName"; 2351 } 2352 else { 2353 $ACC_dump .= " -l ".libPart($LName, "name"); 2354 } 2355 $ACC_dump .= " -dump \"$DPath\""; 2356 if($SystemRoot) 2357 { 2358 $ACC_dump .= " -relpath \"$SystemRoot\""; 2359 $ACC_dump .= " -sysroot \"$SystemRoot\""; 2360 } 2361 my $DumpPath = "$SYS_DUMP_PATH/abi_dumps/$LName.abi"; 2362 $ACC_dump .= " -dump-path \"$DumpPath\""; 2363 my $LogPath = "$SYS_DUMP_PATH/logs/$LName.txt"; 2364 unlink($LogPath); 2365 $ACC_dump .= " -log-path \"$LogPath\""; 2366 if($In::Opt{"CrossGcc"}) { 2367 $ACC_dump .= " -cross-gcc \"".$In::Opt{"CrossGcc"}."\""; 2368 } 2369 if($In::Opt{"CheckHeadersOnly"}) { 2370 $ACC_dump .= " -headers-only"; 2371 } 2372 if($In::Opt{"UseStaticLibs"}) { 2373 $ACC_dump .= " -static-libs"; 2374 } 2375 if($GroupByHeaders) { 2376 $ACC_dump .= " -header $LName"; 2377 } 2378 if($In::Opt{"NoStdInc"} 2379 or $In::Opt{"Target"}=~/windows|symbian/) 2380 { # 1. user-defined 2381 # 2. windows/minGW 2382 # 3. symbian/GCC 2383 $ACC_dump .= " -nostdinc"; 2384 } 2385 if($In::Opt{"CxxIncompat"} or $CxxIncompat_L{$LName}) { 2386 $ACC_dump .= " -cxx-incompatible"; 2387 } 2388 if($In::Opt{"SkipUnidentified"}) { 2389 $ACC_dump .= " -skip-unidentified"; 2390 } 2391 if($In::Opt{"Quiet"}) 2392 { # quiet mode 2393 $ACC_dump .= " -quiet"; 2394 } 2395 if($In::Opt{"LogMode"} eq "n") { 2396 $ACC_dump .= " -logging-mode n"; 2397 } 2398 elsif($In::Opt{"Quiet"}) { 2399 $ACC_dump .= " -logging-mode a"; 2400 } 2401 if($In::Opt{"Debug"}) 2402 { # debug mode 2403 $ACC_dump .= " -debug"; 2404 printMsg("INFO", "$ACC_dump"); 2405 } 2406 printMsg("INFO_C", "Dumping $LName: "); 2407 system($ACC_dump." 1>$TmpDir/null 2>$TmpDir/$LName.stderr"); 2408 my $ErrCode = $?; 2409 appendFile("$SYS_DUMP_PATH/logs/$LName.txt", "The ACC parameters:\n $ACC_dump\n"); 2410 my $ErrCont = readFile("$TmpDir/$LName.stderr"); 2411 if($ErrCont) { 2412 appendFile("$SYS_DUMP_PATH/logs/$LName.txt", $ErrCont); 2413 } 2414 2415 if(filterError($ErrCont)) 2416 { 2417 if(getCodeError($ErrCode>>8) eq "Invalid_Dump") { 2418 printMsg("INFO", "Empty"); 2419 } 2420 else { 2421 printMsg("INFO", "Errors (\'$SYS_DUMP_PATH/logs/$LName.txt\')"); 2422 } 2423 } 2424 elsif(not -f $DumpPath) { 2425 printMsg("INFO", "Failed (\'$SYS_DUMP_PATH/logs/$LName.txt\')"); 2426 } 2427 else 2428 { 2429 $Dumped{$LName}=1; 2430 printMsg("INFO", "Ok"); 2431 } 2432 } 2433 printMsg("INFO", "\n"); 2434 if(not $GroupByHeaders) 2435 { # general mode 2436 printMsg("INFO", "Total libraries: ".keys(%TotalLibs)); 2437 printMsg("INFO", "Skipped libraries: ".keys(%Skipped)." ($SYS_DUMP_PATH/skipped.txt)"); 2438 printMsg("INFO", "Failed to find headers: ".keys(%Failed)." ($SYS_DUMP_PATH/failed.txt)"); 2439 } 2440 printMsg("INFO", "Dumped ABIs: ".keys(%Dumped)." ($SYS_DUMP_PATH/abi_dumps/)"); 2441 printMsg("INFO", "The ".$SysDesc{"Name"}." system ABI has been dumped to:\n $SYS_DUMP_PATH"); 2442} 2443 2444sub filterError($) 2445{ 2446 my $Error = $_[0]; 2447 2448 if(not $Error) { 2449 return undef; 2450 } 2451 2452 my @Err = (); 2453 foreach my $L (split(/\n/, $Error)) 2454 { 2455 if($L!~/warning:/) { 2456 push(@Err, $L); 2457 } 2458 } 2459 2460 return join("\n", @Err); 2461} 2462 2463sub getBinVer($) 2464{ 2465 my $Path = $_[0]; 2466 if($In::Opt{"Target"} eq "windows" 2467 and $Path=~/\.dll\Z/) 2468 { # get version of DLL using "sigcheck" 2469 my $Sigcheck = getCmdPath("sigcheck"); 2470 if(not $Sigcheck) { 2471 return undef; 2472 } 2473 my $TmpDir = $In::Opt{"Tmp"}; 2474 my $VInfo = `$Sigcheck -nobanner -n $Path 2>$TmpDir/null`; 2475 $VInfo=~s/\s*\(.*\)\s*//; 2476 chomp($VInfo); 2477 2478 if($VInfo eq "n/a") { 2479 $VInfo = uc($VInfo); 2480 } 2481 2482 return $VInfo; 2483 } 2484 return undef; 2485} 2486 2487return 1; 2488