]>
Commit | Line | Data |
---|---|---|
14f95a42 JM |
1 | #!/usr/bin/python3 |
2 | # Build many configurations of glibc. | |
26420023 | 3 | # Copyright (C) 2016-2025 Free Software Foundation, Inc. |
72e4a717 | 4 | # Copyright The GNU Toolchain Authors. |
14f95a42 JM |
5 | # This file is part of the GNU C Library. |
6 | # | |
7 | # The GNU C Library is free software; you can redistribute it and/or | |
8 | # modify it under the terms of the GNU Lesser General Public | |
9 | # License as published by the Free Software Foundation; either | |
10 | # version 2.1 of the License, or (at your option) any later version. | |
11 | # | |
12 | # The GNU C Library is distributed in the hope that it will be useful, | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | # Lesser General Public License for more details. | |
16 | # | |
17 | # You should have received a copy of the GNU Lesser General Public | |
18 | # License along with the GNU C Library; if not, see | |
5a82c748 | 19 | # <https://www.gnu.org/licenses/>. |
14f95a42 JM |
20 | |
21 | """Build many configurations of glibc. | |
22 | ||
23 | This script takes as arguments a directory name (containing a src | |
24 | subdirectory with sources of the relevant toolchain components) and a | |
25 | description of what to do: 'checkout', to check out sources into that | |
4d602bce | 26 | directory, 'bot-cycle', to run a series of checkout and build steps, |
a1f6a9ab JM |
27 | 'bot', to run 'bot-cycle' repeatedly, 'host-libraries', to build |
28 | libraries required by the toolchain, 'compilers', to build | |
29 | cross-compilers for various configurations, or 'glibcs', to build | |
30 | glibc for various configurations and run the compilation parts of the | |
31 | testsuite. Subsequent arguments name the versions of components to | |
32 | check out (<component>-<version), for 'checkout', or, for actions | |
33 | other than 'checkout' and 'bot-cycle', name configurations for which | |
34 | compilers or glibc are to be built. | |
4d602bce | 35 | |
feaa1506 FW |
36 | The 'list-compilers' command prints the name of each available |
37 | compiler configuration, without building anything. The 'list-glibcs' | |
38 | command prints the name of each glibc compiler configuration, followed | |
39 | by the space, followed by the name of the compiler configuration used | |
40 | for building this glibc variant. | |
41 | ||
14f95a42 JM |
42 | """ |
43 | ||
44 | import argparse | |
bf469f0c | 45 | import datetime |
4d602bce JM |
46 | import email.mime.text |
47 | import email.utils | |
02c78f02 | 48 | import json |
14f95a42 | 49 | import os |
14f95a42 JM |
50 | import re |
51 | import shutil | |
4d602bce | 52 | import smtplib |
14f95a42 JM |
53 | import stat |
54 | import subprocess | |
55 | import sys | |
a1f6a9ab | 56 | import time |
14f95a42 JM |
57 | import urllib.request |
58 | ||
d846c283 DD |
59 | # This is a list of system utilities that are expected to be available |
60 | # to this script, and, if a non-zero version is included, the minimum | |
61 | # version required to work with this sccript. | |
62 | def get_list_of_required_tools(): | |
63 | global REQUIRED_TOOLS | |
64 | REQUIRED_TOOLS = { | |
65 | 'awk' : (get_version_awk, (0,0,0)), | |
66 | 'bison' : (get_version, (0,0)), | |
67 | 'flex' : (get_version, (0,0,0)), | |
68 | 'git' : (get_version, (1,8,3)), | |
69 | 'make' : (get_version, (4,0)), | |
70 | 'makeinfo' : (get_version, (0,0)), | |
71 | 'patch' : (get_version, (0,0,0)), | |
72 | 'sed' : (get_version, (0,0)), | |
73 | 'tar' : (get_version, (0,0,0)), | |
74 | 'gzip' : (get_version, (0,0)), | |
75 | 'bzip2' : (get_version_bzip2, (0,0,0)), | |
76 | 'xz' : (get_version, (0,0,0)), | |
77 | } | |
78 | ||
64235ccc SN |
79 | try: |
80 | subprocess.run | |
81 | except: | |
82 | class _CompletedProcess: | |
83 | def __init__(self, args, returncode, stdout=None, stderr=None): | |
84 | self.args = args | |
85 | self.returncode = returncode | |
86 | self.stdout = stdout | |
87 | self.stderr = stderr | |
88 | ||
89 | def _run(*popenargs, input=None, timeout=None, check=False, **kwargs): | |
90 | assert(timeout is None) | |
91 | with subprocess.Popen(*popenargs, **kwargs) as process: | |
92 | try: | |
93 | stdout, stderr = process.communicate(input) | |
94 | except: | |
95 | process.kill() | |
96 | process.wait() | |
97 | raise | |
98 | returncode = process.poll() | |
99 | if check and returncode: | |
100 | raise subprocess.CalledProcessError(returncode, popenargs) | |
101 | return _CompletedProcess(popenargs, returncode, stdout, stderr) | |
102 | ||
103 | subprocess.run = _run | |
104 | ||
14f95a42 | 105 | |
0c95f51d | 106 | class Context(object): |
14f95a42 JM |
107 | """The global state associated with builds in a given directory.""" |
108 | ||
297635d8 | 109 | def __init__(self, topdir, parallelism, keep, replace_sources, strip, |
757ac24f | 110 | full_gcc, action, exclude, shallow=False): |
14f95a42 JM |
111 | """Initialize the context.""" |
112 | self.topdir = topdir | |
113 | self.parallelism = parallelism | |
114 | self.keep = keep | |
02c78f02 | 115 | self.replace_sources = replace_sources |
297635d8 | 116 | self.strip = strip |
b1176270 | 117 | self.full_gcc = full_gcc |
757ac24f | 118 | self.exclude = exclude |
c592721a | 119 | self.shallow = shallow |
14f95a42 | 120 | self.srcdir = os.path.join(topdir, 'src') |
02c78f02 | 121 | self.versions_json = os.path.join(self.srcdir, 'versions.json') |
bf469f0c | 122 | self.build_state_json = os.path.join(topdir, 'build-state.json') |
4d602bce | 123 | self.bot_config_json = os.path.join(topdir, 'bot-config.json') |
14f95a42 JM |
124 | self.installdir = os.path.join(topdir, 'install') |
125 | self.host_libraries_installdir = os.path.join(self.installdir, | |
126 | 'host-libraries') | |
127 | self.builddir = os.path.join(topdir, 'build') | |
128 | self.logsdir = os.path.join(topdir, 'logs') | |
a1f6a9ab | 129 | self.logsdir_old = os.path.join(topdir, 'logs-old') |
14f95a42 JM |
130 | self.makefile = os.path.join(self.builddir, 'Makefile') |
131 | self.wrapper = os.path.join(self.builddir, 'wrapper') | |
132 | self.save_logs = os.path.join(self.builddir, 'save-logs') | |
a1c9859b | 133 | self.script_text = self.get_script_text() |
feaa1506 | 134 | if action not in ('checkout', 'list-compilers', 'list-glibcs'): |
14f95a42 JM |
135 | self.build_triplet = self.get_build_triplet() |
136 | self.glibc_version = self.get_glibc_version() | |
137 | self.configs = {} | |
138 | self.glibc_configs = {} | |
139 | self.makefile_pieces = ['.PHONY: all\n'] | |
140 | self.add_all_configs() | |
02c78f02 | 141 | self.load_versions_json() |
bf469f0c JM |
142 | self.load_build_state_json() |
143 | self.status_log_list = [] | |
f0166c16 | 144 | self.email_warning = False |
14f95a42 | 145 | |
a1c9859b JM |
146 | def get_script_text(self): |
147 | """Return the text of this script.""" | |
148 | with open(sys.argv[0], 'r') as f: | |
149 | return f.read() | |
150 | ||
151 | def exec_self(self): | |
152 | """Re-execute this script with the same arguments.""" | |
36820ce9 | 153 | sys.stdout.flush() |
a1c9859b JM |
154 | os.execv(sys.executable, [sys.executable] + sys.argv) |
155 | ||
14f95a42 JM |
156 | def get_build_triplet(self): |
157 | """Determine the build triplet with config.guess.""" | |
158 | config_guess = os.path.join(self.component_srcdir('gcc'), | |
159 | 'config.guess') | |
160 | cg_out = subprocess.run([config_guess], stdout=subprocess.PIPE, | |
161 | check=True, universal_newlines=True).stdout | |
162 | return cg_out.rstrip() | |
163 | ||
164 | def get_glibc_version(self): | |
165 | """Determine the glibc version number (major.minor).""" | |
166 | version_h = os.path.join(self.component_srcdir('glibc'), 'version.h') | |
167 | with open(version_h, 'r') as f: | |
168 | lines = f.readlines() | |
169 | starttext = '#define VERSION "' | |
170 | for l in lines: | |
171 | if l.startswith(starttext): | |
172 | l = l[len(starttext):] | |
173 | l = l.rstrip('"\n') | |
78142735 | 174 | m = re.fullmatch(r'([0-9]+)\.([0-9]+)[.0-9]*', l) |
14f95a42 JM |
175 | return '%s.%s' % m.group(1, 2) |
176 | print('error: could not determine glibc version') | |
177 | exit(1) | |
178 | ||
179 | def add_all_configs(self): | |
180 | """Add all known glibc build configurations.""" | |
181 | self.add_config(arch='aarch64', | |
6cb86fd2 SN |
182 | os_name='linux-gnu', |
183 | extra_glibcs=[{'variant': 'disable-multi-arch', | |
184 | 'cfg': ['--disable-multi-arch']}]) | |
14f95a42 JM |
185 | self.add_config(arch='aarch64_be', |
186 | os_name='linux-gnu') | |
2fc2260b VG |
187 | self.add_config(arch='arc', |
188 | os_name='linux-gnu', | |
189 | gcc_cfg=['--disable-multilib', '--with-cpu=hs38']) | |
190 | self.add_config(arch='arc', | |
191 | os_name='linux-gnuhf', | |
192 | gcc_cfg=['--disable-multilib', '--with-cpu=hs38_linux']) | |
14f95a42 JM |
193 | self.add_config(arch='alpha', |
194 | os_name='linux-gnu') | |
195 | self.add_config(arch='arm', | |
72edea80 FW |
196 | os_name='linux-gnueabi', |
197 | extra_glibcs=[{'variant': 'v4t', | |
198 | 'ccopts': '-march=armv4t'}]) | |
14f95a42 JM |
199 | self.add_config(arch='armeb', |
200 | os_name='linux-gnueabi') | |
201 | self.add_config(arch='armeb', | |
202 | os_name='linux-gnueabi', | |
203 | variant='be8', | |
204 | gcc_cfg=['--with-arch=armv7-a']) | |
205 | self.add_config(arch='arm', | |
3d265601 | 206 | os_name='linux-gnueabihf', |
8e52f573 | 207 | gcc_cfg=['--with-float=hard', '--with-cpu=arm926ej-s'], |
3d265601 | 208 | extra_glibcs=[{'variant': 'v7a', |
8e52f573 | 209 | 'ccopts': '-march=armv7-a -mfpu=vfpv3'}, |
1e5235c5 FW |
210 | {'variant': 'thumb', |
211 | 'ccopts': | |
212 | '-mthumb -march=armv7-a -mfpu=vfpv3'}, | |
3d265601 | 213 | {'variant': 'v7a-disable-multi-arch', |
8e52f573 | 214 | 'ccopts': '-march=armv7-a -mfpu=vfpv3', |
3d265601 | 215 | 'cfg': ['--disable-multi-arch']}]) |
14f95a42 | 216 | self.add_config(arch='armeb', |
5170fa49 | 217 | os_name='linux-gnueabihf', |
8e52f573 | 218 | gcc_cfg=['--with-float=hard', '--with-cpu=arm926ej-s']) |
14f95a42 JM |
219 | self.add_config(arch='armeb', |
220 | os_name='linux-gnueabihf', | |
221 | variant='be8', | |
8e52f573 JM |
222 | gcc_cfg=['--with-float=hard', '--with-arch=armv7-a', |
223 | '--with-fpu=vfpv3']) | |
5f72b005 MH |
224 | self.add_config(arch='csky', |
225 | os_name='linux-gnuabiv2', | |
226 | variant='soft', | |
227 | gcc_cfg=['--disable-multilib']) | |
228 | self.add_config(arch='csky', | |
229 | os_name='linux-gnuabiv2', | |
230 | gcc_cfg=['--with-float=hard', '--disable-multilib']) | |
14f95a42 JM |
231 | self.add_config(arch='hppa', |
232 | os_name='linux-gnu') | |
394b5bac JM |
233 | self.add_config(arch='i686', |
234 | os_name='gnu') | |
3be5fc00 | 235 | self.add_config(arch='loongarch64', |
236 | os_name='linux-gnu', | |
237 | variant='lp64d', | |
238 | gcc_cfg=['--with-abi=lp64d','--disable-multilib']) | |
93099392 | 239 | self.add_config(arch='loongarch64', |
240 | os_name='linux-gnu', | |
241 | variant='lp64s', | |
242 | gcc_cfg=['--with-abi=lp64s','--disable-multilib']) | |
14f95a42 JM |
243 | self.add_config(arch='m68k', |
244 | os_name='linux-gnu', | |
245 | gcc_cfg=['--disable-multilib']) | |
246 | self.add_config(arch='m68k', | |
247 | os_name='linux-gnu', | |
248 | variant='coldfire', | |
249 | gcc_cfg=['--with-arch=cf', '--disable-multilib']) | |
3005b705 JM |
250 | self.add_config(arch='m68k', |
251 | os_name='linux-gnu', | |
252 | variant='coldfire-soft', | |
253 | gcc_cfg=['--with-arch=cf', '--with-cpu=54455', | |
254 | '--disable-multilib']) | |
14f95a42 JM |
255 | self.add_config(arch='microblaze', |
256 | os_name='linux-gnu', | |
257 | gcc_cfg=['--disable-multilib']) | |
258 | self.add_config(arch='microblazeel', | |
259 | os_name='linux-gnu', | |
260 | gcc_cfg=['--disable-multilib']) | |
261 | self.add_config(arch='mips64', | |
262 | os_name='linux-gnu', | |
263 | gcc_cfg=['--with-mips-plt'], | |
264 | glibcs=[{'variant': 'n32'}, | |
265 | {'arch': 'mips', | |
266 | 'ccopts': '-mabi=32'}, | |
267 | {'variant': 'n64', | |
268 | 'ccopts': '-mabi=64'}]) | |
269 | self.add_config(arch='mips64', | |
270 | os_name='linux-gnu', | |
271 | variant='soft', | |
272 | gcc_cfg=['--with-mips-plt', '--with-float=soft'], | |
8df5d347 | 273 | glibcs=[{'variant': 'n32-soft'}, |
14f95a42 JM |
274 | {'variant': 'soft', |
275 | 'arch': 'mips', | |
8df5d347 | 276 | 'ccopts': '-mabi=32'}, |
14f95a42 | 277 | {'variant': 'n64-soft', |
8df5d347 | 278 | 'ccopts': '-mabi=64'}]) |
14f95a42 JM |
279 | self.add_config(arch='mips64', |
280 | os_name='linux-gnu', | |
281 | variant='nan2008', | |
282 | gcc_cfg=['--with-mips-plt', '--with-nan=2008', | |
283 | '--with-arch-64=mips64r2', | |
284 | '--with-arch-32=mips32r2'], | |
285 | glibcs=[{'variant': 'n32-nan2008'}, | |
286 | {'variant': 'nan2008', | |
287 | 'arch': 'mips', | |
288 | 'ccopts': '-mabi=32'}, | |
289 | {'variant': 'n64-nan2008', | |
290 | 'ccopts': '-mabi=64'}]) | |
291 | self.add_config(arch='mips64', | |
292 | os_name='linux-gnu', | |
293 | variant='nan2008-soft', | |
294 | gcc_cfg=['--with-mips-plt', '--with-nan=2008', | |
295 | '--with-arch-64=mips64r2', | |
296 | '--with-arch-32=mips32r2', | |
297 | '--with-float=soft'], | |
8df5d347 | 298 | glibcs=[{'variant': 'n32-nan2008-soft'}, |
14f95a42 JM |
299 | {'variant': 'nan2008-soft', |
300 | 'arch': 'mips', | |
8df5d347 | 301 | 'ccopts': '-mabi=32'}, |
14f95a42 | 302 | {'variant': 'n64-nan2008-soft', |
8df5d347 | 303 | 'ccopts': '-mabi=64'}]) |
14f95a42 JM |
304 | self.add_config(arch='mips64el', |
305 | os_name='linux-gnu', | |
306 | gcc_cfg=['--with-mips-plt'], | |
307 | glibcs=[{'variant': 'n32'}, | |
308 | {'arch': 'mipsel', | |
309 | 'ccopts': '-mabi=32'}, | |
310 | {'variant': 'n64', | |
311 | 'ccopts': '-mabi=64'}]) | |
312 | self.add_config(arch='mips64el', | |
313 | os_name='linux-gnu', | |
314 | variant='soft', | |
315 | gcc_cfg=['--with-mips-plt', '--with-float=soft'], | |
8df5d347 | 316 | glibcs=[{'variant': 'n32-soft'}, |
14f95a42 JM |
317 | {'variant': 'soft', |
318 | 'arch': 'mipsel', | |
8df5d347 | 319 | 'ccopts': '-mabi=32'}, |
14f95a42 | 320 | {'variant': 'n64-soft', |
8df5d347 | 321 | 'ccopts': '-mabi=64'}]) |
14f95a42 JM |
322 | self.add_config(arch='mips64el', |
323 | os_name='linux-gnu', | |
324 | variant='nan2008', | |
325 | gcc_cfg=['--with-mips-plt', '--with-nan=2008', | |
326 | '--with-arch-64=mips64r2', | |
327 | '--with-arch-32=mips32r2'], | |
328 | glibcs=[{'variant': 'n32-nan2008'}, | |
329 | {'variant': 'nan2008', | |
330 | 'arch': 'mipsel', | |
331 | 'ccopts': '-mabi=32'}, | |
332 | {'variant': 'n64-nan2008', | |
333 | 'ccopts': '-mabi=64'}]) | |
334 | self.add_config(arch='mips64el', | |
335 | os_name='linux-gnu', | |
336 | variant='nan2008-soft', | |
337 | gcc_cfg=['--with-mips-plt', '--with-nan=2008', | |
338 | '--with-arch-64=mips64r2', | |
339 | '--with-arch-32=mips32r2', | |
340 | '--with-float=soft'], | |
8df5d347 | 341 | glibcs=[{'variant': 'n32-nan2008-soft'}, |
14f95a42 JM |
342 | {'variant': 'nan2008-soft', |
343 | 'arch': 'mipsel', | |
8df5d347 | 344 | 'ccopts': '-mabi=32'}, |
14f95a42 | 345 | {'variant': 'n64-nan2008-soft', |
8df5d347 | 346 | 'ccopts': '-mabi=64'}]) |
a2e487ce DM |
347 | self.add_config(arch='mipsisa64r6el', |
348 | os_name='linux-gnu', | |
349 | gcc_cfg=['--with-mips-plt', '--with-nan=2008', | |
350 | '--with-arch-64=mips64r6', | |
351 | '--with-arch-32=mips32r6', | |
352 | '--with-float=hard'], | |
353 | glibcs=[{'variant': 'n32'}, | |
354 | {'arch': 'mipsisa32r6el', | |
355 | 'ccopts': '-mabi=32'}, | |
356 | {'variant': 'n64', | |
357 | 'ccopts': '-mabi=64'}]) | |
e9816506 SH |
358 | self.add_config(arch='or1k', |
359 | os_name='linux-gnu', | |
ea73eb5f SH |
360 | gcc_cfg=['--with-multilib-list=mcmov,mhard-float'], |
361 | glibcs=[{'variant': 'soft'}, | |
362 | {'variant': 'hard', 'ccopts': '-mhard-float'}]) | |
14f95a42 JM |
363 | self.add_config(arch='powerpc', |
364 | os_name='linux-gnu', | |
4179178b JM |
365 | gcc_cfg=['--disable-multilib', '--enable-secureplt'], |
366 | extra_glibcs=[{'variant': 'power4', | |
367 | 'ccopts': '-mcpu=power4', | |
368 | 'cfg': ['--with-cpu=power4']}]) | |
14f95a42 JM |
369 | self.add_config(arch='powerpc', |
370 | os_name='linux-gnu', | |
371 | variant='soft', | |
372 | gcc_cfg=['--disable-multilib', '--with-float=soft', | |
8df5d347 | 373 | '--enable-secureplt']) |
14f95a42 JM |
374 | self.add_config(arch='powerpc64', |
375 | os_name='linux-gnu', | |
376 | gcc_cfg=['--disable-multilib', '--enable-secureplt']) | |
377 | self.add_config(arch='powerpc64le', | |
378 | os_name='linux-gnu', | |
4ae2b8a4 PM |
379 | gcc_cfg=['--disable-multilib', '--enable-secureplt'], |
380 | extra_glibcs=[{'variant': 'disable-multi-arch', | |
381 | 'cfg': ['--disable-multi-arch']}]) | |
567b1705 ZL |
382 | self.add_config(arch='riscv32', |
383 | os_name='linux-gnu', | |
384 | variant='rv32imac-ilp32', | |
385 | gcc_cfg=['--with-arch=rv32imac', '--with-abi=ilp32', | |
386 | '--disable-multilib']) | |
387 | self.add_config(arch='riscv32', | |
388 | os_name='linux-gnu', | |
389 | variant='rv32imafdc-ilp32', | |
390 | gcc_cfg=['--with-arch=rv32imafdc', '--with-abi=ilp32', | |
391 | '--disable-multilib']) | |
392 | self.add_config(arch='riscv32', | |
393 | os_name='linux-gnu', | |
394 | variant='rv32imafdc-ilp32d', | |
395 | gcc_cfg=['--with-arch=rv32imafdc', '--with-abi=ilp32d', | |
396 | '--disable-multilib']) | |
0fd5d876 PD |
397 | self.add_config(arch='riscv64', |
398 | os_name='linux-gnu', | |
399 | variant='rv64imac-lp64', | |
400 | gcc_cfg=['--with-arch=rv64imac', '--with-abi=lp64', | |
401 | '--disable-multilib']) | |
402 | self.add_config(arch='riscv64', | |
403 | os_name='linux-gnu', | |
404 | variant='rv64imafdc-lp64', | |
405 | gcc_cfg=['--with-arch=rv64imafdc', '--with-abi=lp64', | |
406 | '--disable-multilib']) | |
407 | self.add_config(arch='riscv64', | |
408 | os_name='linux-gnu', | |
409 | variant='rv64imafdc-lp64d', | |
410 | gcc_cfg=['--with-arch=rv64imafdc', '--with-abi=lp64d', | |
411 | '--disable-multilib']) | |
14f95a42 JM |
412 | self.add_config(arch='s390x', |
413 | os_name='linux-gnu', | |
414 | glibcs=[{}, | |
33dd32fd SL |
415 | {'arch': 's390', 'ccopts': '-m31'}], |
416 | extra_glibcs=[{'variant': 'O3', | |
2938846a SL |
417 | 'cflags': '-O3'}, |
418 | {'variant': 'zEC12', | |
419 | 'ccopts': '-march=zEC12'}, | |
420 | {'variant': 'z13', | |
421 | 'ccopts': '-march=z13'}, | |
422 | {'variant': 'z15', | |
423 | 'ccopts': '-march=z15'}, | |
424 | {'variant': 'zEC12-disable-multi-arch', | |
425 | 'ccopts': '-march=zEC12', | |
426 | 'cfg': ['--disable-multi-arch']}, | |
427 | {'variant': 'z13-disable-multi-arch', | |
428 | 'ccopts': '-march=z13', | |
429 | 'cfg': ['--disable-multi-arch']}, | |
430 | {'variant': 'z15-disable-multi-arch', | |
431 | 'ccopts': '-march=z15', | |
432 | 'cfg': ['--disable-multi-arch']}]) | |
14f95a42 | 433 | self.add_config(arch='sh3', |
c89721e2 | 434 | os_name='linux-gnu') |
14f95a42 | 435 | self.add_config(arch='sh3eb', |
c89721e2 | 436 | os_name='linux-gnu') |
14f95a42 | 437 | self.add_config(arch='sh4', |
c89721e2 | 438 | os_name='linux-gnu') |
14f95a42 | 439 | self.add_config(arch='sh4eb', |
c89721e2 | 440 | os_name='linux-gnu') |
14f95a42 JM |
441 | self.add_config(arch='sh4', |
442 | os_name='linux-gnu', | |
443 | variant='soft', | |
8df5d347 | 444 | gcc_cfg=['--without-fp']) |
14f95a42 JM |
445 | self.add_config(arch='sh4eb', |
446 | os_name='linux-gnu', | |
447 | variant='soft', | |
8df5d347 | 448 | gcc_cfg=['--without-fp']) |
14f95a42 JM |
449 | self.add_config(arch='sparc64', |
450 | os_name='linux-gnu', | |
3bffe5aa AZ |
451 | glibcs=[{'cfg' : ['--disable-default-pie']}, |
452 | {'cfg' : ['--disable-default-pie'], | |
453 | 'arch': 'sparcv9', | |
5d9b7b9f | 454 | 'ccopts': '-m32 -mlong-double-128 -mcpu=v9'}], |
066d80a7 | 455 | extra_glibcs=[{'variant': 'leon3', |
3bffe5aa | 456 | 'cfg' : ['--disable-default-pie'], |
066d80a7 AZ |
457 | 'arch' : 'sparcv8', |
458 | 'ccopts' : '-m32 -mlong-double-128 -mcpu=leon3'}, | |
459 | {'variant': 'disable-multi-arch', | |
3bffe5aa | 460 | 'cfg': ['--disable-multi-arch', '--disable-default-pie']}, |
0c097378 JM |
461 | {'variant': 'disable-multi-arch', |
462 | 'arch': 'sparcv9', | |
066d80a7 | 463 | 'ccopts': '-m32 -mlong-double-128 -mcpu=v9', |
3bffe5aa | 464 | 'cfg': ['--disable-multi-arch', '--disable-default-pie']}]) |
14f95a42 JM |
465 | self.add_config(arch='x86_64', |
466 | os_name='linux-gnu', | |
467 | gcc_cfg=['--with-multilib-list=m64,m32,mx32'], | |
468 | glibcs=[{}, | |
469 | {'variant': 'x32', 'ccopts': '-mx32'}, | |
470 | {'arch': 'i686', 'ccopts': '-m32 -march=i686'}], | |
471 | extra_glibcs=[{'variant': 'disable-multi-arch', | |
472 | 'cfg': ['--disable-multi-arch']}, | |
5f18453d FW |
473 | {'variant': 'minimal', |
474 | 'cfg': ['--disable-multi-arch', | |
475 | '--disable-profile', | |
476 | '--disable-timezone-tools', | |
477 | '--disable-mathvec', | |
5f18453d FW |
478 | '--disable-build-nscd', |
479 | '--disable-nscd']}, | |
23645707 SP |
480 | {'variant': 'no-pie', |
481 | 'cfg': ['--disable-default-pie']}, | |
482 | {'variant': 'x32-no-pie', | |
1a49fc59 | 483 | 'ccopts': '-mx32', |
23645707 SP |
484 | 'cfg': ['--disable-default-pie']}, |
485 | {'variant': 'no-pie', | |
1a49fc59 L |
486 | 'arch': 'i686', |
487 | 'ccopts': '-m32 -march=i686', | |
23645707 | 488 | 'cfg': ['--disable-default-pie']}, |
14f95a42 JM |
489 | {'variant': 'disable-multi-arch', |
490 | 'arch': 'i686', | |
491 | 'ccopts': '-m32 -march=i686', | |
492 | 'cfg': ['--disable-multi-arch']}, | |
493 | {'arch': 'i486', | |
494 | 'ccopts': '-m32 -march=i486'}, | |
495 | {'arch': 'i586', | |
64d9580c FB |
496 | 'ccopts': '-m32 -march=i586'}, |
497 | {'variant': 'enable-fortify-source', | |
e6e3c666 | 498 | 'cfg': ['--enable-fortify-source']}]) |
cd87e368 ST |
499 | self.add_config(arch='x86_64', |
500 | os_name='gnu', | |
501 | gcc_cfg=['--disable-multilib']) | |
14f95a42 JM |
502 | |
503 | def add_config(self, **args): | |
504 | """Add an individual build configuration.""" | |
505 | cfg = Config(self, **args) | |
757ac24f L |
506 | if self.exclude and cfg.name in self.exclude: |
507 | return | |
14f95a42 JM |
508 | if cfg.name in self.configs: |
509 | print('error: duplicate config %s' % cfg.name) | |
510 | exit(1) | |
511 | self.configs[cfg.name] = cfg | |
512 | for c in cfg.all_glibcs: | |
513 | if c.name in self.glibc_configs: | |
514 | print('error: duplicate glibc config %s' % c.name) | |
515 | exit(1) | |
516 | self.glibc_configs[c.name] = c | |
517 | ||
518 | def component_srcdir(self, component): | |
519 | """Return the source directory for a given component, e.g. gcc.""" | |
520 | return os.path.join(self.srcdir, component) | |
521 | ||
522 | def component_builddir(self, action, config, component, subconfig=None): | |
523 | """Return the directory to use for a build.""" | |
524 | if config is None: | |
525 | # Host libraries. | |
526 | assert subconfig is None | |
527 | return os.path.join(self.builddir, action, component) | |
528 | if subconfig is None: | |
529 | return os.path.join(self.builddir, action, config, component) | |
530 | else: | |
531 | # glibc build as part of compiler build. | |
532 | return os.path.join(self.builddir, action, config, component, | |
533 | subconfig) | |
534 | ||
535 | def compiler_installdir(self, config): | |
536 | """Return the directory in which to install a compiler.""" | |
537 | return os.path.join(self.installdir, 'compilers', config) | |
538 | ||
539 | def compiler_bindir(self, config): | |
540 | """Return the directory in which to find compiler binaries.""" | |
541 | return os.path.join(self.compiler_installdir(config), 'bin') | |
542 | ||
543 | def compiler_sysroot(self, config): | |
544 | """Return the sysroot directory for a compiler.""" | |
545 | return os.path.join(self.compiler_installdir(config), 'sysroot') | |
546 | ||
547 | def glibc_installdir(self, config): | |
548 | """Return the directory in which to install glibc.""" | |
549 | return os.path.join(self.installdir, 'glibcs', config) | |
550 | ||
551 | def run_builds(self, action, configs): | |
552 | """Run the requested builds.""" | |
553 | if action == 'checkout': | |
554 | self.checkout(configs) | |
555 | return | |
4d602bce JM |
556 | if action == 'bot-cycle': |
557 | if configs: | |
558 | print('error: configurations specified for bot-cycle') | |
559 | exit(1) | |
560 | self.bot_cycle() | |
561 | return | |
a1f6a9ab JM |
562 | if action == 'bot': |
563 | if configs: | |
564 | print('error: configurations specified for bot') | |
565 | exit(1) | |
566 | self.bot() | |
567 | return | |
feaa1506 FW |
568 | if action in ('host-libraries', 'list-compilers', |
569 | 'list-glibcs') and configs: | |
570 | print('error: configurations specified for ' + action) | |
bf469f0c | 571 | exit(1) |
feaa1506 FW |
572 | if action == 'list-compilers': |
573 | for name in sorted(self.configs.keys()): | |
574 | print(name) | |
575 | return | |
576 | if action == 'list-glibcs': | |
577 | for config in sorted(self.glibc_configs.values(), | |
578 | key=lambda c: c.name): | |
579 | print(config.name, config.compiler.name) | |
580 | return | |
bf469f0c | 581 | self.clear_last_build_state(action) |
9da45337 | 582 | build_time = datetime.datetime.now(datetime.timezone.utc) |
bf469f0c JM |
583 | if action == 'host-libraries': |
584 | build_components = ('gmp', 'mpfr', 'mpc') | |
585 | old_components = () | |
586 | old_versions = {} | |
14f95a42 JM |
587 | self.build_host_libraries() |
588 | elif action == 'compilers': | |
394b5bac JM |
589 | build_components = ('binutils', 'gcc', 'glibc', 'linux', 'mig', |
590 | 'gnumach', 'hurd') | |
bf469f0c JM |
591 | old_components = ('gmp', 'mpfr', 'mpc') |
592 | old_versions = self.build_state['host-libraries']['build-versions'] | |
14f95a42 JM |
593 | self.build_compilers(configs) |
594 | else: | |
bf469f0c | 595 | build_components = ('glibc',) |
394b5bac JM |
596 | old_components = ('gmp', 'mpfr', 'mpc', 'binutils', 'gcc', 'linux', |
597 | 'mig', 'gnumach', 'hurd') | |
bf469f0c | 598 | old_versions = self.build_state['compilers']['build-versions'] |
07a44d23 FW |
599 | if action == 'update-syscalls': |
600 | self.update_syscalls(configs) | |
601 | else: | |
602 | self.build_glibcs(configs) | |
14f95a42 JM |
603 | self.write_files() |
604 | self.do_build() | |
bf469f0c JM |
605 | if configs: |
606 | # Partial build, do not update stored state. | |
607 | return | |
608 | build_versions = {} | |
609 | for k in build_components: | |
610 | if k in self.versions: | |
611 | build_versions[k] = {'version': self.versions[k]['version'], | |
612 | 'revision': self.versions[k]['revision']} | |
613 | for k in old_components: | |
614 | if k in old_versions: | |
615 | build_versions[k] = {'version': old_versions[k]['version'], | |
616 | 'revision': old_versions[k]['revision']} | |
617 | self.update_build_state(action, build_time, build_versions) | |
14f95a42 JM |
618 | |
619 | @staticmethod | |
620 | def remove_dirs(*args): | |
621 | """Remove directories and their contents if they exist.""" | |
622 | for dir in args: | |
623 | shutil.rmtree(dir, ignore_errors=True) | |
624 | ||
625 | @staticmethod | |
626 | def remove_recreate_dirs(*args): | |
627 | """Remove directories if they exist, and create them as empty.""" | |
628 | Context.remove_dirs(*args) | |
629 | for dir in args: | |
630 | os.makedirs(dir, exist_ok=True) | |
631 | ||
632 | def add_makefile_cmdlist(self, target, cmdlist, logsdir): | |
633 | """Add makefile text for a list of commands.""" | |
634 | commands = cmdlist.makefile_commands(self.wrapper, logsdir) | |
635 | self.makefile_pieces.append('all: %s\n.PHONY: %s\n%s:\n%s\n' % | |
636 | (target, target, target, commands)) | |
bf469f0c | 637 | self.status_log_list.extend(cmdlist.status_logs(logsdir)) |
14f95a42 JM |
638 | |
639 | def write_files(self): | |
640 | """Write out the Makefile and wrapper script.""" | |
641 | mftext = ''.join(self.makefile_pieces) | |
642 | with open(self.makefile, 'w') as f: | |
643 | f.write(mftext) | |
644 | wrapper_text = ( | |
645 | '#!/bin/sh\n' | |
646 | 'prev_base=$1\n' | |
647 | 'this_base=$2\n' | |
648 | 'desc=$3\n' | |
649 | 'dir=$4\n' | |
650 | 'path=$5\n' | |
651 | 'shift 5\n' | |
652 | 'prev_status=$prev_base-status.txt\n' | |
653 | 'this_status=$this_base-status.txt\n' | |
654 | 'this_log=$this_base-log.txt\n' | |
655 | 'date > "$this_log"\n' | |
656 | 'echo >> "$this_log"\n' | |
657 | 'echo "Description: $desc" >> "$this_log"\n' | |
8885f979 JM |
658 | 'printf "%s" "Command:" >> "$this_log"\n' |
659 | 'for word in "$@"; do\n' | |
660 | ' if expr "$word" : "[]+,./0-9@A-Z_a-z-]\\\\{1,\\\\}\\$" > /dev/null; then\n' | |
661 | ' printf " %s" "$word"\n' | |
662 | ' else\n' | |
663 | ' printf " \'"\n' | |
664 | ' printf "%s" "$word" | sed -e "s/\'/\'\\\\\\\\\'\'/"\n' | |
665 | ' printf "\'"\n' | |
666 | ' fi\n' | |
667 | 'done >> "$this_log"\n' | |
668 | 'echo >> "$this_log"\n' | |
14f95a42 JM |
669 | 'echo "Directory: $dir" >> "$this_log"\n' |
670 | 'echo "Path addition: $path" >> "$this_log"\n' | |
671 | 'echo >> "$this_log"\n' | |
672 | 'record_status ()\n' | |
673 | '{\n' | |
674 | ' echo >> "$this_log"\n' | |
675 | ' echo "$1: $desc" > "$this_status"\n' | |
676 | ' echo "$1: $desc" >> "$this_log"\n' | |
677 | ' echo >> "$this_log"\n' | |
678 | ' date >> "$this_log"\n' | |
679 | ' echo "$1: $desc"\n' | |
680 | ' exit 0\n' | |
681 | '}\n' | |
682 | 'check_error ()\n' | |
683 | '{\n' | |
684 | ' if [ "$1" != "0" ]; then\n' | |
685 | ' record_status FAIL\n' | |
686 | ' fi\n' | |
687 | '}\n' | |
688 | 'if [ "$prev_base" ] && ! grep -q "^PASS" "$prev_status"; then\n' | |
689 | ' record_status UNRESOLVED\n' | |
690 | 'fi\n' | |
691 | 'if [ "$dir" ]; then\n' | |
692 | ' cd "$dir"\n' | |
693 | ' check_error "$?"\n' | |
694 | 'fi\n' | |
695 | 'if [ "$path" ]; then\n' | |
696 | ' PATH=$path:$PATH\n' | |
697 | 'fi\n' | |
698 | '"$@" < /dev/null >> "$this_log" 2>&1\n' | |
699 | 'check_error "$?"\n' | |
700 | 'record_status PASS\n') | |
701 | with open(self.wrapper, 'w') as f: | |
702 | f.write(wrapper_text) | |
0c95f51d JM |
703 | # Mode 0o755. |
704 | mode_exec = (stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP| | |
705 | stat.S_IROTH|stat.S_IXOTH) | |
706 | os.chmod(self.wrapper, mode_exec) | |
14f95a42 JM |
707 | save_logs_text = ( |
708 | '#!/bin/sh\n' | |
709 | 'if ! [ -f tests.sum ]; then\n' | |
710 | ' echo "No test summary available."\n' | |
711 | ' exit 0\n' | |
712 | 'fi\n' | |
713 | 'save_file ()\n' | |
714 | '{\n' | |
715 | ' echo "Contents of $1:"\n' | |
716 | ' echo\n' | |
717 | ' cat "$1"\n' | |
718 | ' echo\n' | |
719 | ' echo "End of contents of $1."\n' | |
720 | ' echo\n' | |
721 | '}\n' | |
722 | 'save_file tests.sum\n' | |
723 | 'non_pass_tests=$(grep -v "^PASS: " tests.sum | sed -e "s/^PASS: //")\n' | |
724 | 'for t in $non_pass_tests; do\n' | |
725 | ' if [ -f "$t.out" ]; then\n' | |
726 | ' save_file "$t.out"\n' | |
727 | ' fi\n' | |
728 | 'done\n') | |
729 | with open(self.save_logs, 'w') as f: | |
730 | f.write(save_logs_text) | |
0c95f51d | 731 | os.chmod(self.save_logs, mode_exec) |
14f95a42 JM |
732 | |
733 | def do_build(self): | |
734 | """Do the actual build.""" | |
418f5783 | 735 | cmd = ['make', '-O', '-j%d' % self.parallelism] |
14f95a42 JM |
736 | subprocess.run(cmd, cwd=self.builddir, check=True) |
737 | ||
738 | def build_host_libraries(self): | |
739 | """Build the host libraries.""" | |
740 | installdir = self.host_libraries_installdir | |
741 | builddir = os.path.join(self.builddir, 'host-libraries') | |
742 | logsdir = os.path.join(self.logsdir, 'host-libraries') | |
743 | self.remove_recreate_dirs(installdir, builddir, logsdir) | |
744 | cmdlist = CommandList('host-libraries', self.keep) | |
08d7243a JM |
745 | # This CFLAGS setting works around GMP 6.3.0's configure |
746 | # script being incompatible with compilers defaulting to C23 | |
747 | # and should be removed when this script is updated to use a | |
748 | # release of GMP from after that configure test was fixed in | |
749 | # Jan 2025. | |
750 | self.build_host_library(cmdlist, 'gmp', | |
751 | ['CFLAGS=-Wall -O2 -std=gnu17']) | |
14f95a42 JM |
752 | self.build_host_library(cmdlist, 'mpfr', |
753 | ['--with-gmp=%s' % installdir]) | |
754 | self.build_host_library(cmdlist, 'mpc', | |
755 | ['--with-gmp=%s' % installdir, | |
756 | '--with-mpfr=%s' % installdir]) | |
757 | cmdlist.add_command('done', ['touch', os.path.join(installdir, 'ok')]) | |
758 | self.add_makefile_cmdlist('host-libraries', cmdlist, logsdir) | |
759 | ||
760 | def build_host_library(self, cmdlist, lib, extra_opts=None): | |
761 | """Build one host library.""" | |
762 | srcdir = self.component_srcdir(lib) | |
763 | builddir = self.component_builddir('host-libraries', None, lib) | |
764 | installdir = self.host_libraries_installdir | |
765 | cmdlist.push_subdesc(lib) | |
766 | cmdlist.create_use_dir(builddir) | |
767 | cfg_cmd = [os.path.join(srcdir, 'configure'), | |
768 | '--prefix=%s' % installdir, | |
769 | '--disable-shared'] | |
770 | if extra_opts: | |
771 | cfg_cmd.extend (extra_opts) | |
772 | cmdlist.add_command('configure', cfg_cmd) | |
773 | cmdlist.add_command('build', ['make']) | |
774 | cmdlist.add_command('check', ['make', 'check']) | |
775 | cmdlist.add_command('install', ['make', 'install']) | |
776 | cmdlist.cleanup_dir() | |
777 | cmdlist.pop_subdesc() | |
778 | ||
779 | def build_compilers(self, configs): | |
780 | """Build the compilers.""" | |
781 | if not configs: | |
782 | self.remove_dirs(os.path.join(self.builddir, 'compilers')) | |
783 | self.remove_dirs(os.path.join(self.installdir, 'compilers')) | |
784 | self.remove_dirs(os.path.join(self.logsdir, 'compilers')) | |
785 | configs = sorted(self.configs.keys()) | |
786 | for c in configs: | |
787 | self.configs[c].build() | |
788 | ||
789 | def build_glibcs(self, configs): | |
790 | """Build the glibcs.""" | |
791 | if not configs: | |
792 | self.remove_dirs(os.path.join(self.builddir, 'glibcs')) | |
793 | self.remove_dirs(os.path.join(self.installdir, 'glibcs')) | |
794 | self.remove_dirs(os.path.join(self.logsdir, 'glibcs')) | |
795 | configs = sorted(self.glibc_configs.keys()) | |
796 | for c in configs: | |
797 | self.glibc_configs[c].build() | |
798 | ||
07a44d23 FW |
799 | def update_syscalls(self, configs): |
800 | """Update the glibc syscall lists.""" | |
801 | if not configs: | |
802 | self.remove_dirs(os.path.join(self.builddir, 'update-syscalls')) | |
803 | self.remove_dirs(os.path.join(self.logsdir, 'update-syscalls')) | |
804 | configs = sorted(self.glibc_configs.keys()) | |
805 | for c in configs: | |
806 | self.glibc_configs[c].update_syscalls() | |
807 | ||
02c78f02 JM |
808 | def load_versions_json(self): |
809 | """Load information about source directory versions.""" | |
810 | if not os.access(self.versions_json, os.F_OK): | |
811 | self.versions = {} | |
812 | return | |
813 | with open(self.versions_json, 'r') as f: | |
814 | self.versions = json.load(f) | |
815 | ||
816 | def store_json(self, data, filename): | |
817 | """Store information in a JSON file.""" | |
818 | filename_tmp = filename + '.tmp' | |
819 | with open(filename_tmp, 'w') as f: | |
820 | json.dump(data, f, indent=2, sort_keys=True) | |
821 | os.rename(filename_tmp, filename) | |
822 | ||
823 | def store_versions_json(self): | |
824 | """Store information about source directory versions.""" | |
825 | self.store_json(self.versions, self.versions_json) | |
826 | ||
827 | def set_component_version(self, component, version, explicit, revision): | |
828 | """Set the version information for a component.""" | |
829 | self.versions[component] = {'version': version, | |
830 | 'explicit': explicit, | |
831 | 'revision': revision} | |
832 | self.store_versions_json() | |
833 | ||
14f95a42 JM |
834 | def checkout(self, versions): |
835 | """Check out the desired component versions.""" | |
ee3b1d15 | 836 | default_versions = {'binutils': 'vcs-2.44', |
7962fa38 | 837 | 'gcc': 'vcs-14', |
14f95a42 | 838 | 'glibc': 'vcs-mainline', |
d3c34a2d | 839 | 'gmp': '6.3.0', |
34735267 | 840 | 'linux': '6.15', |
d659442e | 841 | 'mpc': '1.3.1', |
8bac7f7a | 842 | 'mpfr': '4.2.2', |
f4d3cee3 | 843 | 'mig': 'vcs-mainline', |
19fd8d0d | 844 | 'gnumach': 'vcs-mainline', |
47c4b4b0 | 845 | 'hurd': 'vcs-mainline'} |
14f95a42 | 846 | use_versions = {} |
02c78f02 | 847 | explicit_versions = {} |
14f95a42 JM |
848 | for v in versions: |
849 | found_v = False | |
850 | for k in default_versions.keys(): | |
851 | kx = k + '-' | |
852 | if v.startswith(kx): | |
853 | vx = v[len(kx):] | |
854 | if k in use_versions: | |
855 | print('error: multiple versions for %s' % k) | |
856 | exit(1) | |
857 | use_versions[k] = vx | |
02c78f02 | 858 | explicit_versions[k] = True |
14f95a42 JM |
859 | found_v = True |
860 | break | |
861 | if not found_v: | |
862 | print('error: unknown component in %s' % v) | |
863 | exit(1) | |
864 | for k in default_versions.keys(): | |
865 | if k not in use_versions: | |
02c78f02 JM |
866 | if k in self.versions and self.versions[k]['explicit']: |
867 | use_versions[k] = self.versions[k]['version'] | |
868 | explicit_versions[k] = True | |
869 | else: | |
870 | use_versions[k] = default_versions[k] | |
871 | explicit_versions[k] = False | |
14f95a42 JM |
872 | os.makedirs(self.srcdir, exist_ok=True) |
873 | for k in sorted(default_versions.keys()): | |
874 | update = os.access(self.component_srcdir(k), os.F_OK) | |
875 | v = use_versions[k] | |
02c78f02 JM |
876 | if (update and |
877 | k in self.versions and | |
878 | v != self.versions[k]['version']): | |
879 | if not self.replace_sources: | |
880 | print('error: version of %s has changed from %s to %s, ' | |
881 | 'use --replace-sources to check out again' % | |
882 | (k, self.versions[k]['version'], v)) | |
883 | exit(1) | |
884 | shutil.rmtree(self.component_srcdir(k)) | |
885 | update = False | |
14f95a42 | 886 | if v.startswith('vcs-'): |
02c78f02 | 887 | revision = self.checkout_vcs(k, v[4:], update) |
14f95a42 JM |
888 | else: |
889 | self.checkout_tar(k, v, update) | |
02c78f02 JM |
890 | revision = v |
891 | self.set_component_version(k, v, explicit_versions[k], revision) | |
a1c9859b JM |
892 | if self.get_script_text() != self.script_text: |
893 | # Rerun the checkout process in case the updated script | |
894 | # uses different default versions or new components. | |
895 | self.exec_self() | |
14f95a42 JM |
896 | |
897 | def checkout_vcs(self, component, version, update): | |
898 | """Check out the given version of the given component from version | |
02c78f02 | 899 | control. Return a revision identifier.""" |
14f95a42 | 900 | if component == 'binutils': |
0784e791 | 901 | git_url = 'https://sourceware.org/git/binutils-gdb.git' |
14f95a42 JM |
902 | if version == 'mainline': |
903 | git_branch = 'master' | |
904 | else: | |
905 | trans = str.maketrans({'.': '_'}) | |
906 | git_branch = 'binutils-%s-branch' % version.translate(trans) | |
02c78f02 | 907 | return self.git_checkout(component, git_url, git_branch, update) |
14f95a42 JM |
908 | elif component == 'gcc': |
909 | if version == 'mainline': | |
bc487d71 | 910 | branch = 'master' |
14f95a42 | 911 | else: |
bc487d71 JM |
912 | branch = 'releases/gcc-%s' % version |
913 | return self.gcc_checkout(branch, update) | |
14f95a42 | 914 | elif component == 'glibc': |
0784e791 | 915 | git_url = 'https://sourceware.org/git/glibc.git' |
14f95a42 JM |
916 | if version == 'mainline': |
917 | git_branch = 'master' | |
918 | else: | |
919 | git_branch = 'release/%s/master' % version | |
02c78f02 | 920 | r = self.git_checkout(component, git_url, git_branch, update) |
14f95a42 | 921 | self.fix_glibc_timestamps() |
02c78f02 | 922 | return r |
19fd8d0d ST |
923 | elif component == 'gnumach': |
924 | git_url = 'git://git.savannah.gnu.org/hurd/gnumach.git' | |
925 | git_branch = 'master' | |
926 | r = self.git_checkout(component, git_url, git_branch, update) | |
927 | subprocess.run(['autoreconf', '-i'], | |
928 | cwd=self.component_srcdir(component), check=True) | |
929 | return r | |
f4d3cee3 ST |
930 | elif component == 'mig': |
931 | git_url = 'git://git.savannah.gnu.org/hurd/mig.git' | |
932 | git_branch = 'master' | |
933 | r = self.git_checkout(component, git_url, git_branch, update) | |
934 | subprocess.run(['autoreconf', '-i'], | |
935 | cwd=self.component_srcdir(component), check=True) | |
936 | return r | |
f9015003 ST |
937 | elif component == 'hurd': |
938 | git_url = 'git://git.savannah.gnu.org/hurd/hurd.git' | |
939 | git_branch = 'master' | |
48130690 ST |
940 | r = self.git_checkout(component, git_url, git_branch, update) |
941 | subprocess.run(['autoconf'], | |
942 | cwd=self.component_srcdir(component), check=True) | |
943 | return r | |
14f95a42 JM |
944 | else: |
945 | print('error: component %s coming from VCS' % component) | |
946 | exit(1) | |
947 | ||
948 | def git_checkout(self, component, git_url, git_branch, update): | |
02c78f02 | 949 | """Check out a component from git. Return a commit identifier.""" |
14f95a42 JM |
950 | if update: |
951 | subprocess.run(['git', 'remote', 'prune', 'origin'], | |
952 | cwd=self.component_srcdir(component), check=True) | |
e3aa6999 JM |
953 | if self.replace_sources: |
954 | subprocess.run(['git', 'clean', '-dxfq'], | |
955 | cwd=self.component_srcdir(component), check=True) | |
14f95a42 JM |
956 | subprocess.run(['git', 'pull', '-q'], |
957 | cwd=self.component_srcdir(component), check=True) | |
958 | else: | |
c592721a FW |
959 | if self.shallow: |
960 | depth_arg = ('--depth', '1') | |
961 | else: | |
962 | depth_arg = () | |
963 | subprocess.run(['git', 'clone', '-q', '-b', git_branch, | |
964 | *depth_arg, git_url, | |
14f95a42 | 965 | self.component_srcdir(component)], check=True) |
02c78f02 JM |
966 | r = subprocess.run(['git', 'rev-parse', 'HEAD'], |
967 | cwd=self.component_srcdir(component), | |
968 | stdout=subprocess.PIPE, | |
969 | check=True, universal_newlines=True).stdout | |
970 | return r.rstrip() | |
14f95a42 JM |
971 | |
972 | def fix_glibc_timestamps(self): | |
973 | """Fix timestamps in a glibc checkout.""" | |
974 | # Ensure that builds do not try to regenerate generated files | |
975 | # in the source tree. | |
976 | srcdir = self.component_srcdir('glibc') | |
2bd0bfcc JM |
977 | # These files have Makefile dependencies to regenerate them in |
978 | # the source tree that may be active during a normal build. | |
979 | # Some other files have such dependencies but do not need to | |
980 | # be touched because nothing in a build depends on the files | |
981 | # in question. | |
f13d2601 | 982 | for f in ('sysdeps/mach/hurd/bits/errno.h',): |
2bd0bfcc JM |
983 | to_touch = os.path.join(srcdir, f) |
984 | subprocess.run(['touch', '-c', to_touch], check=True) | |
14f95a42 JM |
985 | for dirpath, dirnames, filenames in os.walk(srcdir): |
986 | for f in filenames: | |
987 | if (f == 'configure' or | |
988 | f == 'preconfigure' or | |
989 | f.endswith('-kw.h')): | |
990 | to_touch = os.path.join(dirpath, f) | |
991 | subprocess.run(['touch', to_touch], check=True) | |
992 | ||
bc487d71 JM |
993 | def gcc_checkout(self, branch, update): |
994 | """Check out GCC from git. Return the commit identifier.""" | |
995 | if os.access(os.path.join(self.component_srcdir('gcc'), '.svn'), | |
996 | os.F_OK): | |
997 | if not self.replace_sources: | |
998 | print('error: GCC has moved from SVN to git, use ' | |
999 | '--replace-sources to check out again') | |
1000 | exit(1) | |
1001 | shutil.rmtree(self.component_srcdir('gcc')) | |
1002 | update = False | |
14f95a42 | 1003 | if not update: |
0784e791 | 1004 | self.git_checkout('gcc', 'https://gcc.gnu.org/git/gcc.git', |
bc487d71 | 1005 | branch, update) |
14f95a42 JM |
1006 | subprocess.run(['contrib/gcc_update', '--silent'], |
1007 | cwd=self.component_srcdir('gcc'), check=True) | |
bc487d71 JM |
1008 | r = subprocess.run(['git', 'rev-parse', 'HEAD'], |
1009 | cwd=self.component_srcdir('gcc'), | |
02c78f02 JM |
1010 | stdout=subprocess.PIPE, |
1011 | check=True, universal_newlines=True).stdout | |
1012 | return r.rstrip() | |
14f95a42 JM |
1013 | |
1014 | def checkout_tar(self, component, version, update): | |
1015 | """Check out the given version of the given component from a | |
1016 | tarball.""" | |
1017 | if update: | |
1018 | return | |
1019 | url_map = {'binutils': 'https://ftp.gnu.org/gnu/binutils/binutils-%(version)s.tar.bz2', | |
c8fabb84 | 1020 | 'gcc': 'https://ftp.gnu.org/gnu/gcc/gcc-%(version)s/gcc-%(version)s.tar.gz', |
14f95a42 | 1021 | 'gmp': 'https://ftp.gnu.org/gnu/gmp/gmp-%(version)s.tar.xz', |
0c1041ee | 1022 | 'linux': 'https://www.kernel.org/pub/linux/kernel/v%(major)s.x/linux-%(version)s.tar.xz', |
14f95a42 | 1023 | 'mpc': 'https://ftp.gnu.org/gnu/mpc/mpc-%(version)s.tar.gz', |
394b5bac JM |
1024 | 'mpfr': 'https://ftp.gnu.org/gnu/mpfr/mpfr-%(version)s.tar.xz', |
1025 | 'mig': 'https://ftp.gnu.org/gnu/mig/mig-%(version)s.tar.bz2', | |
1026 | 'gnumach': 'https://ftp.gnu.org/gnu/gnumach/gnumach-%(version)s.tar.bz2', | |
1027 | 'hurd': 'https://ftp.gnu.org/gnu/hurd/hurd-%(version)s.tar.bz2'} | |
14f95a42 JM |
1028 | if component not in url_map: |
1029 | print('error: component %s coming from tarball' % component) | |
1030 | exit(1) | |
0c1041ee JM |
1031 | version_major = version.split('.')[0] |
1032 | url = url_map[component] % {'version': version, 'major': version_major} | |
14f95a42 JM |
1033 | filename = os.path.join(self.srcdir, url.split('/')[-1]) |
1034 | response = urllib.request.urlopen(url) | |
1035 | data = response.read() | |
1036 | with open(filename, 'wb') as f: | |
1037 | f.write(data) | |
1038 | subprocess.run(['tar', '-C', self.srcdir, '-x', '-f', filename], | |
1039 | check=True) | |
1040 | os.rename(os.path.join(self.srcdir, '%s-%s' % (component, version)), | |
1041 | self.component_srcdir(component)) | |
1042 | os.remove(filename) | |
1043 | ||
bf469f0c JM |
1044 | def load_build_state_json(self): |
1045 | """Load information about the state of previous builds.""" | |
1046 | if os.access(self.build_state_json, os.F_OK): | |
1047 | with open(self.build_state_json, 'r') as f: | |
1048 | self.build_state = json.load(f) | |
1049 | else: | |
1050 | self.build_state = {} | |
07a44d23 | 1051 | for k in ('host-libraries', 'compilers', 'glibcs', 'update-syscalls'): |
bf469f0c JM |
1052 | if k not in self.build_state: |
1053 | self.build_state[k] = {} | |
1054 | if 'build-time' not in self.build_state[k]: | |
1055 | self.build_state[k]['build-time'] = '' | |
1056 | if 'build-versions' not in self.build_state[k]: | |
1057 | self.build_state[k]['build-versions'] = {} | |
1058 | if 'build-results' not in self.build_state[k]: | |
1059 | self.build_state[k]['build-results'] = {} | |
1060 | if 'result-changes' not in self.build_state[k]: | |
1061 | self.build_state[k]['result-changes'] = {} | |
1062 | if 'ever-passed' not in self.build_state[k]: | |
1063 | self.build_state[k]['ever-passed'] = [] | |
1064 | ||
1065 | def store_build_state_json(self): | |
1066 | """Store information about the state of previous builds.""" | |
1067 | self.store_json(self.build_state, self.build_state_json) | |
1068 | ||
1069 | def clear_last_build_state(self, action): | |
1070 | """Clear information about the state of part of the build.""" | |
1071 | # We clear the last build time and versions when starting a | |
1072 | # new build. The results of the last build are kept around, | |
1073 | # as comparison is still meaningful if this build is aborted | |
1074 | # and a new one started. | |
1075 | self.build_state[action]['build-time'] = '' | |
1076 | self.build_state[action]['build-versions'] = {} | |
1077 | self.store_build_state_json() | |
1078 | ||
1079 | def update_build_state(self, action, build_time, build_versions): | |
1080 | """Update the build state after a build.""" | |
1081 | build_time = build_time.replace(microsecond=0) | |
b86cb494 JM |
1082 | self.build_state[action]['build-time'] = build_time.strftime( |
1083 | '%Y-%m-%d %H:%M:%S') | |
bf469f0c JM |
1084 | self.build_state[action]['build-versions'] = build_versions |
1085 | build_results = {} | |
1086 | for log in self.status_log_list: | |
1087 | with open(log, 'r') as f: | |
1088 | log_text = f.read() | |
1089 | log_text = log_text.rstrip() | |
1090 | m = re.fullmatch('([A-Z]+): (.*)', log_text) | |
1091 | result = m.group(1) | |
1092 | test_name = m.group(2) | |
1093 | assert test_name not in build_results | |
1094 | build_results[test_name] = result | |
1095 | old_build_results = self.build_state[action]['build-results'] | |
1096 | self.build_state[action]['build-results'] = build_results | |
1097 | result_changes = {} | |
1098 | all_tests = set(old_build_results.keys()) | set(build_results.keys()) | |
1099 | for t in all_tests: | |
1100 | if t in old_build_results: | |
1101 | old_res = old_build_results[t] | |
1102 | else: | |
1103 | old_res = '(New test)' | |
1104 | if t in build_results: | |
1105 | new_res = build_results[t] | |
1106 | else: | |
1107 | new_res = '(Test removed)' | |
1108 | if old_res != new_res: | |
1109 | result_changes[t] = '%s -> %s' % (old_res, new_res) | |
1110 | self.build_state[action]['result-changes'] = result_changes | |
1111 | old_ever_passed = {t for t in self.build_state[action]['ever-passed'] | |
1112 | if t in build_results} | |
1113 | new_passes = {t for t in build_results if build_results[t] == 'PASS'} | |
1114 | self.build_state[action]['ever-passed'] = sorted(old_ever_passed | | |
1115 | new_passes) | |
1116 | self.store_build_state_json() | |
1117 | ||
4d602bce JM |
1118 | def load_bot_config_json(self): |
1119 | """Load bot configuration.""" | |
1120 | with open(self.bot_config_json, 'r') as f: | |
1121 | self.bot_config = json.load(f) | |
1122 | ||
1123 | def part_build_old(self, action, delay): | |
1124 | """Return whether the last build for a given action was at least a | |
1125 | given number of seconds ago, or does not have a time recorded.""" | |
1126 | old_time_str = self.build_state[action]['build-time'] | |
1127 | if not old_time_str: | |
1128 | return True | |
b86cb494 JM |
1129 | old_time = datetime.datetime.strptime( |
1130 | old_time_str, '%Y-%m-%d %H:%M:%S').replace( | |
1131 | tzinfo=datetime.timezone.utc) | |
1132 | new_time = datetime.datetime.now(datetime.timezone.utc) | |
4d602bce JM |
1133 | delta = new_time - old_time |
1134 | return delta.total_seconds() >= delay | |
1135 | ||
1136 | def bot_cycle(self): | |
1137 | """Run a single round of checkout and builds.""" | |
b86cb494 JM |
1138 | print('Bot cycle starting %s.' |
1139 | % str(datetime.datetime.now(datetime.timezone.utc))) | |
4d602bce JM |
1140 | self.load_bot_config_json() |
1141 | actions = ('host-libraries', 'compilers', 'glibcs') | |
1142 | self.bot_run_self(['--replace-sources'], 'checkout') | |
1143 | self.load_versions_json() | |
1144 | if self.get_script_text() != self.script_text: | |
1145 | print('Script changed, re-execing.') | |
1146 | # On script change, all parts of the build should be rerun. | |
1147 | for a in actions: | |
1148 | self.clear_last_build_state(a) | |
1149 | self.exec_self() | |
1150 | check_components = {'host-libraries': ('gmp', 'mpfr', 'mpc'), | |
394b5bac JM |
1151 | 'compilers': ('binutils', 'gcc', 'glibc', 'linux', |
1152 | 'mig', 'gnumach', 'hurd'), | |
4d602bce JM |
1153 | 'glibcs': ('glibc',)} |
1154 | must_build = {} | |
1155 | for a in actions: | |
1156 | build_vers = self.build_state[a]['build-versions'] | |
1157 | must_build[a] = False | |
1158 | if not self.build_state[a]['build-time']: | |
1159 | must_build[a] = True | |
1160 | old_vers = {} | |
1161 | new_vers = {} | |
1162 | for c in check_components[a]: | |
1163 | if c in build_vers: | |
1164 | old_vers[c] = build_vers[c] | |
1165 | new_vers[c] = {'version': self.versions[c]['version'], | |
1166 | 'revision': self.versions[c]['revision']} | |
1167 | if new_vers == old_vers: | |
1168 | print('Versions for %s unchanged.' % a) | |
1169 | else: | |
1170 | print('Versions changed or rebuild forced for %s.' % a) | |
1171 | if a == 'compilers' and not self.part_build_old( | |
1172 | a, self.bot_config['compilers-rebuild-delay']): | |
1173 | print('Not requiring rebuild of compilers this soon.') | |
1174 | else: | |
1175 | must_build[a] = True | |
1176 | if must_build['host-libraries']: | |
1177 | must_build['compilers'] = True | |
1178 | if must_build['compilers']: | |
1179 | must_build['glibcs'] = True | |
1180 | for a in actions: | |
1181 | if must_build[a]: | |
1182 | print('Must rebuild %s.' % a) | |
1183 | self.clear_last_build_state(a) | |
1184 | else: | |
1185 | print('No need to rebuild %s.' % a) | |
a1f6a9ab JM |
1186 | if os.access(self.logsdir, os.F_OK): |
1187 | shutil.rmtree(self.logsdir_old, ignore_errors=True) | |
1188 | shutil.copytree(self.logsdir, self.logsdir_old) | |
4d602bce JM |
1189 | for a in actions: |
1190 | if must_build[a]: | |
b86cb494 | 1191 | build_time = datetime.datetime.now(datetime.timezone.utc) |
4d602bce JM |
1192 | print('Rebuilding %s at %s.' % (a, str(build_time))) |
1193 | self.bot_run_self([], a) | |
1194 | self.load_build_state_json() | |
1195 | self.bot_build_mail(a, build_time) | |
b86cb494 JM |
1196 | print('Bot cycle done at %s.' |
1197 | % str(datetime.datetime.now(datetime.timezone.utc))) | |
4d602bce JM |
1198 | |
1199 | def bot_build_mail(self, action, build_time): | |
1200 | """Send email with the results of a build.""" | |
f0166c16 ZW |
1201 | if not ('email-from' in self.bot_config and |
1202 | 'email-server' in self.bot_config and | |
1203 | 'email-subject' in self.bot_config and | |
1204 | 'email-to' in self.bot_config): | |
1205 | if not self.email_warning: | |
1206 | print("Email not configured, not sending.") | |
1207 | self.email_warning = True | |
1208 | return | |
1209 | ||
4d602bce JM |
1210 | build_time = build_time.replace(microsecond=0) |
1211 | subject = (self.bot_config['email-subject'] % | |
1212 | {'action': action, | |
b86cb494 | 1213 | 'build-time': build_time.strftime('%Y-%m-%d %H:%M:%S')}) |
4d602bce JM |
1214 | results = self.build_state[action]['build-results'] |
1215 | changes = self.build_state[action]['result-changes'] | |
1216 | ever_passed = set(self.build_state[action]['ever-passed']) | |
1217 | versions = self.build_state[action]['build-versions'] | |
1218 | new_regressions = {k for k in changes if changes[k] == 'PASS -> FAIL'} | |
1219 | all_regressions = {k for k in ever_passed if results[k] == 'FAIL'} | |
1220 | all_fails = {k for k in results if results[k] == 'FAIL'} | |
1221 | if new_regressions: | |
1222 | new_reg_list = sorted(['FAIL: %s' % k for k in new_regressions]) | |
1223 | new_reg_text = ('New regressions:\n\n%s\n\n' % | |
1224 | '\n'.join(new_reg_list)) | |
1225 | else: | |
1226 | new_reg_text = '' | |
1227 | if all_regressions: | |
1228 | all_reg_list = sorted(['FAIL: %s' % k for k in all_regressions]) | |
1229 | all_reg_text = ('All regressions:\n\n%s\n\n' % | |
1230 | '\n'.join(all_reg_list)) | |
1231 | else: | |
1232 | all_reg_text = '' | |
1233 | if all_fails: | |
1234 | all_fail_list = sorted(['FAIL: %s' % k for k in all_fails]) | |
1235 | all_fail_text = ('All failures:\n\n%s\n\n' % | |
1236 | '\n'.join(all_fail_list)) | |
1237 | else: | |
1238 | all_fail_text = '' | |
1239 | if changes: | |
1240 | changes_list = sorted(changes.keys()) | |
1241 | changes_list = ['%s: %s' % (changes[k], k) for k in changes_list] | |
1242 | changes_text = ('All changed results:\n\n%s\n\n' % | |
1243 | '\n'.join(changes_list)) | |
1244 | else: | |
1245 | changes_text = '' | |
1246 | results_text = (new_reg_text + all_reg_text + all_fail_text + | |
1247 | changes_text) | |
1248 | if not results_text: | |
1249 | results_text = 'Clean build with unchanged results.\n\n' | |
1250 | versions_list = sorted(versions.keys()) | |
1251 | versions_list = ['%s: %s (%s)' % (k, versions[k]['version'], | |
1252 | versions[k]['revision']) | |
1253 | for k in versions_list] | |
1254 | versions_text = ('Component versions for this build:\n\n%s\n' % | |
1255 | '\n'.join(versions_list)) | |
1256 | body_text = results_text + versions_text | |
1257 | msg = email.mime.text.MIMEText(body_text) | |
1258 | msg['Subject'] = subject | |
1259 | msg['From'] = self.bot_config['email-from'] | |
1260 | msg['To'] = self.bot_config['email-to'] | |
1261 | msg['Message-ID'] = email.utils.make_msgid() | |
b86cb494 JM |
1262 | msg['Date'] = email.utils.format_datetime( |
1263 | datetime.datetime.now(datetime.timezone.utc)) | |
4d602bce JM |
1264 | with smtplib.SMTP(self.bot_config['email-server']) as s: |
1265 | s.send_message(msg) | |
1266 | ||
a1f6a9ab | 1267 | def bot_run_self(self, opts, action, check=True): |
4d602bce JM |
1268 | """Run a copy of this script with given options.""" |
1269 | cmd = [sys.executable, sys.argv[0], '--keep=none', | |
1270 | '-j%d' % self.parallelism] | |
b1176270 JM |
1271 | if self.full_gcc: |
1272 | cmd.append('--full-gcc') | |
4d602bce JM |
1273 | cmd.extend(opts) |
1274 | cmd.extend([self.topdir, action]) | |
a1f6a9ab JM |
1275 | sys.stdout.flush() |
1276 | subprocess.run(cmd, check=check) | |
1277 | ||
1278 | def bot(self): | |
1279 | """Run repeated rounds of checkout and builds.""" | |
1280 | while True: | |
1281 | self.load_bot_config_json() | |
1282 | if not self.bot_config['run']: | |
1283 | print('Bot exiting by request.') | |
1284 | exit(0) | |
1285 | self.bot_run_self([], 'bot-cycle', check=False) | |
1286 | self.load_bot_config_json() | |
1287 | if not self.bot_config['run']: | |
1288 | print('Bot exiting by request.') | |
1289 | exit(0) | |
1290 | time.sleep(self.bot_config['delay']) | |
1291 | if self.get_script_text() != self.script_text: | |
1292 | print('Script changed, bot re-execing.') | |
1293 | self.exec_self() | |
4d602bce | 1294 | |
65b6c9b0 FW |
1295 | class LinuxHeadersPolicyForBuild(object): |
1296 | """Names and directories for installing Linux headers. Build variant.""" | |
1297 | ||
1298 | def __init__(self, config): | |
1299 | self.arch = config.arch | |
1300 | self.srcdir = config.ctx.component_srcdir('linux') | |
1301 | self.builddir = config.component_builddir('linux') | |
1302 | self.headers_dir = os.path.join(config.sysroot, 'usr') | |
1303 | ||
07a44d23 FW |
1304 | class LinuxHeadersPolicyForUpdateSyscalls(object): |
1305 | """Names and directories for Linux headers. update-syscalls variant.""" | |
1306 | ||
1307 | def __init__(self, glibc, headers_dir): | |
1308 | self.arch = glibc.compiler.arch | |
1309 | self.srcdir = glibc.compiler.ctx.component_srcdir('linux') | |
1310 | self.builddir = glibc.ctx.component_builddir( | |
1311 | 'update-syscalls', glibc.name, 'build-linux') | |
1312 | self.headers_dir = headers_dir | |
1313 | ||
65b6c9b0 FW |
1314 | def install_linux_headers(policy, cmdlist): |
1315 | """Install Linux kernel headers.""" | |
1316 | arch_map = {'aarch64': 'arm64', | |
1317 | 'alpha': 'alpha', | |
2fc2260b | 1318 | 'arc': 'arc', |
65b6c9b0 FW |
1319 | 'arm': 'arm', |
1320 | 'csky': 'csky', | |
1321 | 'hppa': 'parisc', | |
1322 | 'i486': 'x86', | |
1323 | 'i586': 'x86', | |
1324 | 'i686': 'x86', | |
1325 | 'i786': 'x86', | |
3be5fc00 | 1326 | 'loongarch64': 'loongarch', |
65b6c9b0 FW |
1327 | 'm68k': 'm68k', |
1328 | 'microblaze': 'microblaze', | |
1329 | 'mips': 'mips', | |
e9816506 | 1330 | 'or1k': 'openrisc', |
65b6c9b0 FW |
1331 | 'powerpc': 'powerpc', |
1332 | 's390': 's390', | |
1333 | 'riscv32': 'riscv', | |
1334 | 'riscv64': 'riscv', | |
1335 | 'sh': 'sh', | |
1336 | 'sparc': 'sparc', | |
1337 | 'x86_64': 'x86'} | |
1338 | linux_arch = None | |
1339 | for k in arch_map: | |
1340 | if policy.arch.startswith(k): | |
1341 | linux_arch = arch_map[k] | |
1342 | break | |
1343 | assert linux_arch is not None | |
1344 | cmdlist.push_subdesc('linux') | |
1345 | cmdlist.create_use_dir(policy.builddir) | |
1346 | cmdlist.add_command('install-headers', | |
1347 | ['make', '-C', policy.srcdir, 'O=%s' % policy.builddir, | |
1348 | 'ARCH=%s' % linux_arch, | |
1349 | 'INSTALL_HDR_PATH=%s' % policy.headers_dir, | |
1350 | 'headers_install']) | |
1351 | cmdlist.cleanup_dir() | |
1352 | cmdlist.pop_subdesc() | |
14f95a42 | 1353 | |
0c95f51d | 1354 | class Config(object): |
14f95a42 JM |
1355 | """A configuration for building a compiler and associated libraries.""" |
1356 | ||
1357 | def __init__(self, ctx, arch, os_name, variant=None, gcc_cfg=None, | |
23a7896d JM |
1358 | first_gcc_cfg=None, binutils_cfg=None, glibcs=None, |
1359 | extra_glibcs=None): | |
14f95a42 JM |
1360 | """Initialize a Config object.""" |
1361 | self.ctx = ctx | |
1362 | self.arch = arch | |
1363 | self.os = os_name | |
1364 | self.variant = variant | |
1365 | if variant is None: | |
1366 | self.name = '%s-%s' % (arch, os_name) | |
1367 | else: | |
1368 | self.name = '%s-%s-%s' % (arch, os_name, variant) | |
1369 | self.triplet = '%s-glibc-%s' % (arch, os_name) | |
1370 | if gcc_cfg is None: | |
1371 | self.gcc_cfg = [] | |
1372 | else: | |
1373 | self.gcc_cfg = gcc_cfg | |
1374 | if first_gcc_cfg is None: | |
1375 | self.first_gcc_cfg = [] | |
1376 | else: | |
1377 | self.first_gcc_cfg = first_gcc_cfg | |
23a7896d JM |
1378 | if binutils_cfg is None: |
1379 | self.binutils_cfg = [] | |
1380 | else: | |
1381 | self.binutils_cfg = binutils_cfg | |
14f95a42 JM |
1382 | if glibcs is None: |
1383 | glibcs = [{'variant': variant}] | |
1384 | if extra_glibcs is None: | |
1385 | extra_glibcs = [] | |
1386 | glibcs = [Glibc(self, **g) for g in glibcs] | |
1387 | extra_glibcs = [Glibc(self, **g) for g in extra_glibcs] | |
1388 | self.all_glibcs = glibcs + extra_glibcs | |
1389 | self.compiler_glibcs = glibcs | |
1390 | self.installdir = ctx.compiler_installdir(self.name) | |
1391 | self.bindir = ctx.compiler_bindir(self.name) | |
1392 | self.sysroot = ctx.compiler_sysroot(self.name) | |
1393 | self.builddir = os.path.join(ctx.builddir, 'compilers', self.name) | |
1394 | self.logsdir = os.path.join(ctx.logsdir, 'compilers', self.name) | |
1395 | ||
1396 | def component_builddir(self, component): | |
1397 | """Return the directory to use for a (non-glibc) build.""" | |
1398 | return self.ctx.component_builddir('compilers', self.name, component) | |
1399 | ||
1400 | def build(self): | |
1401 | """Generate commands to build this compiler.""" | |
1402 | self.ctx.remove_recreate_dirs(self.installdir, self.builddir, | |
1403 | self.logsdir) | |
1404 | cmdlist = CommandList('compilers-%s' % self.name, self.ctx.keep) | |
1405 | cmdlist.add_command('check-host-libraries', | |
1406 | ['test', '-f', | |
1407 | os.path.join(self.ctx.host_libraries_installdir, | |
1408 | 'ok')]) | |
1409 | cmdlist.use_path(self.bindir) | |
1410 | self.build_cross_tool(cmdlist, 'binutils', 'binutils', | |
1411 | ['--disable-gdb', | |
a179673f | 1412 | '--disable-gdbserver', |
14f95a42 JM |
1413 | '--disable-libdecnumber', |
1414 | '--disable-readline', | |
23a7896d | 1415 | '--disable-sim'] + self.binutils_cfg) |
14f95a42 | 1416 | if self.os.startswith('linux'): |
65b6c9b0 | 1417 | install_linux_headers(LinuxHeadersPolicyForBuild(self), cmdlist) |
14f95a42 | 1418 | self.build_gcc(cmdlist, True) |
394b5bac JM |
1419 | if self.os == 'gnu': |
1420 | self.install_gnumach_headers(cmdlist) | |
1421 | self.build_cross_tool(cmdlist, 'mig', 'mig') | |
1422 | self.install_hurd_headers(cmdlist) | |
14f95a42 JM |
1423 | for g in self.compiler_glibcs: |
1424 | cmdlist.push_subdesc('glibc') | |
1425 | cmdlist.push_subdesc(g.name) | |
857c7d73 | 1426 | g.build_glibc(cmdlist, GlibcPolicyForCompiler(g)) |
14f95a42 JM |
1427 | cmdlist.pop_subdesc() |
1428 | cmdlist.pop_subdesc() | |
1429 | self.build_gcc(cmdlist, False) | |
1430 | cmdlist.add_command('done', ['touch', | |
1431 | os.path.join(self.installdir, 'ok')]) | |
1432 | self.ctx.add_makefile_cmdlist('compilers-%s' % self.name, cmdlist, | |
1433 | self.logsdir) | |
1434 | ||
1435 | def build_cross_tool(self, cmdlist, tool_src, tool_build, extra_opts=None): | |
1436 | """Build one cross tool.""" | |
1437 | srcdir = self.ctx.component_srcdir(tool_src) | |
1438 | builddir = self.component_builddir(tool_build) | |
1439 | cmdlist.push_subdesc(tool_build) | |
1440 | cmdlist.create_use_dir(builddir) | |
1441 | cfg_cmd = [os.path.join(srcdir, 'configure'), | |
1442 | '--prefix=%s' % self.installdir, | |
1443 | '--build=%s' % self.ctx.build_triplet, | |
1444 | '--host=%s' % self.ctx.build_triplet, | |
1445 | '--target=%s' % self.triplet, | |
1446 | '--with-sysroot=%s' % self.sysroot] | |
1447 | if extra_opts: | |
1448 | cfg_cmd.extend(extra_opts) | |
1449 | cmdlist.add_command('configure', cfg_cmd) | |
1450 | cmdlist.add_command('build', ['make']) | |
cd880aa2 JM |
1451 | # Parallel "make install" for GCC has race conditions that can |
1452 | # cause it to fail; see | |
1453 | # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42980>. Such | |
1454 | # problems are not known for binutils, but doing the | |
1455 | # installation in parallel within a particular toolchain build | |
1456 | # (as opposed to installation of one toolchain from | |
1457 | # build-many-glibcs.py running in parallel to the installation | |
1458 | # of other toolchains being built) is not known to be | |
1459 | # significantly beneficial, so it is simplest just to disable | |
1460 | # parallel install for cross tools here. | |
1461 | cmdlist.add_command('install', ['make', '-j1', 'install']) | |
14f95a42 JM |
1462 | cmdlist.cleanup_dir() |
1463 | cmdlist.pop_subdesc() | |
1464 | ||
394b5bac JM |
1465 | def install_gnumach_headers(self, cmdlist): |
1466 | """Install GNU Mach headers.""" | |
1467 | srcdir = self.ctx.component_srcdir('gnumach') | |
1468 | builddir = self.component_builddir('gnumach') | |
1469 | cmdlist.push_subdesc('gnumach') | |
1470 | cmdlist.create_use_dir(builddir) | |
1471 | cmdlist.add_command('configure', | |
1472 | [os.path.join(srcdir, 'configure'), | |
1473 | '--build=%s' % self.ctx.build_triplet, | |
1474 | '--host=%s' % self.triplet, | |
1475 | '--prefix=', | |
6eb3edee | 1476 | '--disable-user32', |
394b5bac JM |
1477 | 'CC=%s-gcc -nostdlib' % self.triplet]) |
1478 | cmdlist.add_command('install', ['make', 'DESTDIR=%s' % self.sysroot, | |
1479 | 'install-data']) | |
1480 | cmdlist.cleanup_dir() | |
1481 | cmdlist.pop_subdesc() | |
1482 | ||
1483 | def install_hurd_headers(self, cmdlist): | |
1484 | """Install Hurd headers.""" | |
1485 | srcdir = self.ctx.component_srcdir('hurd') | |
1486 | builddir = self.component_builddir('hurd') | |
1487 | cmdlist.push_subdesc('hurd') | |
1488 | cmdlist.create_use_dir(builddir) | |
1489 | cmdlist.add_command('configure', | |
1490 | [os.path.join(srcdir, 'configure'), | |
1491 | '--build=%s' % self.ctx.build_triplet, | |
1492 | '--host=%s' % self.triplet, | |
1493 | '--prefix=', | |
1494 | '--disable-profile', '--without-parted', | |
1495 | 'CC=%s-gcc -nostdlib' % self.triplet]) | |
1496 | cmdlist.add_command('install', ['make', 'prefix=%s' % self.sysroot, | |
1497 | 'no_deps=t', 'install-headers']) | |
1498 | cmdlist.cleanup_dir() | |
1499 | cmdlist.pop_subdesc() | |
1500 | ||
14f95a42 JM |
1501 | def build_gcc(self, cmdlist, bootstrap): |
1502 | """Build GCC.""" | |
b1176270 JM |
1503 | # libssp is of little relevance with glibc's own stack |
1504 | # checking support. libcilkrts does not support GNU/Hurd (and | |
1505 | # has been removed in GCC 8, so --disable-libcilkrts can be | |
1506 | # removed once glibc no longer supports building with older | |
cbcd65c8 L |
1507 | # GCC versions). --enable-initfini-array is enabled by default |
1508 | # in GCC 12, which can be removed when GCC 12 becomes the | |
1509 | # minimum requirement. | |
14f95a42 | 1510 | cfg_opts = list(self.gcc_cfg) |
a586fe9c | 1511 | cfg_opts += ['--enable-initfini-array'] |
b1176270 | 1512 | cfg_opts += ['--disable-libssp', '--disable-libcilkrts'] |
c440d5d5 JM |
1513 | host_libs = self.ctx.host_libraries_installdir |
1514 | cfg_opts += ['--with-gmp=%s' % host_libs, | |
1515 | '--with-mpfr=%s' % host_libs, | |
1516 | '--with-mpc=%s' % host_libs] | |
14f95a42 JM |
1517 | if bootstrap: |
1518 | tool_build = 'gcc-first' | |
1519 | # Building a static-only, C-only compiler that is | |
1520 | # sufficient to build glibc. Various libraries and | |
1521 | # features that may require libc headers must be disabled. | |
1522 | # When configuring with a sysroot, --with-newlib is | |
1523 | # required to define inhibit_libc (to stop some parts of | |
1524 | # libgcc including libc headers); --without-headers is not | |
1525 | # sufficient. | |
1526 | cfg_opts += ['--enable-languages=c', '--disable-shared', | |
1527 | '--disable-threads', | |
1528 | '--disable-libatomic', | |
1529 | '--disable-decimal-float', | |
65cbd521 | 1530 | '--disable-gcov', |
14f95a42 JM |
1531 | '--disable-libffi', |
1532 | '--disable-libgomp', | |
1533 | '--disable-libitm', | |
1534 | '--disable-libmpx', | |
1535 | '--disable-libquadmath', | |
b1176270 | 1536 | '--disable-libsanitizer', |
14f95a42 JM |
1537 | '--without-headers', '--with-newlib', |
1538 | '--with-glibc-version=%s' % self.ctx.glibc_version | |
1539 | ] | |
1540 | cfg_opts += self.first_gcc_cfg | |
1541 | else: | |
1542 | tool_build = 'gcc' | |
b1176270 | 1543 | # libsanitizer commonly breaks because of glibc header |
3aec0c39 FW |
1544 | # changes, or on unusual targets. C++ pre-compiled |
1545 | # headers are not used during the glibc build and are | |
1546 | # expensive to create. | |
b1176270 | 1547 | if not self.ctx.full_gcc: |
3aec0c39 FW |
1548 | cfg_opts += ['--disable-libsanitizer', |
1549 | '--disable-libstdcxx-pch'] | |
b1176270 JM |
1550 | langs = 'all' if self.ctx.full_gcc else 'c,c++' |
1551 | cfg_opts += ['--enable-languages=%s' % langs, | |
1552 | '--enable-shared', '--enable-threads'] | |
14f95a42 JM |
1553 | self.build_cross_tool(cmdlist, 'gcc', tool_build, cfg_opts) |
1554 | ||
857c7d73 FW |
1555 | class GlibcPolicyDefault(object): |
1556 | """Build policy for glibc: common defaults.""" | |
1557 | ||
1558 | def __init__(self, glibc): | |
1559 | self.srcdir = glibc.ctx.component_srcdir('glibc') | |
1560 | self.use_usr = glibc.os != 'gnu' | |
1561 | self.prefix = '/usr' if self.use_usr else '' | |
1562 | self.configure_args = [ | |
1563 | '--prefix=%s' % self.prefix, | |
1564 | '--enable-profile', | |
1565 | '--build=%s' % glibc.ctx.build_triplet, | |
1566 | '--host=%s' % glibc.triplet, | |
1567 | 'CC=%s' % glibc.tool_name('gcc'), | |
1568 | 'CXX=%s' % glibc.tool_name('g++'), | |
857c7d73 FW |
1569 | ] |
1570 | if glibc.os == 'gnu': | |
1571 | self.configure_args.append('MIG=%s' % glibc.tool_name('mig')) | |
33dd32fd SL |
1572 | if glibc.cflags: |
1573 | self.configure_args.append('CFLAGS=%s' % glibc.cflags) | |
1574 | self.configure_args.append('CXXFLAGS=%s' % glibc.cflags) | |
857c7d73 FW |
1575 | self.configure_args += glibc.cfg |
1576 | ||
1577 | def configure(self, cmdlist): | |
1578 | """Invoked to add the configure command to the command list.""" | |
1579 | cmdlist.add_command('configure', | |
1580 | [os.path.join(self.srcdir, 'configure'), | |
1581 | *self.configure_args]) | |
1582 | ||
1583 | def extra_commands(self, cmdlist): | |
1584 | """Invoked to inject additional commands (make check) after build.""" | |
1585 | pass | |
1586 | ||
1587 | class GlibcPolicyForCompiler(GlibcPolicyDefault): | |
1588 | """Build policy for glibc during the compilers stage.""" | |
1589 | ||
1590 | def __init__(self, glibc): | |
1591 | super().__init__(glibc) | |
1592 | self.builddir = glibc.ctx.component_builddir( | |
1593 | 'compilers', glibc.compiler.name, 'glibc', glibc.name) | |
1594 | self.installdir = glibc.compiler.sysroot | |
1595 | ||
1596 | class GlibcPolicyForBuild(GlibcPolicyDefault): | |
1597 | """Build policy for glibc during the glibcs stage.""" | |
1598 | ||
1599 | def __init__(self, glibc): | |
1600 | super().__init__(glibc) | |
1601 | self.builddir = glibc.ctx.component_builddir( | |
1602 | 'glibcs', glibc.name, 'glibc') | |
1603 | self.installdir = glibc.ctx.glibc_installdir(glibc.name) | |
1604 | if glibc.ctx.strip: | |
1605 | self.strip = glibc.tool_name('strip') | |
1606 | else: | |
1607 | self.strip = None | |
1608 | self.save_logs = glibc.ctx.save_logs | |
1609 | ||
1610 | def extra_commands(self, cmdlist): | |
1611 | if self.strip: | |
876e5154 JM |
1612 | # Avoid stripping libc.so and libpthread.so, which are |
1613 | # linker scripts stored in /lib on Hurd. | |
1614 | find_command = 'find %s/lib* -name "*.so*"' % self.installdir | |
1615 | cmdlist.add_command('strip', ['sh', '-c', ( | |
1616 | 'set -e; for f in $(%s); do ' | |
1617 | 'if ! head -c16 $f | grep -q "GNU ld script"; then %s $f; fi; ' | |
1618 | 'done' % (find_command, self.strip))]) | |
857c7d73 FW |
1619 | cmdlist.add_command('check', ['make', 'check']) |
1620 | cmdlist.add_command('save-logs', [self.save_logs], always_run=True) | |
1621 | ||
07a44d23 FW |
1622 | class GlibcPolicyForUpdateSyscalls(GlibcPolicyDefault): |
1623 | """Build policy for glibc during update-syscalls.""" | |
1624 | ||
1625 | def __init__(self, glibc): | |
1626 | super().__init__(glibc) | |
1627 | self.builddir = glibc.ctx.component_builddir( | |
1628 | 'update-syscalls', glibc.name, 'glibc') | |
1629 | self.linuxdir = glibc.ctx.component_builddir( | |
1630 | 'update-syscalls', glibc.name, 'linux') | |
1631 | self.linux_policy = LinuxHeadersPolicyForUpdateSyscalls( | |
1632 | glibc, self.linuxdir) | |
1633 | self.configure_args.insert( | |
1634 | 0, '--with-headers=%s' % os.path.join(self.linuxdir, 'include')) | |
1635 | # self.installdir not set because installation is not supported | |
14f95a42 | 1636 | |
0c95f51d | 1637 | class Glibc(object): |
14f95a42 JM |
1638 | """A configuration for building glibc.""" |
1639 | ||
1640 | def __init__(self, compiler, arch=None, os_name=None, variant=None, | |
33dd32fd | 1641 | cfg=None, ccopts=None, cflags=None): |
14f95a42 JM |
1642 | """Initialize a Glibc object.""" |
1643 | self.ctx = compiler.ctx | |
1644 | self.compiler = compiler | |
1645 | if arch is None: | |
1646 | self.arch = compiler.arch | |
1647 | else: | |
1648 | self.arch = arch | |
1649 | if os_name is None: | |
1650 | self.os = compiler.os | |
1651 | else: | |
1652 | self.os = os_name | |
1653 | self.variant = variant | |
1654 | if variant is None: | |
1655 | self.name = '%s-%s' % (self.arch, self.os) | |
1656 | else: | |
1657 | self.name = '%s-%s-%s' % (self.arch, self.os, variant) | |
1658 | self.triplet = '%s-glibc-%s' % (self.arch, self.os) | |
1659 | if cfg is None: | |
1660 | self.cfg = [] | |
1661 | else: | |
1662 | self.cfg = cfg | |
33dd32fd | 1663 | # ccopts contain ABI options and are passed to configure as CC / CXX. |
14f95a42 | 1664 | self.ccopts = ccopts |
33dd32fd SL |
1665 | # cflags contain non-ABI options like -g or -O and are passed to |
1666 | # configure as CFLAGS / CXXFLAGS. | |
1667 | self.cflags = cflags | |
14f95a42 JM |
1668 | |
1669 | def tool_name(self, tool): | |
1670 | """Return the name of a cross-compilation tool.""" | |
1671 | ctool = '%s-%s' % (self.compiler.triplet, tool) | |
1672 | if self.ccopts and (tool == 'gcc' or tool == 'g++'): | |
1673 | ctool = '%s %s' % (ctool, self.ccopts) | |
1674 | return ctool | |
1675 | ||
1676 | def build(self): | |
1677 | """Generate commands to build this glibc.""" | |
1678 | builddir = self.ctx.component_builddir('glibcs', self.name, 'glibc') | |
1679 | installdir = self.ctx.glibc_installdir(self.name) | |
1680 | logsdir = os.path.join(self.ctx.logsdir, 'glibcs', self.name) | |
1681 | self.ctx.remove_recreate_dirs(installdir, builddir, logsdir) | |
1682 | cmdlist = CommandList('glibcs-%s' % self.name, self.ctx.keep) | |
1683 | cmdlist.add_command('check-compilers', | |
1684 | ['test', '-f', | |
1685 | os.path.join(self.compiler.installdir, 'ok')]) | |
1686 | cmdlist.use_path(self.compiler.bindir) | |
857c7d73 | 1687 | self.build_glibc(cmdlist, GlibcPolicyForBuild(self)) |
14f95a42 JM |
1688 | self.ctx.add_makefile_cmdlist('glibcs-%s' % self.name, cmdlist, |
1689 | logsdir) | |
1690 | ||
857c7d73 | 1691 | def build_glibc(self, cmdlist, policy): |
14f95a42 JM |
1692 | """Generate commands to build this glibc, either as part of a compiler |
1693 | build or with the bootstrapped compiler (and in the latter case, run | |
1694 | tests as well).""" | |
857c7d73 FW |
1695 | cmdlist.create_use_dir(policy.builddir) |
1696 | policy.configure(cmdlist) | |
14f95a42 JM |
1697 | cmdlist.add_command('build', ['make']) |
1698 | cmdlist.add_command('install', ['make', 'install', | |
857c7d73 | 1699 | 'install_root=%s' % policy.installdir]) |
14f95a42 JM |
1700 | # GCC uses paths such as lib/../lib64, so make sure lib |
1701 | # directories always exist. | |
394b5bac | 1702 | mkdir_cmd = ['mkdir', '-p', |
857c7d73 FW |
1703 | os.path.join(policy.installdir, 'lib')] |
1704 | if policy.use_usr: | |
1705 | mkdir_cmd += [os.path.join(policy.installdir, 'usr', 'lib')] | |
394b5bac | 1706 | cmdlist.add_command('mkdir-lib', mkdir_cmd) |
857c7d73 | 1707 | policy.extra_commands(cmdlist) |
14f95a42 JM |
1708 | cmdlist.cleanup_dir() |
1709 | ||
07a44d23 FW |
1710 | def update_syscalls(self): |
1711 | if self.os == 'gnu': | |
1712 | # Hurd does not have system call tables that need updating. | |
1713 | return | |
1714 | ||
1715 | policy = GlibcPolicyForUpdateSyscalls(self) | |
1716 | logsdir = os.path.join(self.ctx.logsdir, 'update-syscalls', self.name) | |
1717 | self.ctx.remove_recreate_dirs(policy.builddir, logsdir) | |
1718 | cmdlist = CommandList('update-syscalls-%s' % self.name, self.ctx.keep) | |
1719 | cmdlist.add_command('check-compilers', | |
1720 | ['test', '-f', | |
1721 | os.path.join(self.compiler.installdir, 'ok')]) | |
1722 | cmdlist.use_path(self.compiler.bindir) | |
1723 | ||
1724 | install_linux_headers(policy.linux_policy, cmdlist) | |
1725 | ||
1726 | cmdlist.create_use_dir(policy.builddir) | |
1727 | policy.configure(cmdlist) | |
1728 | cmdlist.add_command('build', ['make', 'update-syscall-lists']) | |
1729 | cmdlist.cleanup_dir() | |
1730 | self.ctx.add_makefile_cmdlist('update-syscalls-%s' % self.name, | |
1731 | cmdlist, logsdir) | |
14f95a42 | 1732 | |
0c95f51d | 1733 | class Command(object): |
14f95a42 JM |
1734 | """A command run in the build process.""" |
1735 | ||
1736 | def __init__(self, desc, num, dir, path, command, always_run=False): | |
1737 | """Initialize a Command object.""" | |
1738 | self.dir = dir | |
1739 | self.path = path | |
1740 | self.desc = desc | |
1741 | trans = str.maketrans({' ': '-'}) | |
1742 | self.logbase = '%03d-%s' % (num, desc.translate(trans)) | |
1743 | self.command = command | |
1744 | self.always_run = always_run | |
1745 | ||
1746 | @staticmethod | |
1747 | def shell_make_quote_string(s): | |
1748 | """Given a string not containing a newline, quote it for use by the | |
1749 | shell and make.""" | |
1750 | assert '\n' not in s | |
1751 | if re.fullmatch('[]+,./0-9@A-Z_a-z-]+', s): | |
1752 | return s | |
1753 | strans = str.maketrans({"'": "'\\''"}) | |
1754 | s = "'%s'" % s.translate(strans) | |
1755 | mtrans = str.maketrans({'$': '$$'}) | |
1756 | return s.translate(mtrans) | |
1757 | ||
1758 | @staticmethod | |
1759 | def shell_make_quote_list(l, translate_make): | |
1760 | """Given a list of strings not containing newlines, quote them for use | |
1761 | by the shell and make, returning a single string. If translate_make | |
1762 | is true and the first string is 'make', change it to $(MAKE).""" | |
1763 | l = [Command.shell_make_quote_string(s) for s in l] | |
1764 | if translate_make and l[0] == 'make': | |
1765 | l[0] = '$(MAKE)' | |
1766 | return ' '.join(l) | |
1767 | ||
1768 | def shell_make_quote(self): | |
1769 | """Return this command quoted for the shell and make.""" | |
1770 | return self.shell_make_quote_list(self.command, True) | |
1771 | ||
1772 | ||
0c95f51d | 1773 | class CommandList(object): |
14f95a42 JM |
1774 | """A list of commands run in the build process.""" |
1775 | ||
1776 | def __init__(self, desc, keep): | |
1777 | """Initialize a CommandList object.""" | |
1778 | self.cmdlist = [] | |
1779 | self.dir = None | |
1780 | self.path = None | |
1781 | self.desc = [desc] | |
1782 | self.keep = keep | |
1783 | ||
1784 | def desc_txt(self, desc): | |
1785 | """Return the description to use for a command.""" | |
1786 | return '%s %s' % (' '.join(self.desc), desc) | |
1787 | ||
1788 | def use_dir(self, dir): | |
1789 | """Set the default directory for subsequent commands.""" | |
1790 | self.dir = dir | |
1791 | ||
1792 | def use_path(self, path): | |
1793 | """Set a directory to be prepended to the PATH for subsequent | |
1794 | commands.""" | |
1795 | self.path = path | |
1796 | ||
1797 | def push_subdesc(self, subdesc): | |
1798 | """Set the default subdescription for subsequent commands (e.g., the | |
1799 | name of a component being built, within the series of commands | |
1800 | building it).""" | |
1801 | self.desc.append(subdesc) | |
1802 | ||
1803 | def pop_subdesc(self): | |
1804 | """Pop a subdescription from the list of descriptions.""" | |
1805 | self.desc.pop() | |
1806 | ||
1807 | def create_use_dir(self, dir): | |
1808 | """Remove and recreate a directory and use it for subsequent | |
1809 | commands.""" | |
1810 | self.add_command_dir('rm', None, ['rm', '-rf', dir]) | |
1811 | self.add_command_dir('mkdir', None, ['mkdir', '-p', dir]) | |
1812 | self.use_dir(dir) | |
1813 | ||
14f95a42 JM |
1814 | def add_command_dir(self, desc, dir, command, always_run=False): |
1815 | """Add a command to run in a given directory.""" | |
1816 | cmd = Command(self.desc_txt(desc), len(self.cmdlist), dir, self.path, | |
1817 | command, always_run) | |
1818 | self.cmdlist.append(cmd) | |
1819 | ||
1820 | def add_command(self, desc, command, always_run=False): | |
1821 | """Add a command to run in the default directory.""" | |
1822 | cmd = Command(self.desc_txt(desc), len(self.cmdlist), self.dir, | |
1823 | self.path, command, always_run) | |
1824 | self.cmdlist.append(cmd) | |
1825 | ||
1826 | def cleanup_dir(self, desc='cleanup', dir=None): | |
1827 | """Clean up a build directory. If no directory is specified, the | |
1828 | default directory is cleaned up and ceases to be the default | |
1829 | directory.""" | |
1830 | if dir is None: | |
1831 | dir = self.dir | |
1832 | self.use_dir(None) | |
1833 | if self.keep != 'all': | |
1834 | self.add_command_dir(desc, None, ['rm', '-rf', dir], | |
1835 | always_run=(self.keep == 'none')) | |
1836 | ||
1837 | def makefile_commands(self, wrapper, logsdir): | |
1838 | """Return the sequence of commands in the form of text for a Makefile. | |
1839 | The given wrapper script takes arguments: base of logs for | |
1840 | previous command, or empty; base of logs for this command; | |
1841 | description; directory; PATH addition; the command itself.""" | |
1842 | # prev_base is the base of the name for logs of the previous | |
1843 | # command that is not always-run (that is, a build command, | |
1844 | # whose failure should stop subsequent build commands from | |
1845 | # being run, as opposed to a cleanup command, which is run | |
1846 | # even if previous commands failed). | |
1847 | prev_base = '' | |
1848 | cmds = [] | |
1849 | for c in self.cmdlist: | |
1850 | ctxt = c.shell_make_quote() | |
1851 | if prev_base and not c.always_run: | |
1852 | prev_log = os.path.join(logsdir, prev_base) | |
1853 | else: | |
1854 | prev_log = '' | |
1855 | this_log = os.path.join(logsdir, c.logbase) | |
1856 | if not c.always_run: | |
1857 | prev_base = c.logbase | |
1858 | if c.dir is None: | |
1859 | dir = '' | |
1860 | else: | |
1861 | dir = c.dir | |
1862 | if c.path is None: | |
1863 | path = '' | |
1864 | else: | |
1865 | path = c.path | |
1866 | prelims = [wrapper, prev_log, this_log, c.desc, dir, path] | |
1867 | prelim_txt = Command.shell_make_quote_list(prelims, False) | |
1868 | cmds.append('\t@%s %s' % (prelim_txt, ctxt)) | |
1869 | return '\n'.join(cmds) | |
1870 | ||
bf469f0c JM |
1871 | def status_logs(self, logsdir): |
1872 | """Return the list of log files with command status.""" | |
1873 | return [os.path.join(logsdir, '%s-status.txt' % c.logbase) | |
1874 | for c in self.cmdlist] | |
1875 | ||
14f95a42 JM |
1876 | |
1877 | def get_parser(): | |
1878 | """Return an argument parser for this module.""" | |
1879 | parser = argparse.ArgumentParser(description=__doc__) | |
1880 | parser.add_argument('-j', dest='parallelism', | |
1881 | help='Run this number of jobs in parallel', | |
1882 | type=int, default=os.cpu_count()) | |
1883 | parser.add_argument('--keep', dest='keep', | |
1884 | help='Whether to keep all build directories, ' | |
1885 | 'none or only those from failed builds', | |
1886 | default='none', choices=('none', 'all', 'failed')) | |
02c78f02 JM |
1887 | parser.add_argument('--replace-sources', action='store_true', |
1888 | help='Remove and replace source directories ' | |
1889 | 'with the wrong version of a component') | |
297635d8 JM |
1890 | parser.add_argument('--strip', action='store_true', |
1891 | help='Strip installed glibc libraries') | |
b1176270 JM |
1892 | parser.add_argument('--full-gcc', action='store_true', |
1893 | help='Build GCC with all languages and libsanitizer') | |
c592721a FW |
1894 | parser.add_argument('--shallow', action='store_true', |
1895 | help='Do not download Git history during checkout') | |
757ac24f L |
1896 | parser.add_argument('--exclude', dest='exclude', |
1897 | help='Targets to be excluded', nargs='*') | |
14f95a42 JM |
1898 | parser.add_argument('topdir', |
1899 | help='Toplevel working directory') | |
1900 | parser.add_argument('action', | |
1901 | help='What to do', | |
a1f6a9ab | 1902 | choices=('checkout', 'bot-cycle', 'bot', |
07a44d23 | 1903 | 'host-libraries', 'compilers', 'glibcs', |
feaa1506 FW |
1904 | 'update-syscalls', 'list-compilers', |
1905 | 'list-glibcs')) | |
14f95a42 JM |
1906 | parser.add_argument('configs', |
1907 | help='Versions to check out or configurations to build', | |
1908 | nargs='*') | |
1909 | return parser | |
1910 | ||
1911 | ||
0d70accc | 1912 | def get_version_common(progname,line,word,arg1): |
d846c283 DD |
1913 | try: |
1914 | out = subprocess.run([progname, arg1], | |
1915 | stdout=subprocess.PIPE, | |
1916 | stderr=subprocess.DEVNULL, | |
1917 | stdin=subprocess.DEVNULL, | |
1918 | check=True, universal_newlines=True) | |
1919 | v = out.stdout.splitlines()[line].split()[word] | |
0d70accc | 1920 | v = re.match(r'[0-9]+(.[0-9]+)*', v).group() |
d846c283 DD |
1921 | return [int(x) for x in v.split('.')] |
1922 | except: | |
1923 | return 'missing'; | |
1924 | ||
0d70accc | 1925 | def get_version_common_stderr(progname,line,word,arg1): |
d846c283 DD |
1926 | try: |
1927 | out = subprocess.run([progname, arg1], | |
1928 | stdout=subprocess.DEVNULL, | |
1929 | stderr=subprocess.PIPE, | |
1930 | stdin=subprocess.DEVNULL, | |
1931 | check=True, universal_newlines=True) | |
1932 | v = out.stderr.splitlines()[line].split()[word] | |
0d70accc | 1933 | v = re.match(r'[0-9]+(.[0-9]+)*', v).group() |
d846c283 DD |
1934 | return [int(x) for x in v.split('.')] |
1935 | except: | |
1936 | return 'missing'; | |
1937 | ||
1938 | def get_version(progname): | |
0d70accc | 1939 | return get_version_common(progname, 0, -1, '--version'); |
d846c283 DD |
1940 | |
1941 | def get_version_awk(progname): | |
0d70accc | 1942 | return get_version_common(progname, 0, 2, '--version'); |
d846c283 DD |
1943 | |
1944 | def get_version_bzip2(progname): | |
0d70accc | 1945 | return get_version_common_stderr(progname, 0, 6, '-h'); |
d846c283 DD |
1946 | |
1947 | def check_version(ver, req): | |
1948 | for v, r in zip(ver, req): | |
1949 | if v > r: | |
1950 | return True | |
1951 | if v < r: | |
1952 | return False | |
1953 | return True | |
1954 | ||
1955 | def version_str(ver): | |
1956 | return '.'.join([str (x) for x in ver]) | |
1957 | ||
1958 | def check_for_required_tools(): | |
1959 | get_list_of_required_tools() | |
1960 | count_old_tools = 0 | |
1961 | count_missing_tools = 0 | |
1e6ecd21 | 1962 | |
d846c283 DD |
1963 | for k, v in REQUIRED_TOOLS.items(): |
1964 | version = v[0](k) | |
1965 | if version == 'missing': | |
1966 | ok = 'missing' | |
1967 | else: | |
1968 | ok = 'ok' if check_version (version, v[1]) else 'old' | |
1969 | if ok == 'old': | |
1970 | if count_old_tools == 0: | |
1971 | print("One or more required tools are too old:") | |
1972 | count_old_tools = count_old_tools + 1 | |
1973 | print('{:9}: {:3} (obtained=\"{}\" required=\"{}\")'.format(k, ok, | |
1974 | version_str(version), version_str(v[1]))) | |
1975 | if ok == 'missing': | |
1976 | if count_missing_tools == 0: | |
1977 | print("One or more required tools are missing:") | |
1978 | count_missing_tools = count_missing_tools + 1 | |
1979 | print('{:9}: {:3} (required=\"{}\")'.format(k, ok, | |
1980 | version_str(v[1]))) | |
1e6ecd21 | 1981 | |
d846c283 DD |
1982 | if count_old_tools > 0 or count_missing_tools > 0: |
1983 | exit (1); | |
1e6ecd21 | 1984 | |
14f95a42 JM |
1985 | def main(argv): |
1986 | """The main entry point.""" | |
d846c283 | 1987 | check_for_required_tools(); |
14f95a42 JM |
1988 | parser = get_parser() |
1989 | opts = parser.parse_args(argv) | |
1990 | topdir = os.path.abspath(opts.topdir) | |
02c78f02 | 1991 | ctx = Context(topdir, opts.parallelism, opts.keep, opts.replace_sources, |
757ac24f | 1992 | opts.strip, opts.full_gcc, opts.action, opts.exclude, |
c592721a | 1993 | shallow=opts.shallow) |
14f95a42 JM |
1994 | ctx.run_builds(opts.action, opts.configs) |
1995 | ||
1996 | ||
1997 | if __name__ == '__main__': | |
1998 | main(sys.argv[1:]) |