]> git.ipfire.org Git - thirdparty/glibc.git/blame - scripts/glibcextract.py
Disable warnings in string/tester.c at top level.
[thirdparty/glibc.git] / scripts / glibcextract.py
CommitLineData
a8110b72
JM
1#!/usr/bin/python3
2# Extract information from C headers.
04277e02 3# Copyright (C) 2018-2019 Free Software Foundation, Inc.
a8110b72
JM
4# This file is part of the GNU C Library.
5#
6# The GNU C Library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# The GNU C Library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with the GNU C Library; if not, see
5a82c748 18# <https://www.gnu.org/licenses/>.
a8110b72
JM
19
20import os.path
21import re
22import subprocess
23import tempfile
24
25
26def compute_c_consts(sym_data, cc):
27 """Compute the values of some C constants.
28
29 The first argument is a list whose elements are either strings
30 (preprocessor directives, or the special string 'START' to
31 indicate this function should insert its initial boilerplate text
32 in the output there) or pairs of strings (a name and a C
33 expression for the corresponding value). Preprocessor directives
34 in the middle of the list may be used to select which constants
35 end up being evaluated using which expressions.
36
37 """
38 out_lines = []
39 for arg in sym_data:
40 if isinstance(arg, str):
41 if arg == 'START':
42 out_lines.append('void\ndummy (void)\n{')
43 else:
44 out_lines.append(arg)
45 continue
46 name = arg[0]
47 value = arg[1]
48 out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
49 ': : \"i\" ((long int) (%s)));'
50 % (name, value))
51 out_lines.append('}')
52 out_lines.append('')
53 out_text = '\n'.join(out_lines)
54 with tempfile.TemporaryDirectory() as temp_dir:
55 c_file_name = os.path.join(temp_dir, 'test.c')
56 s_file_name = os.path.join(temp_dir, 'test.s')
57 with open(c_file_name, 'w') as c_file:
58 c_file.write(out_text)
59 # Compilation has to be from stdin to avoid the temporary file
60 # name being written into the generated dependencies.
61 cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
62 subprocess.check_call(cmd, shell=True)
63 consts = {}
64 with open(s_file_name, 'r') as s_file:
65 for line in s_file:
66 match = re.search('@@@name@@@([^@]*)'
67 '@@@value@@@[^0-9Xxa-fA-F-]*'
68 '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
69 if match:
70 if (match.group(1) in consts
71 and match.group(2) != consts[match.group(1)]):
72 raise ValueError('duplicate constant %s'
73 % match.group(1))
74 consts[match.group(1)] = match.group(2)
75 return consts
76
77
78def list_macros(source_text, cc):
79 """List the preprocessor macros defined by the given source code.
80
81 The return value is a pair of dicts, the first one mapping macro
82 names to their expansions and the second one mapping macro names
83 to lists of their arguments, or to None for object-like macros.
84
85 """
86 with tempfile.TemporaryDirectory() as temp_dir:
87 c_file_name = os.path.join(temp_dir, 'test.c')
88 i_file_name = os.path.join(temp_dir, 'test.i')
89 with open(c_file_name, 'w') as c_file:
90 c_file.write(source_text)
91 cmd = ('%s -E -dM -o %s %s' % (cc, i_file_name, c_file_name))
92 subprocess.check_call(cmd, shell=True)
93 macros_exp = {}
94 macros_args = {}
95 with open(i_file_name, 'r') as i_file:
96 for line in i_file:
97 match = re.fullmatch('#define ([0-9A-Za-z_]+)(.*)\n', line)
98 if not match:
99 raise ValueError('bad -dM output line: %s' % line)
100 name = match.group(1)
101 value = match.group(2)
102 if value.startswith(' '):
103 value = value[1:]
104 args = None
105 elif value.startswith('('):
106 match = re.fullmatch(r'\((.*?)\) (.*)', value)
107 if not match:
108 raise ValueError('bad -dM output line: %s' % line)
109 args = match.group(1).split(',')
110 value = match.group(2)
111 else:
112 raise ValueError('bad -dM output line: %s' % line)
113 if name in macros_exp:
114 raise ValueError('duplicate macro: %s' % line)
115 macros_exp[name] = value
116 macros_args[name] = args
117 return macros_exp, macros_args
118
119
120def compute_macro_consts(source_text, cc, macro_re, exclude_re=None):
121 """Compute the integer constant values of macros defined by source_text.
122
123 Macros must match the regular expression macro_re, and if
124 exclude_re is defined they must not match exclude_re. Values are
125 computed with compute_c_consts.
126
127 """
128 macros_exp, macros_args = list_macros(source_text, cc)
129 macros_set = {m for m in macros_exp
130 if (macros_args[m] is None
131 and re.fullmatch(macro_re, m)
132 and (exclude_re is None
133 or not re.fullmatch(exclude_re, m)))}
134 sym_data = [source_text, 'START']
135 sym_data.extend(sorted((m, m) for m in macros_set))
136 return compute_c_consts(sym_data, cc)
137
138
df648905
JM
139def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None,
140 allow_extra_1=False, allow_extra_2=False):
a8110b72
JM
141 """Compare the values of macros defined by two different sources.
142
143 The sources would typically be includes of a glibc header and a
df648905
JM
144 kernel header. If allow_extra_1, the first source may define
145 extra macros (typically if the kernel headers are older than the
146 version glibc has taken definitions from); if allow_extra_2, the
147 second source may define extra macros (typically if the kernel
148 headers are newer than the version glibc has taken definitions
149 from). Return 1 if there were any differences other than those
150 allowed, 0 if the macro values were the same apart from any
151 allowed differences.
a8110b72
JM
152
153 """
154 macros_1 = compute_macro_consts(source_1, cc, macro_re, exclude_re)
155 macros_2 = compute_macro_consts(source_2, cc, macro_re, exclude_re)
156 if macros_1 == macros_2:
157 return 0
158 print('First source:\n%s\n' % source_1)
159 print('Second source:\n%s\n' % source_2)
df648905 160 ret = 0
a8110b72
JM
161 for name, value in sorted(macros_1.items()):
162 if name not in macros_2:
163 print('Only in first source: %s' % name)
df648905
JM
164 if not allow_extra_1:
165 ret = 1
a8110b72
JM
166 elif macros_1[name] != macros_2[name]:
167 print('Different values for %s: %s != %s'
168 % (name, macros_1[name], macros_2[name]))
df648905 169 ret = 1
a8110b72
JM
170 for name in sorted(macros_2.keys()):
171 if name not in macros_1:
172 print('Only in second source: %s' % name)
df648905
JM
173 if not allow_extra_2:
174 ret = 1
175 return ret