]> git.ipfire.org Git - thirdparty/glibc.git/blame - scripts/glibcextract.py
sparc (64bit): Regenerate ulps
[thirdparty/glibc.git] / scripts / glibcextract.py
CommitLineData
a8110b72
JM
1#!/usr/bin/python3
2# Extract information from C headers.
6d7e8eda 3# Copyright (C) 2018-2023 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 19
841afa11 20import collections
a8110b72
JM
21import os.path
22import re
23import subprocess
24import tempfile
25
26
27def compute_c_consts(sym_data, cc):
28 """Compute the values of some C constants.
29
30 The first argument is a list whose elements are either strings
31 (preprocessor directives, or the special string 'START' to
32 indicate this function should insert its initial boilerplate text
33 in the output there) or pairs of strings (a name and a C
34 expression for the corresponding value). Preprocessor directives
35 in the middle of the list may be used to select which constants
36 end up being evaluated using which expressions.
37
38 """
39 out_lines = []
40 for arg in sym_data:
41 if isinstance(arg, str):
42 if arg == 'START':
43 out_lines.append('void\ndummy (void)\n{')
44 else:
45 out_lines.append(arg)
46 continue
47 name = arg[0]
48 value = arg[1]
46baeb61 49 out_lines.append('asm ("/* @@@name@@@%s@@@value@@@%%0@@@end@@@ */" '
a8110b72
JM
50 ': : \"i\" ((long int) (%s)));'
51 % (name, value))
52 out_lines.append('}')
53 out_lines.append('')
54 out_text = '\n'.join(out_lines)
55 with tempfile.TemporaryDirectory() as temp_dir:
56 c_file_name = os.path.join(temp_dir, 'test.c')
57 s_file_name = os.path.join(temp_dir, 'test.s')
58 with open(c_file_name, 'w') as c_file:
59 c_file.write(out_text)
60 # Compilation has to be from stdin to avoid the temporary file
61 # name being written into the generated dependencies.
62 cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
63 subprocess.check_call(cmd, shell=True)
64 consts = {}
65 with open(s_file_name, 'r') as s_file:
66 for line in s_file:
67 match = re.search('@@@name@@@([^@]*)'
68 '@@@value@@@[^0-9Xxa-fA-F-]*'
69 '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
70 if match:
71 if (match.group(1) in consts
72 and match.group(2) != consts[match.group(1)]):
73 raise ValueError('duplicate constant %s'
74 % match.group(1))
75 consts[match.group(1)] = match.group(2)
76 return consts
77
78
79def list_macros(source_text, cc):
80 """List the preprocessor macros defined by the given source code.
81
82 The return value is a pair of dicts, the first one mapping macro
83 names to their expansions and the second one mapping macro names
84 to lists of their arguments, or to None for object-like macros.
85
86 """
87 with tempfile.TemporaryDirectory() as temp_dir:
88 c_file_name = os.path.join(temp_dir, 'test.c')
89 i_file_name = os.path.join(temp_dir, 'test.i')
90 with open(c_file_name, 'w') as c_file:
91 c_file.write(source_text)
92 cmd = ('%s -E -dM -o %s %s' % (cc, i_file_name, c_file_name))
93 subprocess.check_call(cmd, shell=True)
94 macros_exp = {}
95 macros_args = {}
96 with open(i_file_name, 'r') as i_file:
97 for line in i_file:
98 match = re.fullmatch('#define ([0-9A-Za-z_]+)(.*)\n', line)
99 if not match:
100 raise ValueError('bad -dM output line: %s' % line)
101 name = match.group(1)
102 value = match.group(2)
103 if value.startswith(' '):
104 value = value[1:]
105 args = None
106 elif value.startswith('('):
107 match = re.fullmatch(r'\((.*?)\) (.*)', value)
108 if not match:
109 raise ValueError('bad -dM output line: %s' % line)
110 args = match.group(1).split(',')
111 value = match.group(2)
112 else:
113 raise ValueError('bad -dM output line: %s' % line)
114 if name in macros_exp:
115 raise ValueError('duplicate macro: %s' % line)
116 macros_exp[name] = value
117 macros_args[name] = args
118 return macros_exp, macros_args
119
120
121def compute_macro_consts(source_text, cc, macro_re, exclude_re=None):
122 """Compute the integer constant values of macros defined by source_text.
123
124 Macros must match the regular expression macro_re, and if
125 exclude_re is defined they must not match exclude_re. Values are
126 computed with compute_c_consts.
127
128 """
129 macros_exp, macros_args = list_macros(source_text, cc)
130 macros_set = {m for m in macros_exp
131 if (macros_args[m] is None
132 and re.fullmatch(macro_re, m)
133 and (exclude_re is None
134 or not re.fullmatch(exclude_re, m)))}
135 sym_data = [source_text, 'START']
136 sym_data.extend(sorted((m, m) for m in macros_set))
137 return compute_c_consts(sym_data, cc)
138
139
df648905
JM
140def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None,
141 allow_extra_1=False, allow_extra_2=False):
a8110b72
JM
142 """Compare the values of macros defined by two different sources.
143
144 The sources would typically be includes of a glibc header and a
df648905
JM
145 kernel header. If allow_extra_1, the first source may define
146 extra macros (typically if the kernel headers are older than the
147 version glibc has taken definitions from); if allow_extra_2, the
148 second source may define extra macros (typically if the kernel
149 headers are newer than the version glibc has taken definitions
150 from). Return 1 if there were any differences other than those
151 allowed, 0 if the macro values were the same apart from any
152 allowed differences.
a8110b72
JM
153
154 """
155 macros_1 = compute_macro_consts(source_1, cc, macro_re, exclude_re)
156 macros_2 = compute_macro_consts(source_2, cc, macro_re, exclude_re)
157 if macros_1 == macros_2:
158 return 0
159 print('First source:\n%s\n' % source_1)
160 print('Second source:\n%s\n' % source_2)
df648905 161 ret = 0
a8110b72
JM
162 for name, value in sorted(macros_1.items()):
163 if name not in macros_2:
164 print('Only in first source: %s' % name)
df648905
JM
165 if not allow_extra_1:
166 ret = 1
a8110b72
JM
167 elif macros_1[name] != macros_2[name]:
168 print('Different values for %s: %s != %s'
169 % (name, macros_1[name], macros_2[name]))
df648905 170 ret = 1
a8110b72
JM
171 for name in sorted(macros_2.keys()):
172 if name not in macros_1:
173 print('Only in second source: %s' % name)
df648905
JM
174 if not allow_extra_2:
175 ret = 1
176 return ret
841afa11
AZ
177
178CompileResult = collections.namedtuple("CompileResult", "returncode output")
179
180def compile_c_snippet(snippet, cc, extra_cc_args=''):
181 """Compile and return whether the SNIPPET can be build with CC along
182 EXTRA_CC_ARGS compiler flags. Return a CompileResult with RETURNCODE
183 being 0 for success, or the failure value and the compiler output.
184 """
185 with tempfile.TemporaryDirectory() as temp_dir:
186 c_file_name = os.path.join(temp_dir, 'test.c')
187 obj_file_name = os.path.join(temp_dir, 'test.o')
188 with open(c_file_name, 'w') as c_file:
189 c_file.write(snippet + '\n')
190 cmd = cc.split() + extra_cc_args.split() + ['-c', '-o', obj_file_name,
191 c_file_name]
192 r = subprocess.run(cmd, check=False, stdout=subprocess.PIPE,
193 stderr=subprocess.STDOUT)
194 return CompileResult(r.returncode, r.stdout)