xref: /petsc/lib/petsc/bin/maint/abi-compliance-checker/modules/Internals/Descriptor.pm (revision e8b6250908b962c387f7ab2e7b38caaa661b5fa1)
1###########################################################################
2# A module to handle XML descriptors
3#
4# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory
5#
6# Written by Andrey Ponomarenko
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16# Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public
19# License along with this library; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21# MA  02110-1301 USA
22###########################################################################
23use strict;
24
25sub createDesc($$)
26{
27    my ($Path, $LVer) = @_;
28
29    if(not -e $Path) {
30        return undef;
31    }
32
33    if(-d $Path)
34    { # directory with headers files and shared objects
35        return "
36            <version>
37                ".$In::Desc{$LVer}{"TargetVersion"}."
38            </version>
39
40            <headers>
41                $Path
42            </headers>
43
44            <libs>
45                $Path
46            </libs>";
47    }
48    else
49    { # files
50        if($Path=~/\.(xml|desc)\Z/i)
51        { # standard XML-descriptor
52            return readFile($Path);
53        }
54        elsif(isHeaderFile($Path))
55        { # header file
56            $In::Opt{"CheckHeadersOnly"} = 1;
57            return "
58                <version>
59                    ".$In::Desc{$LVer}{"TargetVersion"}."
60                </version>
61
62                <headers>
63                    $Path
64                </headers>
65
66                <libs>
67                </libs>";
68        }
69        else
70        { # standard XML-descriptor
71            return readFile($Path);
72        }
73    }
74}
75
76sub readDesc($$)
77{
78    my ($Content, $LVer) = @_;
79
80    if(not $Content) {
81        exitStatus("Error", "XML descriptor is empty");
82    }
83    if($Content!~/\</) {
84        exitStatus("Error", "incorrect descriptor (see -d1 option)");
85    }
86
87    $Content=~s/\/\*(.|\n)+?\*\///g;
88    $Content=~s/<\!--(.|\n)+?-->//g;
89
90    my $DescRef = $In::Desc{$LVer};
91
92    $DescRef->{"Version"} = parseTag(\$Content, "version");
93    if(my $TV = $DescRef->{"TargetVersion"}) {
94        $DescRef->{"Version"} = $TV;
95    }
96    elsif($DescRef->{"Version"} eq "") #Allow the version to be a string
97    {
98        if($LVer==1)
99        {
100            $DescRef->{"Version"} = "X";
101            print STDERR "WARNING: version number #1 is not set (use --v1=NUM option)\n";
102        }
103        else
104        {
105            $DescRef->{"Version"} = "Y";
106            print STDERR "WARNING: version number #2 is not set (use --v2=NUM option)\n";
107        }
108    }
109
110    if(not $DescRef->{"Version"}) {
111        exitStatus("Error", "version in the XML descriptor is not specified (section \"version\")");
112    }
113    if($Content=~/\{RELPATH\}/)
114    {
115        if(my $RelDir = $DescRef->{"RelativeDirectory"}) {
116            $Content =~ s/\{RELPATH\}/$RelDir/g;
117        }
118        else {
119            exitStatus("Error", "you have not specified -relpath* option, but the XML descriptor contains {RELPATH} macro");
120        }
121    }
122
123    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "headers")))
124    {
125        if(not -e $Path) {
126            exitStatus("Access_Error", "can't access \'$Path\'");
127        }
128
129        $DescRef->{"Headers"}{$Path} = keys(%{$DescRef->{"Headers"}});
130    }
131    if(not defined $DescRef->{"Headers"}) {
132        exitStatus("Error", "can't find header files info in the XML descriptor");
133    }
134
135    if(not $In::Opt{"CheckHeadersOnly"})
136    {
137        foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "libs")))
138        {
139            if(not -e $Path) {
140                exitStatus("Access_Error", "can't access \'$Path\'");
141            }
142
143            $DescRef->{"Libs"}{$Path} = 1;
144        }
145
146        if(not defined $DescRef->{"Libs"}) {
147            exitStatus("Error", "can't find libraries info in the XML descriptor");
148        }
149    }
150    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers")))
151    {
152        if(not -d $Path) {
153            exitStatus("Access_Error", "can't access directory \'$Path\'");
154        }
155        $Path = getAbsPath($Path);
156        push_U($In::Opt{"SysPaths"}{"include"}, $Path);
157    }
158    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs")))
159    {
160        if(not -d $Path) {
161            exitStatus("Access_Error", "can't access directory \'$Path\'");
162        }
163        $Path = getAbsPath($Path);
164        push_U($In::Opt{"SysPaths"}{"lib"}, $Path);
165    }
166    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools")))
167    {
168        if(not -d $Path) {
169            exitStatus("Access_Error", "can't access directory \'$Path\'");
170        }
171        $Path = getAbsPath($Path);
172        push_U($In::Opt{"SysPaths"}{"bin"}, $Path);
173        $In::Opt{"TargetTools"}{$Path} = 1;
174    }
175    if(my $Prefix = parseTag(\$Content, "cross_prefix")) {
176        $In::Opt{"CrossPrefix"} = $Prefix;
177    }
178
179    $DescRef->{"IncludePaths"} = [];
180    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths")))
181    {
182        if(not -d $Path) {
183            exitStatus("Access_Error", "can't access directory \'$Path\'");
184        }
185        $Path = getAbsPath($Path);
186        push(@{$DescRef->{"IncludePaths"}}, $Path);
187    }
188
189    if(not @{$DescRef->{"IncludePaths"}}) {
190        $DescRef->{"AutoIncludePaths"} = 1;
191    }
192
193    $DescRef->{"AddIncludePaths"} = [];
194    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths")))
195    {
196        if(not -d $Path) {
197            exitStatus("Access_Error", "can't access directory \'$Path\'");
198        }
199        $Path = getAbsPath($Path);
200        push(@{$DescRef->{"AddIncludePaths"}}, $Path);
201    }
202
203    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths")))
204    { # skip some auto-generated include paths
205        $Path = getAbsPath($Path);
206        $DescRef->{"SkipIncludePaths"}{$Path} = 1;
207    }
208    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including")))
209    { # skip direct including of some headers
210        if(my ($CPath, $Type) = classifyPath($Path)) {
211            $DescRef->{"SkipHeaders"}{$Type}{$CPath} = 2;
212        }
213    }
214    foreach my $Option (split(/\s*\n\s*/, parseTag(\$Content, "gcc_options")))
215    {
216        if($Option!~/\A\-(Wl|l|L)/)
217        { # skip linker options
218            $DescRef->{"CompilerOptions"} .= " ".$Option;
219        }
220    }
221    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_headers")))
222    {
223        if(my ($CPath, $Type) = classifyPath($Path)) {
224            $DescRef->{"SkipHeaders"}{$Type}{$CPath} = 1;
225        }
226    }
227    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_libs")))
228    {
229        if(my ($CPath, $Type) = classifyPath($Path)) {
230            $DescRef->{"SkipLibs"}{$Type}{$CPath} = 1;
231        }
232    }
233    if(my $DDefines = parseTag(\$Content, "defines"))
234    {
235        if($DescRef->{"Defines"})
236        { # multiple descriptors
237            $DescRef->{"Defines"} .= "\n".$DDefines;
238        }
239        else {
240            $DescRef->{"Defines"} = $DDefines;
241        }
242    }
243    foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order")))
244    {
245        if($Order=~/\A(.+):(.+)\Z/) {
246            $DescRef->{"IncludeOrder"}{$1} = $2;
247        }
248    }
249    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "add_namespaces"))) {
250        $DescRef->{"AddNameSpaces"}{$NameSpace} = 1;
251    }
252    if(my $DIncPreamble = parseTag(\$Content, "include_preamble"))
253    {
254        if($DescRef->{"IncludePreamble"})
255        { # multiple descriptors
256            $DescRef->{"IncludePreamble"} .= "\n".$DIncPreamble;
257        }
258        else {
259            $DescRef->{"IncludePreamble"} = $DIncPreamble;
260        }
261    }
262
263    readFilter($Content, $LVer);
264}
265
266sub readFilter($$)
267{
268    my ($Content, $LVer) = @_;
269
270    $Content=~s/\/\*(.|\n)+?\*\///g;
271    $Content=~s/<\!--(.|\n)+?-->//g;
272
273    my $DescRef = $In::Desc{$LVer};
274
275    foreach my $TName (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")),
276    split(/\s*\n\s*/, parseTag(\$Content, "skip_types"))) {
277        $DescRef->{"SkipTypes"}{$TName} = 1;
278    }
279    foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")),
280    split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols"))) {
281        $DescRef->{"SkipSymbols"}{$Symbol} = 1;
282    }
283    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) {
284        $DescRef->{"SkipNameSpaces"}{$NameSpace} = 1;
285    }
286    foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) {
287        $DescRef->{"SkipConstants"}{$Constant} = 1;
288    }
289}
290
291return 1;
292