in src/main/abi-symbols/abi-dumper.pl [6269:6690]
sub detectPublicSymbols($)
{
my $Path = $_[0];
if(not -e $Path) {
exitStatus("Access_Error", "can't access \'$Path\'");
}
my $Path_A = abs_path($Path);
printMsg("INFO", "Detect public symbols");
if($UseTU)
{
if(not checkCmd($GPP)) {
exitStatus("Not_Found", "can't find \"$GPP\"");
}
}
else
{
if(not checkCmd($CTAGS)) {
exitStatus("Not_Found", "can't find \"$CTAGS\"");
}
if(my $CtagsVer = `$CTAGS --version 2>&1`)
{
if($CtagsVer!~/Universal/i)
{
printMsg("ERROR", "requires Universal Ctags to work properly");
if($CtagsVer=~/Exuberant/i) {
$EXUBERANT_CTAGS = 1;
}
}
}
}
$PublicSymbols_Detected = 1;
my @Files = ();
my @Headers = ();
my @DefaultInc = ();
if($PublicHeadersIsDir)
{ # directory
@Files = findFiles($Path, "f");
foreach my $File (@Files)
{
if(isHeader($File)) {
push(@Headers, $File);
}
}
push(@DefaultInc, $Path_A);
if(-d $Path_A."/include") {
push(@DefaultInc, $Path_A."/include");
}
}
else
{ # list of headers
@Headers = split(/\n/, readFile($Path));
}
if(not @Headers) {
exitStatus("Error", "headers not found in \'$Path\'");
}
my $PublicHeader_F = $CacheHeaders."/PublicHeader.data";
my $SymbolToHeader_F = $CacheHeaders."/SymbolToHeader.data";
my $TypeToHeader_F = $CacheHeaders."/TypeToHeader.data";
my $Path_F = $CacheHeaders."/PATH";
if($CacheHeaders
and -f $PublicHeader_F
and -f $SymbolToHeader_F
and -f $TypeToHeader_F
and -f $Path_F)
{
if(readFile($Path_F) eq $Path_A)
{
%PublicHeader = %{eval(readFile($PublicHeader_F))};
%SymbolToHeader = %{eval(readFile($SymbolToHeader_F))};
%TypeToHeader = %{eval(readFile($TypeToHeader_F))};
return;
}
}
foreach my $File (@Headers)
{
$PublicHeader{getFilename($File)} = 1;
}
my $Is_C = ($OBJ_LANG eq "C");
my @Langs = undef;
if($EXUBERANT_CTAGS)
{
@Langs = ("C++");
if($Is_C) {
@Langs = ("C");
}
}
else
{
@Langs = ("C++", "OldC++");
if($Is_C) {
@Langs = ("C", "OldC");
}
}
@Headers = sort {length($b)<=>length($a)} sort {lc($b) cmp lc($a)} @Headers;
foreach my $File (@Headers)
{
my $HName = getFilename($File);
if($UseTU)
{
my $TmpDir = $TMP_DIR."/tu";
if(not -d $TmpDir) {
mkpath($TmpDir);
}
my $File_A = abs_path($File);
my $IncDir = getDirname($File_A);
my $IncDir_O = getDirname($IncDir);
my $TmpInc = $TmpDir."/tmp-inc.h";
my $TmpContent = "";
if($IncludeDefines)
{
foreach my $D (split(/;/, $IncludeDefines)) {
$TmpContent = "#define $D\n";
}
}
if($IncludePreamble)
{
foreach my $P (split(/;/, $IncludePreamble))
{
if($P=~/\A\//) {
$TmpContent = "#include \"".$P."\"\n";
}
else {
$TmpContent = "#include <".$P.">\n";
}
}
}
$TmpContent .= "#include \"$File_A\"\n";
writeFile($TmpInc, $TmpContent);
my $Cmd = $GPP." -w -fpermissive -fdump-translation-unit -fkeep-inline-functions -c \"$TmpInc\"";
if(defined $IncludePaths)
{
foreach my $P (split(/;/, $IncludePaths))
{
if($P!~/\A\//) {
$P = $Path_A."/".$P;
}
$Cmd .= " -I\"".$P."\"";
}
}
else
{ # automatic
$Cmd .= " -I\"$IncDir\" -I\"$IncDir_O\"";
}
foreach my $P (@DefaultInc) {
$Cmd .= " -I\"$P\"";
}
$Cmd .= " -o ./a.out >OUT 2>&1";
chdir($TmpDir);
system($Cmd);
chdir($ORIG_DIR);
my $TuDump = $TmpDir."/tmp-inc.h.001t.tu";
my $Errors = $TmpDir."/OUT";
if(not -e $TuDump)
{
printMsg("ERROR", "failed to list symbols in the header \'$HName\'");
if($Debug) {
printMsg("ERROR", readFile($Errors));
}
next;
}
elsif($?)
{
printMsg("ERROR", "some errors occured when compiling header \'$HName\'");
if($Debug) {
printMsg("ERROR", readFile($Errors));
}
}
my (%Fdecl, %Tdecl, %Tname, %Ident, %NotDecl) = ();
my $Content = readFile($TuDump);
$Content=~s/\n[ ]+/ /g;
my @Lines = split(/\n/, $Content);
foreach my $N (0 .. $#Lines)
{
my $Line = $Lines[$N];
if(index($Line, "function_decl")!=-1
or index($Line, "var_decl")!=-1)
{
if($Line=~/name: \@(\d+)/)
{
my $Id = $1;
if($Line=~/srcp: ([^:]+)\:\d/)
{
if(defined $PublicHeader{$1}) {
$Fdecl{$Id} = $1;
}
}
}
}
elsif($Line=~/\@(\d+)\s+identifier_node\s+strg:\s+(\w+)/)
{
$Ident{$1} = $2;
}
elsif($Is_C)
{
if(index($Line, "type_decl")!=-1)
{
if($Line=~/\A\@(\d+)/)
{
my $Id = $1;
if($Line=~/name: \@(\d+)/)
{
my $NId = $1;
if($Line=~/srcp: ([^:]+)\:\d/)
{
if(defined $PublicHeader{$1})
{
$Tdecl{$Id} = $1;
$Tname{$Id} = $NId;
}
}
}
}
}
elsif(index($Line, "record_type")!=-1
or index($Line, "union_type")!=-1)
{
if($Line!~/ flds:/)
{
if($Line=~/name: \@(\d+)/)
{
$NotDecl{$1} = 1;
}
}
}
elsif(index($Line, "enumeral_type")!=-1)
{
if($Line!~/ csts:/)
{
if($Line=~/name: \@(\d+)/)
{
$NotDecl{$1} = 1;
}
}
}
elsif(index($Line, "integer_type")!=-1)
{
if($Line=~/name: \@(\d+)/)
{
$NotDecl{$1} = 1;
}
}
}
}
foreach my $Id (keys(%Fdecl))
{
if(my $Name = $Ident{$Id}) {
$SymbolToHeader{$Name}{$Fdecl{$Id}} = 1;
}
}
if($Is_C)
{
foreach my $Id (keys(%Tdecl))
{
if(defined $NotDecl{$Id}) {
next;
}
if(my $Name = $Ident{$Tname{$Id}}) {
$TypeToHeader{$Name} = $Tdecl{$Id};
}
}
}
unlink($TuDump);
}
else
{ # using Ctags
my $IgnoreTags = "";
if(defined $IgnoreTagsPath) {
$IgnoreTags .= " -I \@".$IgnoreTagsPath;
}
if(@CtagsDef)
{
foreach my $Def (@CtagsDef) {
$IgnoreTags .= " -D '".$Def."'";
}
}
foreach my $Lang (@Langs)
{
my $List_S = `$CTAGS -x --$Lang-kinds=fpvxd --languages=+$Lang --language-force=$Lang $IgnoreTags \"$File\"`;
foreach my $Line (split(/\n/, $List_S))
{
if($Line=~/\A(\w+)\s+(\w+)/) {
$SymbolToHeader{$1}{$HName} = $2;
}
if(index($Line, " macro ")!=-1)
{
if($Line=~/#define\s+(\w+)\s+(\w+)\Z/) {
$SymbolToHeader{$2}{$HName} = "prototype";
}
}
if(not $Is_C)
{
if(index($Line, "operator ")==0)
{
if($Line=~/\A(operator) (\w.*?)\s+(prototype|function)/) {
$SymbolToHeader{$1." ".$2}{$HName} = $3;
}
elsif($Line=~/\A(operator) (\W.*?)\s+(prototype|function)/) {
$SymbolToHeader{$1.$2}{$HName} = $3;
}
}
}
}
if($Is_C)
{
my $List_T = `$CTAGS -x --$Lang-kinds=gstu --languages=+$Lang --language-force=$Lang $IgnoreTags \"$File\"`;
foreach my $Line (split(/\n/, $List_T))
{
if($Line=~/\A(\w+)/)
{
my $N = $1;
if($Line!~/\b$N\s+$N\b/) {
$TypeToHeader{$N} = $HName;
}
}
}
}
}
}
}
# We can't fully rely on the output of Ctags because it may
# miss some symbols intentionally (due to branches of code)
# or occasionally (due to complex macros).
if(not $UseTU)
{
foreach my $File (@Headers)
{
my $HName = getFilename($File);
my $Content = readFile($File);
$Content=~s&/\*.+?\*/&&sg;
$Content=~s&(//|#define).*\n&\n&g;
# Functions
my @Func = ($Content=~/([a-zA-Z]\w+)\s*\(/g);
foreach (@Func)
{
if(not defined $SymbolToHeader{$_} or not defined $SymbolToHeader{$_}{$HName}) {
$SymbolToHeader{$_}{$HName} = "prototype";
}
}
# Data
my @Data = ($Content=~/([a-zA-Z_]\w+)\s*;/gi);
foreach (@Data)
{
if(not defined $SymbolToHeader{$_} or not defined $SymbolToHeader{$_}{$HName}) {
$SymbolToHeader{$_}{$HName} = "prototype";
}
}
# Types
if($Is_C)
{
my @Type1 = ($Content=~/}\s*([a-zA-Z]\w+)\s*;/g);
my @Type2 = ($Content=~/([a-zA-Z]\w+)\s*{/g);
foreach (@Type1, @Type2)
{
if(not defined $TypeToHeader{$_} or not defined $TypeToHeader{$_}{$HName}) {
$TypeToHeader{$_}{$HName} = 1;
}
}
}
}
}
if($CacheHeaders)
{
writeFile($PublicHeader_F, Dumper(\%PublicHeader));
writeFile($SymbolToHeader_F, Dumper(\%SymbolToHeader));
writeFile($TypeToHeader_F, Dumper(\%TypeToHeader));
writeFile($Path_F, $Path_A);
}
}