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