]> git.ipfire.org Git - thirdparty/glibc.git/blob - scripts/glibcextract.py
Update miscellaneous files from upstream sources.
[thirdparty/glibc.git] / scripts / glibcextract.py
1 #!/usr/bin/python3
2 # Extract information from C headers.
3 # Copyright (C) 2018-2019 Free Software Foundation, Inc.
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
18 # <http://www.gnu.org/licenses/>.
19
20 import os.path
21 import re
22 import subprocess
23 import tempfile
24
25
26 def 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
78 def 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
120 def 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
139 def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None,
140 allow_extra_1=False, allow_extra_2=False):
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
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.
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)
160 ret = 0
161 for name, value in sorted(macros_1.items()):
162 if name not in macros_2:
163 print('Only in first source: %s' % name)
164 if not allow_extra_1:
165 ret = 1
166 elif macros_1[name] != macros_2[name]:
167 print('Different values for %s: %s != %s'
168 % (name, macros_1[name], macros_2[name]))
169 ret = 1
170 for name in sorted(macros_2.keys()):
171 if name not in macros_1:
172 print('Only in second source: %s' % name)
173 if not allow_extra_2:
174 ret = 1
175 return ret