;
close(FILE);
return $Line;
}
sub readAttributes($$)
{
my ($Path, $Num) = @_;
return () if(not $Path or not -f $Path);
my %Attributes = ();
if(readLineNum($Path, $Num)=~//)
{
foreach my $AttrVal (split(/;/, $1))
{
if($AttrVal=~/(.+):(.+)/)
{
my ($Name, $Value) = ($1, $2);
$Attributes{$Name} = $Value;
}
}
}
return \%Attributes;
}
sub is_abs($) {
return ($_[0]=~/\A(\/|\w+:[\/\\])/);
}
sub get_abs_path($)
{ # abs_path() should NOT be called for absolute inputs
# because it can change them
my $Path = $_[0];
if(not is_abs($Path)) {
$Path = abs_path($Path);
}
return path_format($Path, $OSgroup);
}
sub get_OSgroup()
{
my $N = $Config{"osname"};
if($N=~/macos|darwin|rhapsody/i) {
return "macos";
}
elsif($N=~/freebsd|openbsd|netbsd/i) {
return "bsd";
}
elsif($N=~/haiku|beos/i) {
return "beos";
}
elsif($N=~/symbian|epoc/i) {
return "symbian";
}
elsif($N=~/win/i) {
return "windows";
}
else {
return $N;
}
}
sub getGccVersion($)
{
my $LibVersion = $_[0];
if($GCC_VERSION{$LibVersion})
{ # dump version
return $GCC_VERSION{$LibVersion};
}
elsif($UsedDump{$LibVersion}{"V"})
{ # old-version dumps
return "unknown";
}
my $GccVersion = get_dumpversion($GCC_PATH); # host version
if(not $GccVersion) {
return "unknown";
}
return $GccVersion;
}
sub showArch($)
{
my $Arch = $_[0];
if($Arch eq "arm"
or $Arch eq "mips") {
return uc($Arch);
}
return $Arch;
}
sub getArch($)
{
my $LibVersion = $_[0];
if($TargetArch) {
return $TargetArch;
}
elsif($CPU_ARCH{$LibVersion})
{ # dump
return $CPU_ARCH{$LibVersion};
}
elsif($UsedDump{$LibVersion}{"V"})
{ # old-version dumps
return "unknown";
}
return getArch_GCC($LibVersion);
}
sub get_Report_Title($)
{
my $Level = $_[0];
my $ArchInfo = " on ".showArch(getArch(1))."";
if(getArch(1) ne getArch(2)
or getArch(1) eq "unknown"
or $Level eq "Source")
{ # don't show architecture in the header
$ArchInfo="";
}
my $Title = "";
if($Level eq "Source") {
$Title .= "Source compatibility";
}
elsif($Level eq "Binary") {
$Title .= "Binary compatibility";
}
else {
$Title .= "API compatibility";
}
my $V1 = $Descriptor{1}{"Version"};
my $V2 = $Descriptor{2}{"Version"};
if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"})
{
my $M1 = $UsedDump{1}{"M"};
my $M2 = $UsedDump{2}{"M"};
my $M1S = $M1;
my $M2S = $M2;
$M1S=~s/(\.so|\.ko)\..+/$1/ig;
$M2S=~s/(\.so|\.ko)\..+/$1/ig;
if($M1S eq $M2S
and $V1 ne "X" and $V2 ne "Y")
{
$Title .= " report for the $M1S $TargetComponent";
$Title .= " between ".$V1." and ".$V2." versions";
}
else
{
$Title .= " report between $M1 (".$V1.")";
$Title .= " and $M2 (".$V2.") objects";
}
}
else
{
$Title .= " report for the $TargetTitle $TargetComponent";
$Title .= " between ".$V1." and ".$V2." versions";
}
$Title .= $ArchInfo;
if($AppPath) {
$Title .= " (relating to the portability of application ".get_filename($AppPath).")";
}
$Title = "".$Title."
\n";
return $Title;
}
sub get_CheckedHeaders($)
{
my $LibVersion = $_[0];
my @Headers = ();
foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
{
my $File = get_filename($Path);
if(not is_target_header($File, $LibVersion)) {
next;
}
if(skipHeader($File, $LibVersion)) {
next;
}
push(@Headers, $Path);
}
return @Headers;
}
sub get_SourceInfo()
{
my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", "");
if(my @Headers = get_CheckedHeaders(1))
{
$CheckedHeaders = "";
if($OldStyle) {
$CheckedHeaders .= "Header Files (".($#Headers+1).")
";
}
else {
$CheckedHeaders .= "Header Files ".($#Headers+1)."
";
}
$CheckedHeaders .= "
\n\n";
foreach my $Header_Path (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
{
my $Identity = $Registered_Headers{1}{$Header_Path}{"Identity"};
my $Name = get_filename($Identity);
my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
$CheckedHeaders .= $Name.$Comment."
\n";
}
$CheckedHeaders .= "
\n";
$CheckedHeaders .= "
$TOP_REF
\n";
}
if(my @Sources = keys(%{$Registered_Sources{1}}))
{
$CheckedSources = "";
if($OldStyle) {
$CheckedSources .= "Source Files (".($#Sources+1).")
";
}
else {
$CheckedSources .= "Source Files ".($#Sources+1)."
";
}
$CheckedSources .= "
\n\n";
foreach my $Header_Path (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
{
my $Identity = $Registered_Sources{1}{$Header_Path}{"Identity"};
my $Name = get_filename($Identity);
my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
$CheckedSources .= $Name.$Comment."
\n";
}
$CheckedSources .= "
\n";
$CheckedSources .= "
$TOP_REF
\n";
}
if(not $CheckHeadersOnly)
{
$CheckedLibs = "";
if($OldStyle) {
$CheckedLibs .= "".get_ObjTitle()." (".keys(%{$Library_Symbol{1}}).")
";
}
else {
$CheckedLibs .= "".get_ObjTitle()." ".keys(%{$Library_Symbol{1}})."
";
}
$CheckedLibs .= "
\n\n";
foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$Library_Symbol{1}})) {
$CheckedLibs .= $Library."
\n";
}
$CheckedLibs .= "
\n";
$CheckedLibs .= "
$TOP_REF
\n";
}
return $CheckedHeaders.$CheckedSources.$CheckedLibs;
}
sub get_ObjTitle()
{
if(defined $UsedDump{1}{"DWARF"}) {
return "Objects";
}
else {
return ucfirst($SLIB_TYPE)." Libraries";
}
}
sub get_TypeProblems_Count($$)
{
my ($TargetSeverity, $Level) = @_;
my $Type_Problems_Count = 0;
foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
{
my %Kinds_Target = ();
foreach my $Kind (keys(%{$TypeChanges{$Level}{$Type_Name}}))
{
foreach my $Location (keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
{
my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location}{"Target"};
my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
if($Severity ne $TargetSeverity) {
next;
}
if($Kinds_Target{$Kind}{$Target}) {
next;
}
$Kinds_Target{$Kind}{$Target} = 1;
$Type_Problems_Count += 1;
}
}
}
return $Type_Problems_Count;
}
sub get_Summary($)
{
my $Level = $_[0];
my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High,
$C_Problems_Low, $T_Problems_Medium, $T_Problems_Low, $I_Other, $T_Other, $C_Other) = (0,0,0,0,0,0,0,0,0,0,0,0);
%{$RESULT{$Level}} = (
"Problems"=>0,
"Warnings"=>0,
"Affected"=>0 );
# check rules
foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
{
foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
{
if(not defined $CompatRules{$Level}{$Kind})
{ # unknown rule
if(not $UnknownRules{$Level}{$Kind})
{ # only one warning
printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
$UnknownRules{$Level}{$Kind}=1;
}
delete($CompatProblems{$Level}{$Interface}{$Kind});
}
}
}
foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}}))
{
foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
{
if(not defined $CompatRules{$Level}{$Kind})
{ # unknown rule
if(not $UnknownRules{$Level}{$Kind})
{ # only one warning
printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
$UnknownRules{$Level}{$Kind}=1;
}
delete($CompatProblems_Constants{$Level}{$Constant}{$Kind});
}
}
}
foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
{
foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
{
if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
{
foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
{
my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
if($Kind eq "Added_Symbol") {
$Added += 1;
}
elsif($Kind eq "Removed_Symbol")
{
$Removed += 1;
$TotalAffected{$Level}{$Interface} = $Severity;
}
else
{
if($Severity eq "Safe") {
$I_Other += 1;
}
elsif($Severity eq "High") {
$I_Problems_High += 1;
}
elsif($Severity eq "Medium") {
$I_Problems_Medium += 1;
}
elsif($Severity eq "Low") {
$I_Problems_Low += 1;
}
if(($Severity ne "Low" or $StrictCompat)
and $Severity ne "Safe") {
$TotalAffected{$Level}{$Interface} = $Severity;
}
}
}
}
}
}
my %MethodTypeIndex = ();
foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
{
my @Kinds = sort keys(%{$CompatProblems{$Level}{$Interface}});
foreach my $Kind (@Kinds)
{
if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
{
my @Locs = sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}});
foreach my $Location (@Locs)
{
my $Type_Name = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"};
my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"};
if(defined $MethodTypeIndex{$Interface}{$Type_Name}{$Kind}{$Target})
{ # one location for one type and target
next;
}
$MethodTypeIndex{$Interface}{$Type_Name}{$Kind}{$Target} = 1;
$TypeChanges{$Level}{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location};
my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
if(($Severity ne "Low" or $StrictCompat)
and $Severity ne "Safe")
{
if(my $Sev = $TotalAffected{$Level}{$Interface})
{
if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
$TotalAffected{$Level}{$Interface} = $Severity;
}
}
else {
$TotalAffected{$Level}{$Interface} = $Severity;
}
}
}
}
}
}
$T_Problems_High = get_TypeProblems_Count("High", $Level);
$T_Problems_Medium = get_TypeProblems_Count("Medium", $Level);
$T_Problems_Low = get_TypeProblems_Count("Low", $Level);
$T_Other = get_TypeProblems_Count("Safe", $Level);
# changed and removed public symbols
my $SCount = keys(%{$CheckedSymbols{$Level}});
if($ExtendedCheck)
{ # don't count external_func_0 for constants
$SCount-=1;
}
if($SCount)
{
my %Weight = (
"High" => 100,
"Medium" => 50,
"Low" => 25
);
foreach (keys(%{$TotalAffected{$Level}})) {
$RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
}
$RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
}
else {
$RESULT{$Level}{"Affected"} = 0;
}
$RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"});
if($RESULT{$Level}{"Affected"}>=100) {
$RESULT{$Level}{"Affected"} = 100;
}
$RESULT{$Level}{"Problems"} += $Removed;
$RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High;
$RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
if($StrictCompat) {
$RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low;
}
else {
$RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low;
}
foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
{
foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
{
my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
if($Severity eq "Safe")
{
$C_Other+=1;
}
elsif($Severity eq "Low")
{
$C_Problems_Low+=1;
}
}
}
if($C_Problems_Low)
{
if($StrictCompat) {
$RESULT{$Level}{"Problems"} += $C_Problems_Low;
}
else {
$RESULT{$Level}{"Warnings"} += $C_Problems_Low;
}
}
if($RESULT{$Level}{"Problems"}
and $RESULT{$Level}{"Affected"}) {
$RESULT{$Level}{"Verdict"} = "incompatible";
}
else {
$RESULT{$Level}{"Verdict"} = "compatible";
}
my $TotalTypes = keys(%{$CheckedTypes{$Level}});
if(not $TotalTypes)
{ # list all the types
$TotalTypes = keys(%{$TName_Tid{1}});
}
my ($Arch1, $Arch2) = (getArch(1), getArch(2));
my ($GccV1, $GccV2) = (getGccVersion(1), getGccVersion(2));
my ($ClangV1, $ClangV2) = ($CLANG_VERSION{1}, $CLANG_VERSION{2});
my ($TestInfo, $TestResults, $Problem_Summary) = ();
if($ReportFormat eq "xml")
{ # XML
# test info
$TestInfo .= " $TargetLibraryName\n";
$TestInfo .= " \n";
$TestInfo .= " ".$Descriptor{1}{"Version"}."\n";
$TestInfo .= " $Arch1\n";
if($GccV1) {
$TestInfo .= " $GccV1\n";
}
elsif($ClangV1) {
$TestInfo .= " $ClangV1\n";
}
$TestInfo .= " \n";
$TestInfo .= " \n";
$TestInfo .= " ".$Descriptor{2}{"Version"}."\n";
$TestInfo .= " $Arch2\n";
if($GccV2) {
$TestInfo .= " $GccV2\n";
}
elsif($ClangV2) {
$TestInfo .= " $ClangV2\n";
}
$TestInfo .= " \n";
$TestInfo = "\n".$TestInfo."\n\n";
# test results
if(my @Headers = keys(%{$Registered_Headers{1}}))
{
$TestResults .= " \n";
foreach my $Name (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
{
my $Identity = $Registered_Headers{1}{$Name}{"Identity"};
my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
$TestResults .= " ".get_filename($Name).$Comment."\n";
}
$TestResults .= " \n";
}
if(my @Sources = keys(%{$Registered_Sources{1}}))
{
$TestResults .= " \n";
foreach my $Name (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
{
my $Identity = $Registered_Sources{1}{$Name}{"Identity"};
my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
$TestResults .= " ".get_filename($Name).$Comment."\n";
}
$TestResults .= " \n";
}
$TestResults .= " \n";
foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$Library_Symbol{1}}))
{
# $Library .= " (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
$TestResults .= " $Library\n";
}
$TestResults .= " \n";
$TestResults .= " ".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))."\n";
$TestResults .= " ".$TotalTypes."\n";
$TestResults .= " ".$RESULT{$Level}{"Verdict"}."\n";
$TestResults .= " ".$RESULT{$Level}{"Affected"}."\n";
$TestResults = "\n".$TestResults."\n\n";
# problem summary
$Problem_Summary .= " ".$Added."\n";
$Problem_Summary .= " ".$Removed."\n";
$Problem_Summary .= " \n";
$Problem_Summary .= " $T_Problems_High\n";
$Problem_Summary .= " $T_Problems_Medium\n";
$Problem_Summary .= " $T_Problems_Low\n";
$Problem_Summary .= " $T_Other\n";
$Problem_Summary .= " \n";
$Problem_Summary .= " \n";
$Problem_Summary .= " $I_Problems_High\n";
$Problem_Summary .= " $I_Problems_Medium\n";
$Problem_Summary .= " $I_Problems_Low\n";
$Problem_Summary .= " $I_Other\n";
$Problem_Summary .= " \n";
$Problem_Summary .= " \n";
$Problem_Summary .= " $C_Problems_Low\n";
$Problem_Summary .= " \n";
$Problem_Summary = "\n".$Problem_Summary."\n\n";
return ($TestInfo.$TestResults.$Problem_Summary, "");
}
else
{ # HTML
# test info
$TestInfo = "Test Info
\n";
$TestInfo .= "\n";
if($TargetComponent eq "library") {
$TestInfo .= "Library Name | $TargetTitle |
\n";
}
else {
$TestInfo .= "Module Name | $TargetTitle |
\n";
}
my (@VInf1, @VInf2, $AddTestInfo) = ();
if($Arch1 ne "unknown"
and $Arch2 ne "unknown")
{ # CPU arch
if($Arch1 eq $Arch2)
{ # go to the separate section
$AddTestInfo .= "Arch | ".showArch($Arch1)." |
\n";
}
else
{ # go to the version number
push(@VInf1, showArch($Arch1));
push(@VInf2, showArch($Arch2));
}
}
if($Level eq "Binary"
and $OStarget ne "windows")
{
if($GccV1 ne "unknown"
and $GccV2 ne "unknown")
{ # GCC version
if($GccV1 eq $GccV2)
{ # go to the separate section
$AddTestInfo .= "GCC Version | $GccV1 |
\n";
}
else
{ # go to the version number
push(@VInf1, "gcc ".$GccV1);
push(@VInf2, "gcc ".$GccV2);
}
}
elsif($ClangV1
and $ClangV2)
{ # Clang version
if($ClangV1 eq $ClangV2)
{ # go to the separate section
$AddTestInfo .= "Clang Version | $ClangV1 |
\n";
}
else
{ # go to the version number
push(@VInf1, "clang ".$ClangV1);
push(@VInf2, "clang ".$ClangV2);
}
}
elsif($GccV1 ne "unknown" and $ClangV2)
{
push(@VInf1, "gcc ".$GccV1);
push(@VInf2, "clang ".$ClangV2);
}
elsif($ClangV1 and $GccV2 ne "unknown")
{
push(@VInf1, "clang ".$ClangV1);
push(@VInf2, "gcc ".$GccV2);
}
}
# show long version names with GCC version and CPU architecture name (if different)
$TestInfo .= "Version #1 | ".$Descriptor{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")." |
\n";
$TestInfo .= "Version #2 | ".$Descriptor{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")." |
\n";
$TestInfo .= $AddTestInfo;
#if($COMMON_LANGUAGE{1}) {
# $TestInfo .= "Language | ".$COMMON_LANGUAGE{1}." |
\n";
#}
if($ExtendedCheck) {
$TestInfo .= "Mode | Extended |
\n";
}
if($JoinReport)
{
if($Level eq "Binary") {
$TestInfo .= "Subject | Binary Compatibility |
\n"; # Run-time
}
elsif($Level eq "Source") {
$TestInfo .= "Subject | Source Compatibility |
\n"; # Build-time
}
}
$TestInfo .= "
\n";
# test results
$TestResults = "Test Results
\n";
$TestResults .= "";
if(my @Headers = get_CheckedHeaders(1))
{
my $Headers_Link = "".($#Headers + 1)."";
$TestResults .= "Total Header Files | ".$Headers_Link." |
\n";
}
if(my @Sources = keys(%{$Registered_Sources{1}}))
{
my $Src_Link = "".($#Sources + 1)."";
$TestResults .= "Total Source Files | ".$Src_Link." |
\n";
}
if(not $ExtendedCheck)
{
my $Libs_Link = "0";
$Libs_Link = "".keys(%{$Library_Symbol{1}})."" if(keys(%{$Library_Symbol{1}})>0);
$TestResults .= "Total ".get_ObjTitle()." | ".($CheckHeadersOnly?"0 (not analyzed)":$Libs_Link)." |
\n";
}
$TestResults .= "Total Symbols / Types | ".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))." / ".$TotalTypes." |
\n";
my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";";
if($JoinReport) {
$META_DATA = "kind:".lc($Level).";".$META_DATA;
}
my $BC_Rate = show_number(100 - $RESULT{$Level}{"Affected"});
$TestResults .= "Compatibility | \n";
if($RESULT{$Level}{"Verdict"} eq "incompatible")
{
my $Cl = "incompatible";
if($BC_Rate>=90) {
$Cl = "warning";
}
elsif($BC_Rate>=80) {
$Cl = "almost_compatible";
}
$TestResults .= "".$BC_Rate."% | \n";
}
else {
$TestResults .= "100% | \n";
}
$TestResults .= "
\n";
$TestResults .= "
\n";
$META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
# problem summary
$Problem_Summary = "Problem Summary
\n";
$Problem_Summary .= "";
$Problem_Summary .= " | Severity | Count |
";
my $Added_Link = "0";
if($Added>0)
{
if($JoinReport) {
$Added_Link = "$Added";
}
else {
$Added_Link = "$Added";
}
}
$META_DATA .= "added:$Added;";
$Problem_Summary .= "Added Symbols | - | $Added_Link |
\n";
my $Removed_Link = "0";
if($Removed>0)
{
if($JoinReport) {
$Removed_Link = "$Removed"
}
else {
$Removed_Link = "$Removed"
}
}
$META_DATA .= "removed:$Removed;";
$Problem_Summary .= "Removed Symbols | ";
$Problem_Summary .= "High | $Removed_Link |
\n";
my $TH_Link = "0";
$TH_Link = "$T_Problems_High" if($T_Problems_High>0);
$META_DATA .= "type_problems_high:$T_Problems_High;";
$Problem_Summary .= "Problems with Data Types | ";
$Problem_Summary .= "High | $TH_Link |
\n";
my $TM_Link = "0";
$TM_Link = "$T_Problems_Medium" if($T_Problems_Medium>0);
$META_DATA .= "type_problems_medium:$T_Problems_Medium;";
$Problem_Summary .= "Medium | $TM_Link |
\n";
my $TL_Link = "0";
$TL_Link = "$T_Problems_Low" if($T_Problems_Low>0);
$META_DATA .= "type_problems_low:$T_Problems_Low;";
$Problem_Summary .= "Low | $TL_Link |
\n";
my $IH_Link = "0";
$IH_Link = "$I_Problems_High" if($I_Problems_High>0);
$META_DATA .= "interface_problems_high:$I_Problems_High;";
$Problem_Summary .= "Problems with Symbols | ";
$Problem_Summary .= "High | $IH_Link |
\n";
my $IM_Link = "0";
$IM_Link = "$I_Problems_Medium" if($I_Problems_Medium>0);
$META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
$Problem_Summary .= "Medium | $IM_Link |
\n";
my $IL_Link = "0";
$IL_Link = "$I_Problems_Low" if($I_Problems_Low>0);
$META_DATA .= "interface_problems_low:$I_Problems_Low;";
$Problem_Summary .= "Low | $IL_Link |
\n";
my $ChangedConstants_Link = "0";
if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) {
$ChangedConstants_Link = "$C_Problems_Low";
}
$META_DATA .= "changed_constants:$C_Problems_Low;";
$Problem_Summary .= "Problems with Constants | Low | $ChangedConstants_Link |
\n";
# Safe Changes
if($T_Other)
{
my $TS_Link = "$T_Other";
$Problem_Summary .= "Other Changes in Data Types | - | $TS_Link |
\n";
$META_DATA .= "type_changes_other:$T_Other;";
}
if($I_Other)
{
my $IS_Link = "$I_Other";
$Problem_Summary .= "Other Changes in Symbols | - | $IS_Link |
\n";
$META_DATA .= "interface_changes_other:$I_Other;";
}
if($C_Other)
{
my $CS_Link = "$C_Other";
$Problem_Summary .= "Other Changes in Constants | - | $CS_Link |
\n";
$META_DATA .= "constant_changes_other:$C_Other;";
}
$META_DATA .= "tool_version:$TOOL_VERSION";
$Problem_Summary .= "
\n";
return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
}
}
sub getStyle($$$)
{
my ($Subj, $Act, $Num) = @_;
my %Style = (
"Added"=>"new",
"Removed"=>"failed",
"Safe"=>"passed",
"Low"=>"warning",
"Medium"=>"failed",
"High"=>"failed"
);
if($Num>0) {
return " class='".$Style{$Act}."'";
}
return "";
}
sub show_number($)
{
if($_[0])
{
my $Num = cut_off_number($_[0], 2, 0);
if($Num eq "0")
{
foreach my $P (3 .. 7)
{
$Num = cut_off_number($_[0], $P, 1);
if($Num ne "0") {
last;
}
}
}
if($Num eq "0") {
$Num = $_[0];
}
return $Num;
}
return $_[0];
}
sub cut_off_number($$$)
{
my ($num, $digs_to_cut, $z) = @_;
if($num!~/\./)
{
$num .= ".";
foreach (1 .. $digs_to_cut-1) {
$num .= "0";
}
}
elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
{
foreach (1 .. $digs_to_cut - 1 - length($1)) {
$num .= "0";
}
}
elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
$num=sprintf("%.".($digs_to_cut-1)."f", $num);
}
$num=~s/\.[0]+\Z//g;
if($z) {
$num=~s/(\.[1-9]+)[0]+\Z/$1/g;
}
return $num;
}
sub get_Report_ChangedConstants($$)
{
my ($TargetSeverity, $Level) = @_;
my $CHANGED_CONSTANTS = "";
my %ReportMap = ();
foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
{
my $Header = $Constants{1}{$Constant}{"Header"};
if(not $Header)
{ # added
$Header = $Constants{2}{$Constant}{"Header"}
}
foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
{
if(not defined $CompatRules{$Level}{$Kind}) {
next;
}
if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) {
next;
}
$ReportMap{$Header}{$Constant}{$Kind} = 1;
}
}
if($ReportFormat eq "xml")
{ # XML
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
$CHANGED_CONSTANTS .= " \n";
foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
$CHANGED_CONSTANTS .= " \n";
foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
{
my $Change = $CompatRules{$Level}{$Kind}{"Change"};
my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
$CHANGED_CONSTANTS .= " \n";
$CHANGED_CONSTANTS .= " $Change\n";
$CHANGED_CONSTANTS .= " $Effect\n";
if($Overcome) {
$CHANGED_CONSTANTS .= " $Overcome\n";
}
$CHANGED_CONSTANTS .= " \n";
}
$CHANGED_CONSTANTS .= " \n";
}
$CHANGED_CONSTANTS .= " \n";
}
$CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS."\n\n";
}
else
{ # HTML
my $ProblemsNum = 0;
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
$CHANGED_CONSTANTS .= "$HeaderName
\n";
foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
my $Report = "";
foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
{
my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind});
my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
$Report .= "\n1 | \n".$Change." | \n$Effect | \n
\n";
$ProblemsNum += 1;
}
if($Report)
{
$Report = $ContentDivStart."\n\n | \nChange | \nEffect | \n
\n".$Report."
\n
\n$ContentDivEnd\n";
$Report = $ContentSpanStart."[+] ".$Constant.$ContentSpanEnd."
\n".$Report;
$Report = insertIDs($Report);
}
$CHANGED_CONSTANTS .= $Report;
}
$CHANGED_CONSTANTS .= "
\n";
}
if($CHANGED_CONSTANTS)
{
my $Title = "Problems with Constants, $TargetSeverity Severity";
if($TargetSeverity eq "Safe")
{ # Safe Changes
$Title = "Other Changes in Constants";
}
if($OldStyle) {
$CHANGED_CONSTANTS = "$Title ($ProblemsNum)
\n".$CHANGED_CONSTANTS;
}
else {
$CHANGED_CONSTANTS = "$Title $ProblemsNum
\n".$CHANGED_CONSTANTS;
}
$CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS.$TOP_REF."
\n";
}
}
return $CHANGED_CONSTANTS;
}
sub getTitle($$$)
{
my ($Header, $Library, $NameSpace) = @_;
my $Title = "";
# if($Library and $Library!~/\.\w+\Z/) {
# $Library .= " (.$LIB_EXT)";
# }
if($Header and $Library)
{
$Title .= "$Header";
$Title .= ", $Library
\n";
}
elsif($Library) {
$Title .= "$Library
\n";
}
elsif($Header) {
$Title .= "$Header
\n";
}
if($NameSpace) {
$Title .= "namespace $NameSpace
\n";
}
return $Title;
}
sub get_Report_Added($)
{
my $Level = $_[0];
my $ADDED_INTERFACES = "";
my %ReportMap = ();
foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
{
foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
{
if($Kind eq "Added_Symbol")
{
my $HeaderName = $CompleteSignature{2}{$Interface}{"Header"};
my $DyLib = $Symbol_Library{2}{$Interface};
if($Level eq "Source" and $ReportFormat eq "html")
{ # do not show library name in HTML report
$DyLib = "";
}
$ReportMap{$HeaderName}{$DyLib}{$Interface} = 1;
}
}
}
if($ReportFormat eq "xml")
{ # XML
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
$ADDED_INTERFACES .= " \n";
foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
$ADDED_INTERFACES .= " \n";
foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
$ADDED_INTERFACES .= " $Interface\n";
}
$ADDED_INTERFACES .= " \n";
}
$ADDED_INTERFACES .= " \n";
}
$ADDED_INTERFACES = "\n".$ADDED_INTERFACES."\n\n";
}
else
{ # HTML
my $Added_Number = 0;
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
my %NameSpaceSymbols = ();
foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
$NameSpaceSymbols{select_Symbol_NS($Interface, 2)}{$Interface} = 1;
}
foreach my $NameSpace (sort keys(%NameSpaceSymbols))
{
$ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
my @SortedInterfaces = sort {lc(get_Signature($a, 2)) cmp lc(get_Signature($b, 2))} keys(%{$NameSpaceSymbols{$NameSpace}});
foreach my $Interface (@SortedInterfaces)
{
$Added_Number += 1;
my $Signature = get_Signature($Interface, 2);
if($NameSpace) {
$Signature=~s/\b\Q$NameSpace\E::\b//g;
}
if($Interface=~/\A(_Z|\?)/)
{
if($Signature) {
$ADDED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."
\n".$ContentDivStart."[symbol: $Interface]\n
\n
\n".$ContentDivEnd."\n");
}
else {
$ADDED_INTERFACES .= "".$Interface."
\n";
}
}
else
{
if($Signature) {
$ADDED_INTERFACES .= "".highLight_Signature_Italic_Color($Signature)."
\n";
}
else {
$ADDED_INTERFACES .= "".$Interface."
\n";
}
}
}
$ADDED_INTERFACES .= "
\n";
}
}
}
if($ADDED_INTERFACES)
{
my $Anchor = "";
if($JoinReport) {
$Anchor = "";
}
if($OldStyle) {
$ADDED_INTERFACES = "Added Symbols ($Added_Number)
\n".$ADDED_INTERFACES;
}
else {
$ADDED_INTERFACES = "Added Symbols $Added_Number
\n".$ADDED_INTERFACES;
}
$ADDED_INTERFACES = $Anchor.$ADDED_INTERFACES.$TOP_REF."
\n";
}
}
return $ADDED_INTERFACES;
}
sub get_Report_Removed($)
{
my $Level = $_[0];
my $REMOVED_INTERFACES = "";
my %ReportMap = ();
foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
{
foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
{
if($Kind eq "Removed_Symbol")
{
my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
my $DyLib = $Symbol_Library{1}{$Symbol};
if($Level eq "Source" and $ReportFormat eq "html")
{ # do not show library name in HTML report
$DyLib = "";
}
$ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
}
}
}
if($ReportFormat eq "xml")
{ # XML
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
$REMOVED_INTERFACES .= " \n";
foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
$REMOVED_INTERFACES .= " \n";
foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
$REMOVED_INTERFACES .= " $Symbol\n";
}
$REMOVED_INTERFACES .= " \n";
}
$REMOVED_INTERFACES .= " \n";
}
$REMOVED_INTERFACES = "\n".$REMOVED_INTERFACES."\n\n";
}
else
{ # HTML
my $Removed_Number = 0;
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
my %NameSpaceSymbols = ();
foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
$NameSpaceSymbols{select_Symbol_NS($Interface, 1)}{$Interface} = 1;
}
foreach my $NameSpace (sort keys(%NameSpaceSymbols))
{
$REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpaceSymbols{$NameSpace}});
foreach my $Symbol (@SortedInterfaces)
{
$Removed_Number += 1;
my $SubReport = "";
my $Signature = get_Signature($Symbol, 1);
if($NameSpace) {
$Signature=~s/\b\Q$NameSpace\E::\b//g;
}
if($Symbol=~/\A(_Z|\?)/)
{
if($Signature) {
$REMOVED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."
\n".$ContentDivStart."[symbol: $Symbol]\n
\n
\n".$ContentDivEnd."\n");
}
else {
$REMOVED_INTERFACES .= "".$Symbol."
\n";
}
}
else
{
if($Signature) {
$REMOVED_INTERFACES .= "".highLight_Signature_Italic_Color($Signature)."
\n";
}
else {
$REMOVED_INTERFACES .= "".$Symbol."
\n";
}
}
}
}
$REMOVED_INTERFACES .= "
\n";
}
}
if($REMOVED_INTERFACES)
{
my $Anchor = "";
if($JoinReport) {
$Anchor = "";
}
if($OldStyle) {
$REMOVED_INTERFACES = "Removed Symbols ($Removed_Number)
\n".$REMOVED_INTERFACES;
}
else {
$REMOVED_INTERFACES = "Removed Symbols $Removed_Number
\n".$REMOVED_INTERFACES;
}
$REMOVED_INTERFACES = $Anchor.$REMOVED_INTERFACES.$TOP_REF."
\n";
}
}
return $REMOVED_INTERFACES;
}
sub getXmlParams($$)
{
my ($Content, $Problem) = @_;
return "" if(not $Content or not $Problem);
my %XMLparams = ();
foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
{
my $Macro = "\@".lc($Attr);
if($Content=~/\Q$Macro\E/) {
$XMLparams{lc($Attr)} = $Problem->{$Attr};
}
}
my @PString = ();
foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\"");
}
if(@PString) {
return " ".join(" ", @PString);
}
else {
return "";
}
}
sub addMarkup($)
{
my $Content = $_[0];
# auto-markup
$Content=~s/\n[ ]*//; # spaces
$Content=~s!(\@\w+\s*\(\@\w+\))!$1!g; # @old_type (@old_size)
$Content=~s!(... \(\w+\))!$1!g; # ... (va_list)
$Content=~s!(.+?)!$1!g;
$Content=~s!([2-9]\))!
$1!g; # 1), 2), ...
if($Content=~/\ANOTE:/)
{ # notes
$Content=~s!(NOTE):!$1:!g;
}
else {
$Content=~s!(NOTE):!
$1:!g;
}
$Content=~s! (out)-! $1-!g; # out-parameters
my @Keywords = (
"void",
"const",
"static",
"restrict",
"volatile",
"register",
"virtual"
);
my $MKeys = join("|", @Keywords);
foreach (@Keywords) {
$MKeys .= "|non-".$_;
}
$Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1$2$3!ig; # intrinsic types, modifiers
# Markdown
$Content=~s!\*\*([\w\-]+)\*\*!$1!ig;
$Content=~s!\*([\w\-]+)\*!$1!ig;
return $Content;
}
sub applyMacroses($$$$)
{
my ($Level, $Kind, $Content, $Problem) = @_;
return "" if(not $Content or not $Problem);
$Problem->{"Word_Size"} = $WORD_SIZE{2};
$Content = addMarkup($Content);
# macros
foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
{
my $Macro = "\@".lc($Attr);
my $Value = $Problem->{$Attr};
if(not defined $Value
or $Value eq "") {
next;
}
if(index($Content, $Macro)==-1) {
next;
}
if($Kind!~/\A(Changed|Added|Removed)_Constant\Z/
and $Kind!~/_Type_/
and $Value=~/\s\(/ and $Value!~/['"]/)
{ # functions
$Value=~s/\s*\[[\w\-]+\]//g; # remove quals
$Value=~s/\s[a-z]\w*(\)|,)/$1/ig; # remove parameter names
$Value = black_name($Value);
}
elsif($Value=~/\s/) {
$Value = "".htmlSpecChars($Value)."";
}
elsif($Value=~/\A\d+\Z/
and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
{ # bits to bytes
if($Value % $BYTE_SIZE)
{ # bits
if($Value==1) {
$Value = "".$Value." bit";
}
else {
$Value = "".$Value." bits";
}
}
else
{ # bytes
$Value /= $BYTE_SIZE;
if($Value==1) {
$Value = "".$Value." byte";
}
else {
$Value = "".$Value." bytes";
}
}
}
else
{
$Value = "".htmlSpecChars($Value)."";
}
$Content=~s/\Q$Macro\E/$Value/g;
}
if($Content=~/(\A|[^\@\w])\@\w/)
{
if(not $IncompleteRules{$Level}{$Kind})
{ # only one warning
printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
$IncompleteRules{$Level}{$Kind} = 1;
}
}
return $Content;
}
sub get_Report_SymbolProblems($$)
{
my ($TargetSeverity, $Level) = @_;
my $INTERFACE_PROBLEMS = "";
my (%ReportMap, %SymbolChanges) = ();
foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
{
my ($SN, $SS, $SV) = separate_symbol($Symbol);
if($SV and defined $CompatProblems{$Level}{$SN}) {
next;
}
my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
my $DyLib = $Symbol_Library{1}{$Symbol};
if(not $DyLib and my $VSym = $SymVer{1}{$Symbol})
{ # Symbol with Version
$DyLib = $Symbol_Library{1}{$VSym};
}
if(not $DyLib)
{ # const global data
$DyLib = "";
}
if($Level eq "Source" and $ReportFormat eq "html")
{ # do not show library name in HTML report
$DyLib = "";
}
foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
{
if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol")
{
my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Symbol}{$Kind}}))
{
if($Severity eq $TargetSeverity)
{
$SymbolChanges{$Symbol}{$Kind} = $CompatProblems{$Level}{$Symbol}{$Kind};
$ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
}
}
}
}
}
if($ReportFormat eq "xml")
{ # XML
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
$INTERFACE_PROBLEMS .= " \n";
foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
$INTERFACE_PROBLEMS .= " \n";
my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$ReportMap{$HeaderName}{$DyLib}});
foreach my $Symbol (@SortedInterfaces)
{
$INTERFACE_PROBLEMS .= " \n";
foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
{
foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
{
my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
$Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
$INTERFACE_PROBLEMS .= " \n";
my $Change = $CompatRules{$Level}{$Kind}{"Change"};
$INTERFACE_PROBLEMS .= " $Change\n";
my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
$INTERFACE_PROBLEMS .= " $Effect\n";
if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
$INTERFACE_PROBLEMS .= " $Overcome\n";
}
$INTERFACE_PROBLEMS .= " \n";
}
}
$INTERFACE_PROBLEMS .= " \n";
}
$INTERFACE_PROBLEMS .= " \n";
}
$INTERFACE_PROBLEMS .= " \n";
}
$INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS."\n\n";
}
else
{ # HTML
my $ProblemsNum = 0;
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
{
my (%NameSpaceSymbols, %NewSignature) = ();
foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
$NameSpaceSymbols{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
}
foreach my $NameSpace (sort keys(%NameSpaceSymbols))
{
$INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace);
my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} sort keys(%{$NameSpaceSymbols{$NameSpace}});
foreach my $Symbol (@SortedInterfaces)
{
my $Signature = get_Signature($Symbol, 1);
my $SYMBOL_REPORT = "";
my $ProblemNum = 1;
foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
{
foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
{
my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
$Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
if($Problem{"New_Signature"}) {
$NewSignature{$Symbol} = $Problem{"New_Signature"};
}
if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
{
my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
$SYMBOL_REPORT .= "\n$ProblemNum | \n".$Change." | \n".$Effect." | \n
\n";
$ProblemNum += 1;
$ProblemsNum += 1;
}
}
}
$ProblemNum -= 1;
if($SYMBOL_REPORT)
{
my $ShowSymbol = $Symbol;
if($Signature) {
$ShowSymbol = highLight_Signature_Italic_Color($Signature);
}
if($NameSpace)
{
$SYMBOL_REPORT = cut_Namespace($SYMBOL_REPORT, $NameSpace);
$ShowSymbol = cut_Namespace($ShowSymbol, $NameSpace);
}
$INTERFACE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowSymbol;
if($OldStyle) {
$INTERFACE_PROBLEMS .= " ($ProblemNum)";
}
else {
$INTERFACE_PROBLEMS .= " $ProblemNum ";
}
$INTERFACE_PROBLEMS .= $ContentSpanEnd."
\n";
$INTERFACE_PROBLEMS .= $ContentDivStart."\n";
if(my $NSign = $NewSignature{$Symbol})
{ # argument list changed to
if($NameSpace) {
$NSign = cut_Namespace($NSign, $NameSpace);
}
$INTERFACE_PROBLEMS .= "\nchanged to:\n
\n".highLight_Signature_Italic_Color($NSign)."
\n";
}
if($Symbol=~/\A(_Z|\?)/) {
$INTERFACE_PROBLEMS .= " [symbol: $Symbol]
\n";
}
$INTERFACE_PROBLEMS .= "\n\n | \nChange | \nEffect | \n
\n$SYMBOL_REPORT
\n
\n";
$INTERFACE_PROBLEMS .= $ContentDivEnd;
}
}
$INTERFACE_PROBLEMS .= "
\n";
}
}
}
if($INTERFACE_PROBLEMS)
{
$INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS);
my $Title = "Problems with Symbols, $TargetSeverity Severity";
if($TargetSeverity eq "Safe")
{ # Safe Changes
$Title = "Other Changes in Symbols";
}
if($OldStyle) {
$INTERFACE_PROBLEMS = "$Title ($ProblemsNum)
\n".$INTERFACE_PROBLEMS;
}
else {
$INTERFACE_PROBLEMS = "$Title $ProblemsNum
\n".$INTERFACE_PROBLEMS;
}
$INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS.$TOP_REF."
\n";
}
}
return $INTERFACE_PROBLEMS;
}
sub cut_Namespace($$)
{
my ($N, $Ns) = @_;
$N=~s/\b\Q$Ns\E:://g;
return $N;
}
sub get_Report_TypeProblems($$)
{
my ($TargetSeverity, $Level) = @_;
my $TYPE_PROBLEMS = "";
my %ReportMap = ();
my %TypeChanges_Sev = ();
foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
{
my $HeaderName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Header"};
foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
{
foreach my $Location (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
{
my $Target = $TypeChanges{$Level}{$TypeName}{$Kind}{$Location}{"Target"};
my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
if($Severity eq $TargetSeverity)
{
$ReportMap{$HeaderName}{$TypeName} = 1;
$TypeChanges_Sev{$TypeName}{$Kind}{$Location} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Location};
}
}
}
}
if($ReportFormat eq "xml")
{ # XML
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
$TYPE_PROBLEMS .= " \n";
foreach my $TypeName (keys(%{$ReportMap{$HeaderName}}))
{
my (%Kinds_Locations, %Kinds_Target) = ();
$TYPE_PROBLEMS .= " \n";
foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}}))
{
foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
{
$Kinds_Locations{$Kind}{$Location} = 1;
my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
if($Kinds_Target{$Kind}{$Target}) {
next;
}
$Kinds_Target{$Kind}{$Target} = 1;
my %Problem = %{$TypeChanges_Sev{$TypeName}{$Kind}{$Location}};
$TYPE_PROBLEMS .= " \n";
my $Change = $CompatRules{$Level}{$Kind}{"Change"};
$TYPE_PROBLEMS .= " $Change\n";
my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
$TYPE_PROBLEMS .= " $Effect\n";
if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
$TYPE_PROBLEMS .= " $Overcome\n";
}
$TYPE_PROBLEMS .= " \n";
}
}
$TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
$TYPE_PROBLEMS .= showVTables($TypeName);
}
$TYPE_PROBLEMS .= " \n";
}
$TYPE_PROBLEMS .= " \n";
}
$TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS."\n\n";
}
else
{ # HTML
my $ProblemsNum = 0;
foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
my (%NameSpace_Type) = ();
foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) {
$NameSpace_Type{select_Type_NS($TypeName, 1)}{$TypeName} = 1;
}
foreach my $NameSpace (sort keys(%NameSpace_Type))
{
$TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace);
my @SortedTypes = sort {lc(show_Type($a, 0, 1)) cmp lc(show_Type($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
foreach my $TypeName (@SortedTypes)
{
my $ProblemNum = 1;
my $TYPE_REPORT = "";
my (%Kinds_Locations, %Kinds_Target) = ();
foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}}))
{
foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
{
$Kinds_Locations{$Kind}{$Location} = 1;
my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
if($Kinds_Target{$Kind}{$Target}) {
next;
}
$Kinds_Target{$Kind}{$Target} = 1;
my %Problem = %{$TypeChanges_Sev{$TypeName}{$Kind}{$Location}};
if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
{
my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
$TYPE_REPORT .= "\n$ProblemNum | \n".$Change." | \n$Effect | \n
\n";
$ProblemNum += 1;
$ProblemsNum += 1;
}
}
}
$ProblemNum -= 1;
if($TYPE_REPORT)
{
my $Affected = getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
my $ShowVTables = "";
if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
$ShowVTables = showVTables($TypeName);
}
my $ShowType = show_Type($TypeName, 1, 1);
if($NameSpace)
{
$TYPE_REPORT = cut_Namespace($TYPE_REPORT, $NameSpace);
$ShowType = cut_Namespace($ShowType, $NameSpace);
$Affected = cut_Namespace($Affected, $NameSpace);
$ShowVTables = cut_Namespace($ShowVTables, $NameSpace);
}
$TYPE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowType;
if($OldStyle) {
$TYPE_PROBLEMS .= " ($ProblemNum)";
}
else {
$TYPE_PROBLEMS .= " $ProblemNum ";
}
$TYPE_PROBLEMS .= $ContentSpanEnd;
$TYPE_PROBLEMS .= "
\n".$ContentDivStart."\n";
$TYPE_PROBLEMS .= " | Change | \n";
$TYPE_PROBLEMS .= "Effect |
".$TYPE_REPORT."
\n";
$TYPE_PROBLEMS .= $ShowVTables.$Affected."
".$ContentDivEnd."\n";
}
}
$TYPE_PROBLEMS .= "
\n";
}
}
if($TYPE_PROBLEMS)
{
$TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
my $Title = "Problems with Data Types, $TargetSeverity Severity";
if($TargetSeverity eq "Safe")
{ # Safe Changes
$Title = "Other Changes in Data Types";
}
if($OldStyle) {
$TYPE_PROBLEMS = "$Title ($ProblemsNum)
\n".$TYPE_PROBLEMS;
}
else {
$TYPE_PROBLEMS = "$Title $ProblemsNum
\n".$TYPE_PROBLEMS;
}
$TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS.$TOP_REF."
\n";
}
}
return $TYPE_PROBLEMS;
}
sub show_Type($$$)
{
my ($Name, $Html, $LibVersion) = @_;
my $TType = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$Name}}{"Type"};
$TType = lc($TType);
if($TType=~/struct|union|enum/) {
$Name=~s/\A\Q$TType\E //g;
}
if($Html) {
$Name = "".$TType." ".htmlSpecChars($Name);
}
else {
$Name = $TType." ".$Name;
}
return $Name;
}
sub get_Anchor($$$)
{
my ($Kind, $Level, $Severity) = @_;
if($JoinReport)
{
if($Severity eq "Safe") {
return "Other_".$Level."_Changes_In_".$Kind."s";
}
else {
return $Kind."_".$Level."_Problems_".$Severity;
}
}
else
{
if($Severity eq "Safe") {
return "Other_Changes_In_".$Kind."s";
}
else {
return $Kind."_Problems_".$Severity;
}
}
}
sub showVTables($)
{
my $TypeName = $_[0];
my $TypeId1 = $TName_Tid{1}{$TypeName};
my %Type1 = get_Type($TypeId1, 1);
if(defined $Type1{"VTable"}
and keys(%{$Type1{"VTable"}}))
{
my $TypeId2 = $TName_Tid{2}{$TypeName};
my %Type2 = get_Type($TypeId2, 2);
if(defined $Type2{"VTable"}
and keys(%{$Type2{"VTable"}}))
{
my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
my %Entries = ();
foreach my $Index (sort {int($a)<=>int($b)} (keys(%Indexes)))
{
$Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index});
$Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index});
}
my $VTABLES = "";
if($ReportFormat eq "xml")
{ # XML
$VTABLES .= " \n";
foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
{
$VTABLES .= " \n";
$VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E1"})."\n";
$VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E2"})."\n";
$VTABLES .= " \n";
}
$VTABLES .= " \n\n";
}
else
{ # HTML
$VTABLES .= "";
$VTABLES .= "Offset | ";
$VTABLES .= "Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries | ";
$VTABLES .= "Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries |
";
foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
{
my ($Color1, $Color2) = ("", "");
my $E1 = $Entries{$Index}{"E1"};
my $E2 = $Entries{$Index}{"E2"};
if($E1 ne $E2
and $E1!~/ 0x/
and $E2!~/ 0x/)
{
if($Entries{$Index}{"E1"})
{
$Color1 = " class='failed'";
$Color2 = " class='failed'";
}
else {
$Color2 = " class='warning'";
}
}
$VTABLES .= "".$Index." | \n";
$VTABLES .= "".htmlSpecChars($Entries{$Index}{"E1"})." | \n";
$VTABLES .= "".htmlSpecChars($Entries{$Index}{"E2"})." |
\n";
}
$VTABLES .= "
\n";
$VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
$VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."
\n".$VTABLES;
}
return $VTABLES;
}
}
return "";
}
sub simpleVEntry($)
{
my $VEntry = $_[0];
if(not defined $VEntry
or $VEntry eq "") {
return "";
}
$VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper
$VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
$VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
if($VEntry=~/\A_ZThn.+\Z/) {
$VEntry = "non-virtual thunk";
}
$VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i;
# support for old GCC versions
$VEntry=~s/\A0u\Z/(int (*)(...))0/;
$VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
$VEntry=~s/\A&_Z\Z/& _Z/;
$VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
return $VEntry;
}
sub adjustParamPos($$$)
{
my ($Pos, $Symbol, $LibVersion) = @_;
if(defined $CompleteSignature{$LibVersion}{$Symbol})
{
if(not $CompleteSignature{$LibVersion}{$Symbol}{"Static"}
and $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
{
return $Pos-1;
}
return $Pos;
}
return undef;
}
sub getParamPos($$$)
{
my ($Name, $Symbol, $LibVersion) = @_;
if(defined $CompleteSignature{$LibVersion}{$Symbol}
and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"})
{
my $Info = $CompleteSignature{$LibVersion}{$Symbol};
foreach (keys(%{$Info->{"Param"}}))
{
if($Info->{"Param"}{$_}{"name"} eq $Name)
{
return $_;
}
}
}
return undef;
}
sub getParamName($)
{
my $Loc = $_[0];
$Loc=~s/\->.*//g;
return $Loc;
}
sub getAffectedSymbols($$$)
{
my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
my $LIMIT = 10;
if(defined $AffectLimit) {
$LIMIT = $AffectLimit;
}
my @Kinds = sort keys(%{$Kinds_Locations});
my %KLocs = ();
foreach my $Kind (@Kinds)
{
my @Locs = sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
$KLocs{$Kind} = \@Locs;
}
my %SymLocKind = ();
foreach my $Symbol (sort keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}}))
{
if(index($Symbol, "_Z")==0
and $Symbol=~/(C2|D2|D0)[EI]/)
{ # duplicated problems for C2 constructors, D2 and D0 destructors
next;
}
foreach my $Kind (@Kinds)
{
foreach my $Loc (@{$KLocs{$Kind}})
{
if(not defined $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}) {
next;
}
if(index($Symbol, "\@")!=-1
or index($Symbol, "\$")!=-1)
{
my ($SN, $SS, $SV) = separate_symbol($Symbol);
if($Level eq "Source")
{ # remove symbol version
$Symbol = $SN;
}
if($SV and defined $CompatProblems{$Level}{$SN}
and defined $CompatProblems{$Level}{$SN}{$Kind}{$Loc})
{ # duplicated problems for versioned symbols
next;
}
}
my $Type_Name = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}{"Type_Name"};
if($Type_Name ne $Target_TypeName) {
next;
}
$SymLocKind{$Symbol}{$Loc}{$Kind} = 1;
last;
}
}
}
%KLocs = (); # clear
my %SymSel = ();
my $Num = 0;
foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymLocKind))
{
LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Symbol}}))
{
foreach my $Kind (sort keys(%{$SymLocKind{$Symbol}{$Loc}}))
{
$SymSel{$Symbol}{"Loc"} = $Loc;
$SymSel{$Symbol}{"Kind"} = $Kind;
last LOOP;
}
}
$Num += 1;
if($Num>=$LIMIT) {
last;
}
}
my $Affected = "";
if($ReportFormat eq "xml")
{ # XML
$Affected .= " \n";
foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
{
my $Kind = $SymSel{$Symbol}{"Kind"};
my $Loc = $SymSel{$Symbol}{"Loc"};
my $PName = getParamName($Loc);
my $Desc = getAffectDesc($Level, $Symbol, $Kind, $Loc);
my $Target = "";
if($PName)
{
$Target .= " param=\"$PName\"";
$Desc=~s/parameter $PName /parameter \@param /;
}
elsif($Loc=~/\Aretval(\-|\Z)/i) {
$Target .= " affected=\"retval\"";
}
elsif($Loc=~/\Athis(\-|\Z)/i) {
$Target .= " affected=\"this\"";
}
if($Desc=~s/\AField ([^\s]+) /Field \@field /) {
$Target .= " field=\"$1\"";
}
$Affected .= " \n";
$Affected .= " ".xmlSpecChars($Desc)."\n";
$Affected .= " \n";
}
$Affected .= " \n";
}
else
{ # HTML
foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
{
my $Kind = $SymSel{$Symbol}{"Kind"};
my $Loc = $SymSel{$Symbol}{"Loc"};
my $Desc = getAffectDesc($Level, $Symbol, $Kind, $Loc);
my $S = get_Signature($Symbol, 1);
my $PName = getParamName($Loc);
my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1);
$Affected .= "".highLight_Signature_PPos_Italic($S, $Pos, 1, 0, 0)."
\n";
$Affected .= "".htmlSpecChars($Desc)."
\n";
}
if(keys(%SymLocKind)>$LIMIT) {
$Affected .= " ...\n
\n"; # and others ...
}
$Affected = "".$Affected."
\n";
if($Affected)
{
my $Num = keys(%SymLocKind);
my $Per = show_number($Num*100/keys(%{$CheckedSymbols{$Level}}));
$Affected = $ContentDivStart.$Affected.$ContentDivEnd;
$Affected = $ContentSpanStart_Affected."[+] affected symbols: $Num ($Per\%)".$ContentSpanEnd.$Affected;
}
}
return $Affected;
}
sub cmpLocations($$)
{
my ($L1, $L2) = @_;
if($L2=~/\A(retval|this)\b/
and $L1!~/\A(retval|this)\b/)
{
if($L1!~/\-\>/) {
return 1;
}
elsif($L2=~/\-\>/) {
return 1;
}
}
return 0;
}
sub getAffectDesc($$$$)
{
my ($Level, $Symbol, $Kind, $Location) = @_;
my %Problem = %{$CompatProblems{$Level}{$Symbol}{$Kind}{$Location}};
my $Location_I = $Location;
$Location=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field
my @Sentence = ();
if($Kind eq "Overridden_Virtual_Method"
or $Kind eq "Overridden_Virtual_Method_B") {
push(@Sentence, "The method '".$Problem{"New_Value"}."' will be called instead of this method.");
}
elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
{
my %SymInfo = %{$CompleteSignature{1}{$Symbol}};
if($Location eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
{
my $METHOD_TYPE = $SymInfo{"Constructor"}?"constructor":"method";
my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"};
if($ClassName eq $Problem{"Type_Name"}) {
push(@Sentence, "This $METHOD_TYPE is from \'".$Problem{"Type_Name"}."\' class.");
}
else {
push(@Sentence, "This $METHOD_TYPE is from derived class \'".$ClassName."\'.");
}
}
else
{
my $TypeID = undef;
if($Location=~/retval/)
{ # return value
if(index($Location, "->")!=-1) {
push(@Sentence, "Field \'".$Location."\' in return value");
}
else {
push(@Sentence, "Return value");
}
$TypeID = $SymInfo{"Return"};
}
elsif($Location=~/this/)
{ # "this" pointer
if(index($Location, "->")!=-1) {
push(@Sentence, "Field \'".$Location."\' in the object of this method");
}
else {
push(@Sentence, "\'this\' pointer");
}
$TypeID = $SymInfo{"Class"};
}
else
{ # parameters
my $PName = getParamName($Location);
my $PPos = getParamPos($PName, $Symbol, 1);
if(index($Location, "->")!=-1) {
push(@Sentence, "Field \'".$Location."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
}
else {
push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
}
if($PName) {
push(@Sentence, "\'".$PName."\'");
}
$TypeID = $SymInfo{"Param"}{$PPos}{"type"};
}
if($Location!~/this/)
{
if(my %PureType = get_PureType($TypeID, $TypeInfo{1}))
{
if($PureType{"Type"} eq "Pointer") {
push(@Sentence, "(pointer)");
}
elsif($PureType{"Type"} eq "Ref") {
push(@Sentence, "(reference)");
}
}
}
if($Location eq "this") {
push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
}
else
{
my $Location_T = $Location;
$Location_T=~s/\A\w+(\->|\Z)//; # location in type
my $TypeID_Problem = $TypeID;
if($Location_T) {
$TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
}
if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Problem{"Type_Name"}) {
push(@Sentence, "has type \'".$Problem{"Type_Name"}."\'.");
}
else {
push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
}
}
}
}
if($ExtendedSymbols{$Symbol}) {
push(@Sentence, " This is a symbol from an external library that may use the \'$TargetLibraryName\' library and change the ABI after recompiling.");
}
my $Sent = join(" ", @Sentence);
$Sent=~s/->/./g;
if($ReportFormat eq "xml")
{
$Sent=~s/'//g;
}
return $Sent;
}
sub getFieldType($$$)
{
my ($Location, $TypeId, $LibVersion) = @_;
my @Fields = split(/\->/, $Location);
foreach my $Name (@Fields)
{
my %Info = get_BaseType($TypeId, $LibVersion);
foreach my $Pos (keys(%{$Info{"Memb"}}))
{
if($Info{"Memb"}{$Pos}{"name"} eq $Name)
{
$TypeId = $Info{"Memb"}{$Pos}{"type"};
last;
}
}
}
return $TypeId;
}
sub get_XmlSign($$)
{
my ($Symbol, $LibVersion) = @_;
my $Info = $CompleteSignature{$LibVersion}{$Symbol};
my $Report = "";
foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Info->{"Param"}}))
{
my $Name = $Info->{"Param"}{$Pos}{"name"};
my $Type = $Info->{"Param"}{$Pos}{"type"};
my $TypeName = $TypeInfo{$LibVersion}{$Type}{"Name"};
foreach my $Typedef (keys(%ChangedTypedef))
{
if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
$TypeName=~s/\b\Q$Typedef\E\b/$Base/g;
}
}
$Report .= " \n";
$Report .= " ".$Name."\n";
$Report .= " ".xmlSpecChars($TypeName)."\n";
$Report .= " \n";
}
if(my $Return = $Info->{"Return"})
{
my $RTName = $TypeInfo{$LibVersion}{$Return}{"Name"};
$Report .= " \n";
$Report .= " ".xmlSpecChars($RTName)."\n";
$Report .= " \n";
}
return $Report;
}
sub get_Report_SymbolsInfo($)
{
my $Level = $_[0];
my $Report = "\n";
foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
{
my ($SN, $SS, $SV) = separate_symbol($Symbol);
if($SV and defined $CompatProblems{$Level}{$SN}) {
next;
}
$Report .= " \n";
my ($S1, $P1, $S2, $P2) = ();
if(not $AddedInt{$Level}{$Symbol})
{
if(defined $CompleteSignature{1}{$Symbol}
and defined $CompleteSignature{1}{$Symbol}{"Header"})
{
$P1 = get_XmlSign($Symbol, 1);
$S1 = get_Signature($Symbol, 1);
}
elsif($Symbol=~/\A(_Z|\?)/) {
$S1 = $tr_name{$Symbol};
}
}
if(not $RemovedInt{$Level}{$Symbol})
{
if(defined $CompleteSignature{2}{$Symbol}
and defined $CompleteSignature{2}{$Symbol}{"Header"})
{
$P2 = get_XmlSign($Symbol, 2);
$S2 = get_Signature($Symbol, 2);
}
elsif($Symbol=~/\A(_Z|\?)/) {
$S2 = $tr_name{$Symbol};
}
}
if($S1)
{
$Report .= " \n";
$Report .= $P1;
$Report .= " \n";
}
if($S2 and $S2 ne $S1)
{
$Report .= " \n";
$Report .= $P2;
$Report .= " \n";
}
$Report .= " \n";
}
$Report .= "\n";
return $Report;
}
sub writeReport($$)
{
my ($Level, $Report) = @_;
if($ReportFormat eq "xml") {
$Report = "\n".$Report;
}
if($StdOut)
{ # --stdout option
print STDOUT $Report;
}
else
{
my $RPath = getReportPath($Level);
mkpath(get_dirname($RPath));
open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n");
print REPORT $Report;
close(REPORT);
}
}
sub getReport($)
{
my $Level = $_[0];
if($ReportFormat eq "xml")
{ # XML
if($Level eq "Join")
{
my $Report = "\n";
$Report .= getReport("Binary");
$Report .= getReport("Source");
$Report .= "\n";
return $Report;
}
else
{
my $Report = "\n\n";
my ($Summary, $MetaData) = get_Summary($Level);
$Report .= $Summary."\n";
$Report .= get_Report_Added($Level).get_Report_Removed($Level);
$Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
# additional symbols info (if needed)
# $Report .= get_Report_SymbolsInfo($Level);
$Report .= "\n";
return $Report;
}
}
else
{ # HTML
my $CssStyles = readModule("Styles", "Report.css");
my $JScripts = readModule("Scripts", "Sections.js");
if($Level eq "Join")
{
$CssStyles .= "\n".readModule("Styles", "Tabs.css");
$JScripts .= "\n".readModule("Scripts", "Tabs.js");
my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report";
my $Keywords = $TargetTitle.", compatibility, API, ABI, report";
my $Description = "API/ABI compatibility report for the $TargetTitle $TargetComponent between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
my ($BSummary, $BMetaData) = get_Summary("Binary");
my ($SSummary, $SMetaData) = get_Summary("Source");
my $Report = "\n\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."";
$Report .= get_Report_Title("Join")."
";
$Report .= "\n$BSummary\n".get_Report_Added("Binary").get_Report_Removed("Binary").get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary").get_SourceInfo()."
";
$Report .= "\n$SSummary\n".get_Report_Added("Source").get_Report_Removed("Source").get_Report_Problems("High", "Source").get_Report_Problems("Medium", "Source").get_Report_Problems("Low", "Source").get_Report_Problems("Safe", "Source").get_SourceInfo()."
";
$Report .= getReportFooter();
$Report .= "\n