• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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###########################################################################
24use strict;
25use File::Temp qw(tempdir);
26use Cwd qw(abs_path cwd);
27use Fcntl;
28
29my ($Debug, $Quiet, $LogMode, $CheckHeadersOnly, $SystemRoot, $GCC_PATH,
30$CrossPrefix, $TargetSysInfo, $TargetLibraryName, $CrossGcc, $UseStaticLibs,
31$NoStdInc, $CxxIncompat, $SkipUnidentified, $OStarget, $BinaryOnly,
32$SourceOnly, $DisableConstantsCheck);
33
34my $OSgroup = get_OSgroup();
35my $TMP_DIR = tempdir(CLEANUP=>1);
36my $ORIG_DIR = cwd();
37my $LIB_EXT = getLIB_EXT($OSgroup);
38
39my %SysDescriptor;
40my %Cache;
41my %NonPrefix;
42
43sub 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
931sub printVer($)
932{
933    if($_[0] eq "") {
934        return 0;
935    }
936    return $_[0];
937}
938
939sub 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
948sub problem_title($)
949{
950    if($_[0]==1)  {
951        return "1 change";
952    }
953    else  {
954        return $_[0]." changes";
955    }
956}
957
958sub warning_title($)
959{
960    if($_[0]==1)  {
961        return "1 warning";
962    }
963    else  {
964        return $_[0]." warnings";
965    }
966}
967
968sub 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
1052sub 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
1083sub 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
1113sub 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
1127sub 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
1187sub 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
1252sub 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
1275sub 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
1284sub 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
2580sub 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
2599return 1;
2600