Source code

Revision control

Other Tools

1
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
2
# vim: set filetype=python:
3
# This Source Code Form is subject to the terms of the Mozilla Public
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
imply_option('--enable-release', mozilla_official)
8
imply_option('--enable-release', depends_if('MOZ_AUTOMATION')(lambda x: True))
9
10
js_option('--enable-release',
11
default=milestone.is_release_or_beta,
12
help='{Build|Do not build} with more conservative, release '
13
'engineering-oriented options.{ This may slow down builds.|}')
14
15
16
@depends('--enable-release')
17
def developer_options(value):
18
if not value:
19
return True
20
21
22
add_old_configure_assignment('DEVELOPER_OPTIONS', developer_options)
23
set_config('DEVELOPER_OPTIONS', developer_options)
24
25
# Code optimization
26
# ==============================================================
27
28
js_option('--disable-optimize',
29
nargs='?',
30
help='Disable optimizations via compiler flags')
31
32
33
@depends('--enable-optimize', '--help')
34
def moz_optimize(option, _):
35
flags = None
36
37
if len(option):
38
val = '2'
39
flags = option[0]
40
elif option:
41
val = '1'
42
else:
43
val = None
44
45
return namespace(
46
optimize=val,
47
flags=flags,
48
)
49
50
51
set_config('MOZ_OPTIMIZE', moz_optimize.optimize)
52
add_old_configure_assignment('MOZ_OPTIMIZE', moz_optimize.optimize)
53
add_old_configure_assignment('MOZ_CONFIGURE_OPTIMIZE_FLAGS', moz_optimize.flags)
54
55
# yasm detection
56
# ==============================================================
57
yasm = check_prog('YASM', ['yasm'], allow_missing=True)
58
59
60
@depends_if(yasm)
61
@checking('yasm version')
62
def yasm_version(yasm):
63
version = check_cmd_output(
64
yasm, '--version',
65
onerror=lambda: die('Failed to get yasm version.')
66
).splitlines()[0].split()[1]
67
return Version(version)
68
69
70
@depends(yasm, target)
71
def yasm_asflags(yasm, target):
72
if yasm:
73
asflags = {
74
('OSX', 'x86'): ['-f', 'macho32'],
75
('OSX', 'x86_64'): ['-f', 'macho64'],
76
('WINNT', 'x86'): ['-f', 'win32'],
77
('WINNT', 'x86_64'): ['-f', 'x64'],
78
}.get((target.os, target.cpu), None)
79
if asflags is None:
80
# We're assuming every x86 platform we support that's
81
# not Windows or Mac is ELF.
82
if target.cpu == 'x86':
83
asflags = ['-f', 'elf32']
84
elif target.cpu == 'x86_64':
85
asflags = ['-f', 'elf64']
86
if asflags:
87
asflags += ['-rnasm', '-pnasm']
88
return asflags
89
90
91
set_config('YASM_ASFLAGS', yasm_asflags)
92
93
94
# Android NDK
95
# ==============================================================
96
97
98
@depends('--disable-compile-environment', target)
99
def compiling_android(compile_env, target):
100
return compile_env and target.os == 'Android'
101
102
103
include('android-ndk.configure', when=compiling_android)
104
105
with only_when(target_is_osx):
106
# MacOS deployment target version
107
# ==============================================================
108
# This needs to happen before any compilation test is done.
109
110
option('--enable-macos-target', env='MACOSX_DEPLOYMENT_TARGET', nargs=1,
111
default='10.9', help='Set the minimum MacOS version needed at runtime')
112
113
114
@depends('--enable-macos-target')
115
@imports(_from='os', _import='environ')
116
def macos_target(value):
117
if value:
118
# Ensure every compiler process we spawn uses this value.
119
environ['MACOSX_DEPLOYMENT_TARGET'] = value[0]
120
return value[0]
121
122
123
set_config('MACOSX_DEPLOYMENT_TARGET', macos_target)
124
add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target)
125
126
127
@depends(host)
128
def host_is_osx(host):
129
if host.os == 'OSX':
130
return True
131
132
133
with only_when(host_is_osx | target_is_osx):
134
# MacOS SDK
135
# =========
136
js_option('--with-macos-sdk', env='MACOS_SDK_DIR', nargs=1,
137
help='Location of platform SDK to use')
138
139
@depends('--with-macos-sdk', host)
140
@imports(_from='os.path', _import='isdir')
141
@imports(_from='biplist', _import='readPlist')
142
def macos_sdk(sdk, host):
143
sdk_min_version = Version('10.11')
144
sdk_max_version = Version('10.14')
145
146
if sdk:
147
sdk = sdk[0]
148
elif host.os == 'OSX':
149
sdk = check_cmd_output('xcrun', '--show-sdk-path', onerror=lambda: '').rstrip()
150
if not sdk:
151
die('Could not find the macOS SDK. Please use --with-macos-sdk to give '
152
'the path to a macOS SDK.')
153
else:
154
die('Need a macOS SDK when targeting macOS. Please use --with-macos-sdk '
155
'to give the path to a macOS SDK.')
156
157
if not isdir(sdk):
158
die('SDK not found in %s. When using --with-macos-sdk, you must specify a '
159
'valid SDK. SDKs are installed when the optional cross-development '
160
'tools are selected during the Xcode/Developer Tools installation.'
161
% sdk)
162
obj = readPlist(os.path.join(sdk, 'SDKSettings.plist'))
163
if not obj:
164
die('Error parsing SDKSettings.plist in the SDK directory: %s' % sdk)
165
if 'Version' not in obj:
166
die('Error finding Version information in SDKSettings.plist from the SDK: %s' % sdk)
167
version = Version(obj['Version'])
168
if version < sdk_min_version:
169
die('SDK version "%s" is too old. Please upgrade to at least %s. '
170
'You may need to point to it using --with-macos-sdk=<path> in your '
171
'mozconfig. Various SDK versions are available from '
172
'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_min_version))
173
if version > sdk_max_version:
174
die('SDK version "%s" is unsupported. Please downgrade to version '
175
'%s. You may need to point to it using --with-macos-sdk=<path> in '
176
'your mozconfig. Various SDK versions are available from '
177
'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_max_version))
178
return sdk
179
180
set_config('MACOS_SDK_DIR', macos_sdk)
181
182
183
with only_when(target_is_osx):
184
with only_when(cross_compiling):
185
option('--with-macos-private-frameworks',
186
env="MACOS_PRIVATE_FRAMEWORKS_DIR", nargs=1,
187
help='Location of private frameworks to use')
188
189
@depends_if('--with-macos-private-frameworks')
190
@imports(_from='os.path', _import='isdir')
191
def macos_private_frameworks(value):
192
if value and not isdir(value[0]):
193
die('PrivateFrameworks not found not found in %s. When using '
194
'--with-macos-private-frameworks, you must specify a valid '
195
'directory', value[0])
196
return value[0]
197
198
@depends(macos_private_frameworks)
199
def macos_private_frameworks(value):
200
if value:
201
return value
202
return '/System/Library/PrivateFrameworks'
203
204
set_config('MACOS_PRIVATE_FRAMEWORKS_DIR', macos_private_frameworks)
205
206
207
# Compiler wrappers
208
# ==============================================================
209
# Normally, we'd use js_option and automatically have those variables
210
# propagated to js/src, but things are complicated by possible additional
211
# wrappers in CC/CXX, and by other subconfigures that do not handle those
212
# options and do need CC/CXX altered.
213
option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
214
help='Enable compiling with wrappers such as distcc and ccache')
215
216
js_option('--with-ccache', env='CCACHE', nargs='?',
217
help='Enable compiling with ccache')
218
219
220
@depends_if('--with-ccache')
221
def ccache(value):
222
if len(value):
223
return value
224
# If --with-ccache was given without an explicit value, we default to
225
# 'ccache'.
226
return 'ccache'
227
228
229
ccache = check_prog('CCACHE', progs=(), input=ccache)
230
231
js_option(env='CCACHE_PREFIX',
232
nargs=1,
233
help='Compiler prefix to use when using ccache')
234
235
ccache_prefix = depends_if('CCACHE_PREFIX')(lambda prefix: prefix[0])
236
set_config('CCACHE_PREFIX', ccache_prefix)
237
238
# Distinguish ccache from sccache.
239
240
241
@depends_if(ccache)
242
def ccache_is_sccache(ccache):
243
return check_cmd_output(ccache, '--version').startswith('sccache')
244
245
246
@depends(ccache, ccache_is_sccache)
247
def using_ccache(ccache, ccache_is_sccache):
248
return ccache and not ccache_is_sccache
249
250
251
@depends_if(ccache, ccache_is_sccache)
252
def using_sccache(ccache, ccache_is_sccache):
253
return ccache and ccache_is_sccache
254
255
js_option(env='RUSTC_WRAPPER', nargs=1,
256
help='Wrap rust compilation with given tool')
257
258
@depends(ccache, ccache_is_sccache, 'RUSTC_WRAPPER')
259
@imports(_from='textwrap', _import='dedent')
260
@imports('os')
261
def check_sccache_version(ccache, ccache_is_sccache, rustc_wrapper):
262
sccache_min_version = Version('0.2.12')
263
264
def check_version(path):
265
out = check_cmd_output(path, '--version')
266
version = Version(out.rstrip().split()[-1])
267
if version < sccache_min_version:
268
die(dedent('''\
269
sccache %s or later is required. sccache in use at %s has
270
version %s.
271
272
Please upgrade or acquire a new version with |./mach bootstrap|.
273
'''), sccache_min_version, path, version)
274
275
if ccache and ccache_is_sccache:
276
check_version(ccache)
277
278
if (rustc_wrapper and
279
(os.path.splitext(os.path.basename(rustc_wrapper[0]))[0].lower() ==
280
'sccache')):
281
check_version(rustc_wrapper[0])
282
283
set_config('MOZ_USING_CCACHE', using_ccache)
284
set_config('MOZ_USING_SCCACHE', using_sccache)
285
286
option(env='SCCACHE_VERBOSE_STATS',
287
help='Print verbose sccache stats after build')
288
289
290
@depends(using_sccache, 'SCCACHE_VERBOSE_STATS')
291
def sccache_verbose_stats(using_sccache, verbose_stats):
292
return using_sccache and bool(verbose_stats)
293
294
295
set_config('SCCACHE_VERBOSE_STATS', sccache_verbose_stats)
296
297
298
@depends('--with-compiler-wrapper', ccache)
299
@imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
300
def compiler_wrapper(wrapper, ccache):
301
if wrapper:
302
raw_wrapper = wrapper[0]
303
wrapper = shell_split(raw_wrapper)
304
wrapper_program = find_program(wrapper[0])
305
if not wrapper_program:
306
die('Cannot find `%s` from the given compiler wrapper `%s`',
307
wrapper[0], raw_wrapper)
308
wrapper[0] = wrapper_program
309
310
if ccache:
311
if wrapper:
312
return tuple([ccache] + wrapper)
313
else:
314
return (ccache,)
315
elif wrapper:
316
return tuple(wrapper)
317
318
319
@depends_if(compiler_wrapper)
320
def using_compiler_wrapper(compiler_wrapper):
321
return True
322
323
324
set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
325
326
327
# GC rooting and hazard analysis.
328
# ==============================================================
329
option(env='MOZ_HAZARD', help='Build for the GC rooting hazard analysis')
330
331
332
@depends('MOZ_HAZARD')
333
def hazard_analysis(value):
334
if value:
335
return True
336
337
338
set_config('MOZ_HAZARD', hazard_analysis)
339
340
341
# Cross-compilation related things.
342
# ==============================================================
343
js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
344
help='Prefix for the target toolchain')
345
346
347
@depends('--with-toolchain-prefix', target, cross_compiling)
348
def toolchain_prefix(value, target, cross_compiling):
349
if value:
350
return tuple(value)
351
if cross_compiling:
352
return ('%s-' % target.toolchain, '%s-' % target.alias)
353
354
355
@depends(toolchain_prefix, target)
356
def first_toolchain_prefix(toolchain_prefix, target):
357
# Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
358
# command line/environment (in which case there's only one value in the tuple),
359
# or when cross-compiling for Android or OSX.
360
if toolchain_prefix and (target.os in ('Android', 'OSX') or len(toolchain_prefix) == 1):
361
return toolchain_prefix[0]
362
363
364
set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix)
365
add_old_configure_assignment('TOOLCHAIN_PREFIX', first_toolchain_prefix)
366
367
368
# Compilers
369
# ==============================================================
370
include('compilers-util.configure')
371
372
373
def try_preprocess(compiler, language, source):
374
return try_invoke_compiler(compiler, language, source, ['-E'])
375
376
377
@imports(_from='mozbuild.configure.constants', _import='CompilerType')
378
@imports(_from='mozbuild.configure.constants',
379
_import='CPU_preprocessor_checks')
380
@imports(_from='mozbuild.configure.constants',
381
_import='kernel_preprocessor_checks')
382
@imports(_from='mozbuild.configure.constants',
383
_import='OS_preprocessor_checks')
384
@imports(_from='textwrap', _import='dedent')
385
@imports(_from='__builtin__', _import='Exception')
386
def get_compiler_info(compiler, language):
387
'''Returns information about the given `compiler` (command line in the
388
form of a list or tuple), in the given `language`.
389
390
The returned information includes:
391
- the compiler type (clang-cl, clang or gcc)
392
- the compiler version
393
- the compiler supported language
394
- the compiler supported language version
395
'''
396
# Note: We'd normally do a version check for clang, but versions of clang
397
# in Xcode have a completely different versioning scheme despite exposing
398
# the version with the same defines.
399
# So instead, we make things such that the version is missing when the
400
# clang used is below the minimum supported version (currently clang 5.0).
401
# We then only include the version information when the compiler matches
402
# the feature check, so that an unsupported version of clang would have
403
# no version number.
404
check = dedent('''\
405
#if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
406
%COMPILER "clang-cl"
407
%VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
408
#elif defined(__clang__)
409
%COMPILER "clang"
410
# if __has_warning("-Wunguarded-availability")
411
%VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
412
# endif
413
#elif defined(__GNUC__)
414
%COMPILER "gcc"
415
%VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
416
#endif
417
418
#if __cplusplus
419
%cplusplus __cplusplus
420
#elif __STDC_VERSION__
421
%STDC_VERSION __STDC_VERSION__
422
#endif
423
''')
424
425
# While we're doing some preprocessing, we might as well do some more
426
# preprocessor-based tests at the same time, to check the toolchain
427
# matches what we want.
428
for name, preprocessor_checks in (
429
('CPU', CPU_preprocessor_checks),
430
('KERNEL', kernel_preprocessor_checks),
431
('OS', OS_preprocessor_checks),
432
):
433
for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
434
check += dedent('''\
435
#%(if)s %(condition)s
436
%%%(name)s "%(value)s"
437
''' % {
438
'if': 'elif' if n else 'if',
439
'condition': condition,
440
'name': name,
441
'value': value,
442
})
443
check += '#endif\n'
444
445
# Also check for endianness. The advantage of living in modern times is
446
# that all the modern compilers we support now have __BYTE_ORDER__ defined
447
# by the preprocessor.
448
check += dedent('''\
449
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
450
%ENDIANNESS "little"
451
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
452
%ENDIANNESS "big"
453
#endif
454
''')
455
456
result = try_preprocess(compiler, language, check)
457
458
if not result:
459
raise FatalCheckError(
460
'Unknown compiler or compiler not supported.')
461
462
# Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
463
# have non-ASCII characters. Treat the output as bytearray.
464
data = {}
465
for line in result.splitlines():
466
if line.startswith(b'%'):
467
k, _, v = line.partition(' ')
468
k = k.lstrip('%')
469
data[k] = v.replace(' ', '').lstrip('"').rstrip('"')
470
log.debug('%s = %s', k, data[k])
471
472
try:
473
type = CompilerType(data['COMPILER'])
474
except Exception:
475
raise FatalCheckError(
476
'Unknown compiler or compiler not supported.')
477
478
cplusplus = int(data.get('cplusplus', '0L').rstrip('L'))
479
stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L'))
480
481
version = data.get('VERSION')
482
if version:
483
version = Version(version)
484
485
return namespace(
486
type=type,
487
version=version,
488
cpu=data.get('CPU'),
489
kernel=data.get('KERNEL'),
490
endianness=data.get('ENDIANNESS'),
491
os=data.get('OS'),
492
language='C++' if cplusplus else 'C',
493
language_version=cplusplus if cplusplus else stdc_version,
494
)
495
496
497
def same_arch_different_bits():
498
return (
499
('x86', 'x86_64'),
500
('ppc', 'ppc64'),
501
('sparc', 'sparc64'),
502
)
503
504
505
@imports(_from='mozbuild.shellutil', _import='quote')
506
@imports(_from='mozbuild.configure.constants',
507
_import='OS_preprocessor_checks')
508
def check_compiler(compiler, language, target):
509
info = get_compiler_info(compiler, language)
510
511
flags = []
512
513
# Check language standards
514
# --------------------------------------------------------------------
515
if language != info.language:
516
raise FatalCheckError(
517
'`%s` is not a %s compiler.' % (quote(*compiler), language))
518
519
# Note: We do a strict version check because there sometimes are backwards
520
# incompatible changes in the standard, and not all code that compiles as
521
# C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
522
# example)
523
if info.language == 'C' and info.language_version != 199901:
524
if info.type == 'clang-cl':
525
flags.append('-Xclang')
526
flags.append('-std=gnu99')
527
528
# Note: this is a strict version check because we used to always add
529
# -std=gnu++14.
530
cxx14_version = 201402
531
if info.language == 'C++':
532
if info.language_version != cxx14_version:
533
# MSVC headers include C++14 features, but don't guard them
534
# with appropriate checks.
535
if info.type == 'clang-cl':
536
flags.append('-Xclang')
537
flags.append('-std=c++14')
538
else:
539
flags.append('-std=gnu++14')
540
541
# Check compiler target
542
# --------------------------------------------------------------------
543
has_target = False
544
if info.type == 'clang':
545
if not info.kernel or info.kernel != target.kernel or \
546
not info.endianness or info.endianness != target.endianness:
547
flags.append('--target=%s' % target.toolchain)
548
has_target = True
549
550
# Add target flag when there is an OS mismatch (e.g. building for Android on
551
# Linux). However, only do this if the target OS is in our whitelist, to
552
# keep things the same on other platforms.
553
elif target.os in OS_preprocessor_checks and (
554
not info.os or info.os != target.os):
555
flags.append('--target=%s' % target.toolchain)
556
has_target = True
557
558
if not has_target and (not info.cpu or info.cpu != target.cpu):
559
same_arch = same_arch_different_bits()
560
if (target.cpu, info.cpu) in same_arch:
561
flags.append('-m32')
562
elif (info.cpu, target.cpu) in same_arch:
563
flags.append('-m64')
564
elif info.type == 'clang-cl' and target.cpu == 'aarch64':
565
flags.append('--target=%s' % target.toolchain)
566
elif info.type == 'clang':
567
flags.append('--target=%s' % target.toolchain)
568
569
return namespace(
570
type=info.type,
571
version=info.version,
572
target_cpu=info.cpu,
573
target_kernel=info.kernel,
574
target_endianness=info.endianness,
575
target_os=info.os,
576
flags=flags,
577
)
578
579
580
@imports(_from='__builtin__', _import='open')
581
@imports('json')
582
@imports('os')
583
def get_vc_paths(topsrcdir):
584
def vswhere(args):
585
program_files = (os.environ.get('PROGRAMFILES(X86)') or
586
os.environ.get('PROGRAMFILES'))
587
if not program_files:
588
return []
589
vswhere = os.path.join(program_files, 'Microsoft Visual Studio',
590
'Installer', 'vswhere.exe')
591
if not os.path.exists(vswhere):
592
return []
593
return json.loads(check_cmd_output(vswhere, '-format', 'json', *args))
594
595
for install in vswhere(['-products', '*', '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64']):
596
path = install['installationPath']
597
tools_version = open(os.path.join(
598
path, r'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt'), 'rb').read().strip()
599
tools_path = os.path.join(
600
path, r'VC\Tools\MSVC', tools_version)
601
yield (Version(install['installationVersion']), tools_path)
602
603
604
@depends(host)
605
def host_is_windows(host):
606
if host.kernel == 'WINNT':
607
return True
608
609
610
js_option('--with-visual-studio-version', nargs=1,
611
choices=('2017',), when=host_is_windows,
612
help='Select a specific Visual Studio version to use')
613
614
615
@depends('--with-visual-studio-version', when=host_is_windows)
616
def vs_major_version(value):
617
if value:
618
return {'2017': 15}[value[0]]
619
620
621
js_option(env='VC_PATH', nargs=1, when=host_is_windows,
622
help='Path to the Microsoft Visual C/C++ compiler')
623
624
625
@depends(host, vs_major_version, check_build_environment, 'VC_PATH',
626
'--with-visual-studio-version', when=host_is_windows)
627
@imports(_from='__builtin__', _import='sorted')
628
@imports(_from='operator', _import='itemgetter')
629
def vc_compiler_paths_for_version(host, vs_major_version, env, vc_path, vs_release_name):
630
if vc_path and vs_release_name:
631
die('VC_PATH and --with-visual-studio-version cannot be used together.')
632
if vc_path:
633
# Use an arbitrary version, it doesn't matter.
634
all_versions = [(Version('15'), vc_path[0])]
635
else:
636
all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
637
if not all_versions:
638
return
639
if vs_major_version:
640
versions = [d for (v, d) in all_versions if v.major ==
641
vs_major_version]
642
if not versions:
643
die('Visual Studio %s could not be found!' % vs_release_name)
644
path = versions[0]
645
else:
646
# Choose the newest version.
647
path = all_versions[-1][1]
648
host_dir = {
649
'x86_64': 'HostX64',
650
'x86': 'HostX86',
651
}.get(host.cpu)
652
if host_dir:
653
path = os.path.join(path, 'bin', host_dir)
654
return {
655
'x64': [os.path.join(path, 'x64')],
656
# The cross toolchains require DLLs from the native x64 toolchain.
657
'x86': [os.path.join(path, 'x86'), os.path.join(path, 'x64')],
658
'arm64': [os.path.join(path, 'arm64'), os.path.join(path, 'x64')],
659
}
660
661
662
@template
663
def vc_compiler_path_for(host_or_target):
664
@depends(host_or_target, vc_compiler_paths_for_version,
665
when=host_is_windows)
666
def vc_compiler_path(target, paths):
667
vc_target = {
668
'x86': 'x86',
669
'x86_64': 'x64',
670
'arm': 'arm',
671
'aarch64': 'arm64'
672
}.get(target.cpu)
673
if not paths:
674
return
675
return paths.get(vc_target)
676
return vc_compiler_path
677
678
679
vc_compiler_path = vc_compiler_path_for(target)
680
host_vc_compiler_path = vc_compiler_path_for(host)
681
682
683
@dependable
684
@imports('os')
685
@imports(_from='os', _import='environ')
686
def original_path():
687
return environ['PATH'].split(os.pathsep)
688
689
690
@template
691
def toolchain_search_path_for(host_or_target):
692
vc_path = {
693
host: host_vc_compiler_path,
694
target: vc_compiler_path,
695
}[host_or_target]
696
697
@depends(vc_path, original_path, developer_options, mozbuild_state_path)
698
@imports('os')
699
@imports(_from='os', _import='environ')
700
def toolchain_search_path(vc_compiler_path, original_path, developer_options,
701
mozbuild_state_path):
702
result = list(original_path)
703
704
if vc_compiler_path:
705
# The second item, if there is one, is necessary to have in $PATH for
706
# Windows to load the required DLLs from there.
707
if len(vc_compiler_path) > 1:
708
environ['PATH'] = os.pathsep.join(result + vc_compiler_path[1:])
709
710
# The first item is where the programs are going to be
711
result.append(vc_compiler_path[0])
712
713
# Also add in the location to which `mach bootstrap` or
714
# `mach artifact toolchain` installs clang.
715
bootstrapped = []
716
717
bootstrap_clang_path = os.path.join(mozbuild_state_path, 'clang', 'bin')
718
bootstrapped.append(bootstrap_clang_path)
719
720
bootstrap_cbindgen_path = os.path.join(mozbuild_state_path, 'cbindgen')
721
bootstrapped.append(bootstrap_cbindgen_path)
722
723
bootstrap_nasm_path = os.path.join(mozbuild_state_path, 'nasm')
724
bootstrapped.append(bootstrap_nasm_path)
725
726
# Also add the rustup install directory for cargo/rustc.
727
rustup_path = os.path.expanduser(os.path.join('~', '.cargo', 'bin'))
728
result.append(rustup_path)
729
730
if developer_options:
731
return bootstrapped + result
732
return result + bootstrapped
733
return toolchain_search_path
734
735
736
toolchain_search_path = toolchain_search_path_for(target)
737
host_toolchain_search_path = toolchain_search_path_for(host)
738
739
740
# As a workaround until bug 1516228 and bug 1516253 are fixed, set the PATH
741
# variable for the build to contain the toolchain search path.
742
@depends(toolchain_search_path, host_toolchain_search_path)
743
@imports('os')
744
@imports(_from='os', _import='environ')
745
def altered_path(toolchain_search_path, host_toolchain_search_path):
746
path = environ['PATH'].split(os.pathsep)
747
altered_path = list(toolchain_search_path)
748
for p in host_toolchain_search_path:
749
if p not in altered_path:
750
altered_path.append(p)
751
for p in path:
752
if p not in altered_path:
753
altered_path.append(p)
754
return os.pathsep.join(altered_path)
755
756
757
set_config('PATH', altered_path)
758
759
760
@template
761
def default_c_compilers(host_or_target, other_c_compiler=None):
762
'''Template defining the set of default C compilers for the host and
763
target platforms.
764
`host_or_target` is either `host` or `target` (the @depends functions
765
from init.configure.
766
`other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
767
'''
768
assert host_or_target in {host, target}
769
770
other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
771
772
@depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
773
def default_c_compilers(host_or_target, target, toolchain_prefix,
774
*other_c_compiler):
775
if host_or_target.kernel == 'WINNT':
776
supported = types = ('clang-cl', 'gcc', 'clang')
777
elif host_or_target.kernel == 'Darwin':
778
types = ('clang',)
779
supported = ('clang', 'gcc')
780
else:
781
supported = types = ('clang', 'gcc')
782
783
info = other_c_compiler[0] if other_c_compiler else None
784
if info and info.type in supported:
785
# When getting default C compilers for the host, we prioritize the
786
# same compiler as the target C compiler.
787
prioritized = info.compiler
788
if info.type == 'gcc':
789
same_arch = same_arch_different_bits()
790
if (target.cpu != host_or_target.cpu and
791
(target.cpu, host_or_target.cpu) not in same_arch and
792
(host_or_target.cpu, target.cpu) not in same_arch):
793
# If the target C compiler is GCC, and it can't be used with
794
# -m32/-m64 for the host, it's probably toolchain-prefixed,
795
# so we prioritize a raw 'gcc' instead.
796
prioritized = info.type
797
798
types = [prioritized] + [t for t in types if t != info.type]
799
800
gcc = ('gcc',)
801
if toolchain_prefix and host_or_target is target:
802
gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc
803
804
result = []
805
for type in types:
806
if type == 'gcc':
807
result.extend(gcc)
808
else:
809
result.append(type)
810
811
return tuple(result)
812
813
return default_c_compilers
814
815
816
@template
817
def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
818
'''Template defining the set of default C++ compilers for the host and
819
target platforms.
820
`c_compiler` is the @depends function returning a Compiler instance for
821
the desired platform.
822
823
Because the build system expects the C and C++ compilers to be from the
824
same compiler suite, we derive the default C++ compilers from the C
825
compiler that was found if none was provided.
826
827
We also factor in the target C++ compiler when getting the default host
828
C++ compiler, using the target C++ compiler if the host and target C
829
compilers are the same.
830
'''
831
832
assert (other_c_compiler is None) == (other_cxx_compiler is None)
833
if other_c_compiler is not None:
834
other_compilers = (other_c_compiler, other_cxx_compiler)
835
else:
836
other_compilers = ()
837
838
@depends(c_compiler, *other_compilers)
839
def default_cxx_compilers(c_compiler, *other_compilers):
840
if other_compilers:
841
other_c_compiler, other_cxx_compiler = other_compilers
842
if other_c_compiler.compiler == c_compiler.compiler:
843
return (other_cxx_compiler.compiler,)
844
845
dir = os.path.dirname(c_compiler.compiler)
846
file = os.path.basename(c_compiler.compiler)
847
848
if c_compiler.type == 'gcc':
849
return (os.path.join(dir, file.replace('gcc', 'g++')),)
850
851
if c_compiler.type == 'clang':
852
return (os.path.join(dir, file.replace('clang', 'clang++')),)
853
854
return (c_compiler.compiler,)
855
856
return default_cxx_compilers
857
858
859
@template
860
def provided_program(env_var, when=None):
861
'''Template handling cases where a program can be specified either as a
862
path or as a path with applicable arguments.
863
'''
864
865
@depends_if(env_var, when=when)
866
@imports(_from='itertools', _import='takewhile')
867
@imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
868
def provided(cmd):
869
# Assume the first dash-prefixed item (and any subsequent items) are
870
# command-line options, the item before the dash-prefixed item is
871
# the program we're looking for, and anything before that is a wrapper
872
# of some kind (e.g. sccache).
873
cmd = shell_split(cmd[0])
874
875
without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd))
876
877
return namespace(
878
wrapper=without_flags[:-1],
879
program=without_flags[-1],
880
flags=cmd[len(without_flags):],
881
)
882
883
return provided
884
885
886
def prepare_flags(host_or_target, macos_sdk):
887
if macos_sdk and host_or_target.os == 'OSX':
888
return ['-isysroot', macos_sdk]
889
return []
890
891
892
@template
893
def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
894
other_c_compiler=None):
895
'''Template handling the generic base checks for the compiler for the
896
given `language` on the given platform (`host_or_target`).
897
`host_or_target` is either `host` or `target` (the @depends functions
898
from init.configure.
899
When the language is 'C++', `c_compiler` is the result of the `compiler`
900
template for the language 'C' for the same `host_or_target`.
901
When `host_or_target` is `host`, `other_compiler` is the result of the
902
`compiler` template for the same `language` for `target`.
903
When `host_or_target` is `host` and the language is 'C++',
904
`other_c_compiler` is the result of the `compiler` template for the
905
language 'C' for `target`.
906
'''
907
assert host_or_target in {host, target}
908
assert language in ('C', 'C++')
909
assert language == 'C' or c_compiler is not None
910
assert host_or_target is target or other_compiler is not None
911
assert language == 'C' or host_or_target is target or \
912
other_c_compiler is not None
913
914
host_or_target_str = {
915
host: 'host',
916
target: 'target',
917
}[host_or_target]
918
919
var = {
920
('C', target): 'CC',
921
('C++', target): 'CXX',
922
('C', host): 'HOST_CC',
923
('C++', host): 'HOST_CXX',
924
}[language, host_or_target]
925
926
default_compilers = {
927
'C': lambda: default_c_compilers(host_or_target, other_compiler),
928
'C++': lambda: default_cxx_compilers(c_compiler, other_c_compiler, other_compiler),
929
}[language]()
930
931
what = 'the %s %s compiler' % (host_or_target_str, language)
932
933
option(env=var, nargs=1, help='Path to %s' % what)
934
935
# Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
936
# HOST_CXX variables.
937
provided_compiler = provided_program(var)
938
939
search_path = {
940
host: host_toolchain_search_path,
941
target: toolchain_search_path,
942
}[host_or_target]
943
944
# Normally, we'd use `var` instead of `_var`, but the interaction with
945
# old-configure complicates things, and for now, we a) can't take the plain
946
# result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
947
# old-configure AC_SUBST it (because it's autoconf doing it, not us)
948
compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
949
input=provided_compiler.program,
950
paths=search_path)
951
952
@depends(compiler, provided_compiler, compiler_wrapper, host_or_target, macos_sdk)
953
@checking('whether %s can be used' % what, lambda x: bool(x))
954
@imports(_from='mozbuild.shellutil', _import='quote')
955
def valid_compiler(compiler, provided_compiler, compiler_wrapper,
956
host_or_target, macos_sdk):
957
wrapper = list(compiler_wrapper or ())
958
flags = prepare_flags(host_or_target, macos_sdk)
959
if provided_compiler:
960
provided_wrapper = list(provided_compiler.wrapper)
961
# When doing a subconfigure, the compiler is set by old-configure
962
# and it contains the wrappers from --with-compiler-wrapper and
963
# --with-ccache.
964
if provided_wrapper[:len(wrapper)] == wrapper:
965
provided_wrapper = provided_wrapper[len(wrapper):]
966
wrapper.extend(provided_wrapper)
967
flags.extend(provided_compiler.flags)
968
969
info = check_compiler(wrapper + [compiler] + flags, language,
970
host_or_target)
971
972
# Check that the additional flags we got are enough to not require any
973
# more flags. If we get an exception, just ignore it; it's liable to be
974
# invalid command-line flags, which means the compiler we're checking
975
# doesn't support those command-line flags and will fail one or more of
976
# the checks below.
977
try:
978
if info.flags:
979
flags += info.flags
980
info = check_compiler(wrapper + [compiler] + flags, language,
981
host_or_target)
982
except FatalCheckError:
983
pass
984
985
if not info.target_cpu or info.target_cpu != host_or_target.cpu:
986
raise FatalCheckError(
987
'%s %s compiler target CPU (%s) does not match --%s CPU (%s)'
988
% (host_or_target_str.capitalize(), language,
989
info.target_cpu or 'unknown', host_or_target_str,
990
host_or_target.raw_cpu))
991
992
if not info.target_kernel or (info.target_kernel !=
993
host_or_target.kernel):
994
raise FatalCheckError(
995
'%s %s compiler target kernel (%s) does not match --%s kernel (%s)'
996
% (host_or_target_str.capitalize(), language,
997
info.target_kernel or 'unknown', host_or_target_str,
998
host_or_target.kernel))
999
1000
if not info.target_endianness or (info.target_endianness !=
1001
host_or_target.endianness):
1002
raise FatalCheckError(
1003
'%s %s compiler target endianness (%s) does not match --%s '
1004
'endianness (%s)'
1005
% (host_or_target_str.capitalize(), language,
1006
info.target_endianness or 'unknown', host_or_target_str,
1007
host_or_target.endianness))
1008
1009
# Compiler version checks
1010
# ===================================================
1011
# Check the compiler version here instead of in `compiler_version` so
1012
# that the `checking` message doesn't pretend the compiler can be used
1013
# to then bail out one line later.
1014
if info.type == 'gcc':
1015
if host_or_target.os == 'Android':
1016
raise FatalCheckError('GCC is not supported on Android.\n'
1017
'Please use clang from the Android NDK instead.')
1018
if info.version < '6.1.0':
1019
raise FatalCheckError(
1020
'Only GCC 6.1 or newer is supported (found version %s).'
1021
% info.version)
1022
1023
if info.type == 'clang-cl':
1024
if info.version < '8.0.0':
1025
raise FatalCheckError(
1026
'Only clang-cl 8.0 or newer is supported (found version %s)'
1027
% info.version)
1028
1029
# If you want to bump the version check here search for
1030
# diagnose_if above, and see the associated comment.
1031
if info.type == 'clang' and not info.version:
1032
raise FatalCheckError(
1033
'Only clang/llvm 5.0 or newer is supported.')
1034
1035
if info.flags:
1036
raise FatalCheckError(
1037
'Unknown compiler or compiler not supported.')
1038
1039
return namespace(
1040
wrapper=wrapper,
1041
compiler=compiler,
1042
flags=flags,
1043
type=info.type,
1044
version=info.version,
1045
language=language,
1046
)
1047
1048
@depends(valid_compiler)
1049
@checking('%s version' % what)
1050
def compiler_version(compiler):
1051
return compiler.version
1052
1053
if language == 'C++':
1054
@depends(valid_compiler, c_compiler)
1055
def valid_compiler(compiler, c_compiler):
1056
if compiler.type != c_compiler.type:
1057
die('The %s C compiler is %s, while the %s C++ compiler is '
1058
'%s. Need to use the same compiler suite.',
1059
host_or_target_str, c_compiler.type,
1060
host_or_target_str, compiler.type)
1061
1062
if compiler.version != c_compiler.version:
1063
die('The %s C compiler is version %s, while the %s C++ '
1064
'compiler is version %s. Need to use the same compiler '
1065
'version.',
1066
host_or_target_str, c_compiler.version,
1067
host_or_target_str, compiler.version)
1068
return compiler
1069
1070
# Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
1071
# and the flags that were part of the user input for those variables to
1072
# be provided.
1073
add_old_configure_assignment(var, depends_if(valid_compiler)(
1074
lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
1075
1076
if host_or_target is target:
1077
add_old_configure_assignment('ac_cv_prog_%s' % var, depends_if(valid_compiler)(
1078
lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
1079
# We check that it works in python configure already.
1080
add_old_configure_assignment('ac_cv_prog_%s_works' % var.lower(), 'yes')
1081
add_old_configure_assignment(
1082
'ac_cv_prog_%s_cross' % var.lower(),
1083
depends(cross_compiling)(lambda x: 'yes' if x else 'no'))
1084
gcc_like = depends(valid_compiler.type)(lambda x: 'yes' if x in ('gcc', 'clang') else 'no')
1085
add_old_configure_assignment('ac_cv_prog_%s_g' % var.lower(), gcc_like)
1086
if language == 'C':
1087
add_old_configure_assignment('ac_cv_prog_gcc', gcc_like)
1088
if language == 'C++':
1089
add_old_configure_assignment('ac_cv_prog_gxx', gcc_like)
1090
1091
1092
# Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
1093
# old-configure to do some of its still existing checks.
1094
if language == 'C':
1095
set_config(
1096
'%s_TYPE' % var, valid_compiler.type)
1097
add_old_configure_assignment(
1098
'%s_TYPE' % var, valid_compiler.type)
1099
set_config(
1100
'%s_VERSION' % var, depends(valid_compiler.version)(lambda v: str(v)))
1101
1102
valid_compiler = compiler_class(valid_compiler, host_or_target)
1103
1104
def compiler_error():
1105
raise FatalCheckError('Failed compiling a simple %s source with %s'
1106
% (language, what))
1107
1108
valid_compiler.try_compile(check_msg='%s works' % what,
1109
onerror=compiler_error)
1110
1111
set_config('%s_BASE_FLAGS' % var, valid_compiler.flags)
1112
1113
# Set CPP/CXXCPP for both the build system and old-configure. We don't
1114
# need to check this works for preprocessing, because we already relied
1115
# on $CC -E/$CXX -E doing preprocessing work to validate the compiler
1116
# in the first place.
1117
if host_or_target is target:
1118
pp_var = {
1119
'C': 'CPP',
1120
'C++': 'CXXCPP',
1121
}[language]
1122
1123
preprocessor = depends_if(valid_compiler)(
1124
lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
1125
1126
set_config(pp_var, preprocessor)
1127
add_old_configure_assignment(pp_var, preprocessor)
1128
1129
if language == 'C':
1130
linker_var = {
1131
target: 'LD',
1132
host: 'HOST_LD',
1133
}[host_or_target]
1134
1135
@deprecated_option(env=linker_var, nargs=1)
1136
def linker(value):
1137
if value:
1138
return value[0]
1139
1140
@depends(linker)
1141
def unused_linker(linker):
1142
if linker:
1143
log.warning('The value of %s is not used by this build system.'
1144
% linker_var)
1145
1146
return valid_compiler
1147
1148
1149
c_compiler = compiler('C', target)
1150
cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
1151
host_c_compiler = compiler('C', host, other_compiler=c_compiler)
1152
host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
1153
other_compiler=cxx_compiler,
1154
other_c_compiler=c_compiler)
1155
1156
# Generic compiler-based conditions.
1157
building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
1158
1159
1160
@depends(cxx_compiler, ccache_prefix)
1161
@imports('os')
1162
def cxx_is_icecream(info, ccache_prefix):
1163
if (os.path.islink(info.compiler) and os.path.basename(
1164
os.readlink(info.compiler)) == 'icecc'):
1165
return True
1166
if ccache_prefix and os.path.basename(ccache_prefix) == 'icecc':
1167
return True
1168
1169
set_config('CXX_IS_ICECREAM', cxx_is_icecream)
1170
1171
1172
@depends(c_compiler)
1173
def msvs_version(info):
1174
# clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
1175
# be set for GYP on Windows.
1176
if info.type == 'clang-cl':
1177
return '2017'
1178
1179
return ''
1180
1181
1182
set_config('MSVS_VERSION', msvs_version)
1183
1184
include('compile-checks.configure')
1185
include('arm.configure', when=depends(target.cpu)(lambda cpu: cpu == 'arm'))
1186
1187
1188
@depends(host,
1189
host_os_kernel_major_version,
1190
target,
1191
cxx_compiler.try_run(header='#include_next <inttypes.h>'))
1192
def check_have_mac_10_14_sdk(host, version, target, value):
1193
# Only an issue on Mac OS X 10.14 (and probably above).
1194
if host.kernel != 'Darwin' or target.kernel !='Darwin' or version < '18' or value:
1195
return
1196
1197
die('System inttypes.h not found. Please try running '
1198
'`open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg` '
1199
'and following the instructions to install the necessary headers')
1200
1201
1202
@depends(have_64_bit,
1203
try_compile(body='static_assert(sizeof(void *) == 8, "")',
1204
check_msg='for 64-bit OS'))
1205
def check_have_64_bit(have_64_bit, compiler_have_64_bit):
1206
if have_64_bit != compiler_have_64_bit:
1207
configure_error('The target compiler does not agree with configure '
1208
'about the target bitness.')
1209
1210
1211
@depends(c_compiler, target)
1212
def default_debug_flags(compiler_info, target):
1213
# Debug info is ON by default.
1214
if compiler_info.type == 'clang-cl':
1215
return '-Z7'
1216
elif target.kernel == 'WINNT' and compiler_info.type == 'clang':
1217
return '-g -gcodeview'
1218
return '-g'
1219
1220
1221
option(env='MOZ_DEBUG_FLAGS',
1222
nargs=1,
1223
help='Debug compiler flags')
1224
1225
imply_option('--enable-debug-symbols',
1226
depends_if('--enable-debug')(lambda v: v))
1227
1228
js_option('--disable-debug-symbols',
1229
nargs='?',
1230
help='Disable debug symbols using the given compiler flags')
1231
1232
set_config('MOZ_DEBUG_SYMBOLS',
1233
depends_if('--enable-debug-symbols')(lambda _: True))
1234
1235
1236
@depends('MOZ_DEBUG_FLAGS', '--enable-debug-symbols', default_debug_flags)
1237
def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
1238
# If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
1239
# --enable-debug-symbols takes precedence. Note, the value of
1240
# --enable-debug-symbols may be implied by --enable-debug.
1241
if len(enable_debug_flags):
1242
return enable_debug_flags[0]
1243
if env_debug_flags:
1244
return env_debug_flags[0]
1245
return default_debug_flags
1246
1247
1248
set_config('MOZ_DEBUG_FLAGS', debug_flags)
1249
add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
1250
1251
1252
@depends(c_compiler)
1253
def color_cflags(info):
1254
# We could test compiling with flags. By why incur the overhead when
1255
# color support should always be present in a specific toolchain
1256
# version?
1257
1258
# Code for auto-adding this flag to compiler invocations needs to
1259
# determine if an existing flag isn't already present. That is likely
1260
# using exact string matching on the returned value. So if the return
1261
# value changes to e.g. "<x>=always", exact string match may fail and
1262
# multiple color flags could be added. So examine downstream consumers
1263
# before adding flags to return values.
1264
if info.type == 'gcc':
1265
return '-fdiagnostics-color'
1266
elif info.type == 'clang':
1267
return '-fcolor-diagnostics'
1268
else:
1269
return ''
1270
1271
1272
set_config('COLOR_CFLAGS', color_cflags)
1273
1274
# Some standard library headers (notably bionic on Android) declare standard
1275
# functions (e.g. getchar()) and also #define macros for those standard
1276
# functions. libc++ deals with this by doing something like the following
1277
# (explanatory comments added):
1278
#
1279
# #ifdef FUNC
1280
# // Capture the definition of FUNC.
1281
# inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
1282
# #undef FUNC
1283
# // Use a real inline definition.
1284
# inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
1285
# #endif
1286
#
1287
# _LIBCPP_INLINE_VISIBILITY is typically defined as:
1288
#
1289
# __attribute__((__visibility__("hidden"), __always_inline__))
1290
#
1291
# Unfortunately, this interacts badly with our system header wrappers, as the:
1292
#
1293
# #pragma GCC visibility push(default)
1294
#
1295
# that they do prior to including the actual system header is treated by the
1296
# compiler as an explicit declaration of visibility on every function declared
1297
# in the header. Therefore, when the libc++ code above is encountered, it is
1298
# as though the compiler has effectively seen:
1299
#
1300
# int FUNC(...) __attribute__((__visibility__("default")));
1301
# int FUNC(...) __attribute__((__visibility__("hidden")));
1302
#
1303
# and the compiler complains about the mismatched visibility declarations.
1304
#
1305
# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
1306
# existing definition. We can therefore define it to the empty string (since
1307
# we are properly managing visibility ourselves) and avoid this whole mess.
1308
# Note that we don't need to do this with gcc, as libc++ detects gcc and
1309
# effectively does the same thing we are doing here.
1310
#
1311
# _LIBCPP_ALWAYS_INLINE needs a similar workarounds, since it too declares
1312
# hidden visibility.
1313
#
1314
# _LIBCPP_HIDE_FROM_ABI is a macro in libc++ versions in NDKs >=r19. It too
1315
# declares hidden visibility, but it also declares functions as excluded from
1316
# explicit instantiation (roughly: the function can be unused in the current
1317
# compilation, but does not then trigger an actual definition of the function;
1318
# it is assumed the real definition comes from elsewhere). We need to replicate
1319
# this setup.
1320
1321
1322
@depends(c_compiler, target)
1323
def libcxx_override_visibility(c_compiler, target):
1324
if c_compiler.type == 'clang' and target.os == 'Android':
1325
return namespace(
1326
empty='',
1327
hide_from_abi='__attribute__((__exclude_from_explicit_instantiation__))',
1328
)
1329
1330
1331
set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_override_visibility.empty)
1332
set_define('_LIBCPP_ALWAYS_INLINE', libcxx_override_visibility.empty)
1333
1334
set_define('_LIBCPP_HIDE_FROM_ABI', libcxx_override_visibility.hide_from_abi)
1335
1336
@depends(target, check_build_environment)
1337
def visibility_flags(target, env):
1338
if target.os != 'WINNT':
1339
if target.kernel == 'Darwin':
1340
return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
1341
return ('-I%s/system_wrappers' % os.path.join(env.dist),
1342
'-include',
1343
'%s/config/gcc_hidden.h' % env.topsrcdir)
1344
1345
1346
@depends(target, visibility_flags)
1347
def wrap_system_includes(target, visibility_flags):
1348
if visibility_flags and target.kernel != 'Darwin':
1349
return True
1350
1351
1352
set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
1353
depends(visibility_flags)(lambda v: bool(v) or None))
1354
set_define('HAVE_VISIBILITY_ATTRIBUTE',
1355
depends(visibility_flags)(lambda v: bool(v) or None))
1356
set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
1357
set_config('VISIBILITY_FLAGS', visibility_flags)
1358
1359
1360
@template
1361
def depend_cflags(host_or_target_c_compiler):
1362
@depends(host_or_target_c_compiler)
1363
def depend_cflags(host_or_target_c_compiler):
1364
if host_or_target_c_compiler.type != 'clang-cl':
1365
return ['-MD', '-MP', '-MF $(MDDEPDIR)/$(@F).pp']
1366
else:
1367
# clang-cl doesn't accept the normal -MD -MP -MF options that clang
1368
# does, but the underlying cc1 binary understands how to generate
1369
# dependency files. These options are based on analyzing what the
1370
# normal clang driver sends to cc1 when given the "correct"
1371
# dependency options.
1372
return [
1373
'-Xclang', '-MP',
1374
'-Xclang', '-dependency-file',
1375
'-Xclang', '$(MDDEPDIR)/$(@F).pp',
1376
'-Xclang', '-MT',
1377
'-Xclang', '$@'
1378
]
1379
1380
return depend_cflags
1381
1382
1383
set_config('_DEPEND_CFLAGS', depend_cflags(c_compiler))
1384
set_config('_HOST_DEPEND_CFLAGS', depend_cflags(host_c_compiler))
1385
1386
1387
@depends(c_compiler)
1388
def preprocess_option(compiler):
1389
# The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
1390
if compiler.type in ('gcc', 'clang'):
1391
return '-E -o '
1392
else:
1393
return '-P -Fi'
1394
1395
1396
set_config('PREPROCESS_OPTION', preprocess_option)
1397
1398
1399
# We only want to include windows.configure when we are compiling on
1400
# Windows, or for Windows.
1401
1402
1403
@depends(target, host)
1404
def is_windows(target, host):
1405
return host.kernel == 'WINNT' or target.kernel == 'WINNT'
1406
1407
1408
include('windows.configure', when=is_windows)
1409
1410
1411
# On Power ISA, determine compiler flags for VMX, VSX and VSX-3.
1412
1413
set_config('PPC_VMX_FLAGS',
1414
['-maltivec'],
1415
when=depends(target.cpu)(lambda cpu: cpu.startswith('ppc')))
1416
1417
set_config('PPC_VSX_FLAGS',
1418
['-mvsx'],
1419
when=depends(target.cpu)(lambda cpu: cpu.startswith('ppc')))
1420
1421
set_config('PPC_VSX3_FLAGS',
1422
['-mvsx','-mcpu=power9'],
1423
when=depends(target.cpu)(lambda cpu: cpu.startswith('ppc')))
1424
1425
# ASAN
1426
# ==============================================================
1427
1428
js_option('--enable-address-sanitizer', help='Enable Address Sanitizer')
1429
1430
1431
@depends(when='--enable-address-sanitizer')
1432
def asan():
1433
return True
1434
1435
1436
add_old_configure_assignment('MOZ_ASAN', asan)
1437
1438
# MSAN
1439
# ==============================================================
1440
1441
js_option('--enable-memory-sanitizer', help='Enable Memory Sanitizer')
1442
1443
1444
@depends(when='--enable-memory-sanitizer')
1445
def msan():
1446
return True
1447
1448
1449
add_old_configure_assignment('MOZ_MSAN', msan)
1450
1451
# TSAN
1452
# ==============================================================
1453
1454
js_option('--enable-thread-sanitizer', help='Enable Thread Sanitizer')
1455
1456
1457
@depends(when='--enable-thread-sanitizer')
1458
def tsan():
1459
return True
1460
1461
1462
add_old_configure_assignment('MOZ_TSAN', tsan)
1463
1464
# UBSAN
1465
# ==============================================================
1466
1467
js_option('--enable-undefined-sanitizer',
1468
nargs='*',
1469
help='Enable UndefinedBehavior Sanitizer')
1470
1471
@depends_if('--enable-undefined-sanitizer')
1472
def ubsan(options):
1473
default_checks = [
1474
'bool',
1475
'bounds',
1476
'integer-divide-by-zero',
1477
'pointer-overflow',
1478
'vla-bound',
1479
]
1480
1481
checks = options if len(options) else default_checks
1482
1483
return ','.join(checks)
1484
1485
add_old_configure_assignment('MOZ_UBSAN_CHECKS', ubsan)
1486
1487
1488
js_option('--enable-signed-overflow-sanitizer',
1489
help='Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)')
1490
1491
1492
@depends(when='--enable-signed-overflow-sanitizer')
1493
def ub_signed_overflow_san():
1494
return True
1495
1496
1497
add_old_configure_assignment('MOZ_SIGNED_OVERFLOW_SANITIZE', ub_signed_overflow_san)
1498
1499
1500
js_option('--enable-unsigned-overflow-sanitizer',
1501
help='Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)')
1502
1503
1504
@depends(when='--enable-unsigned-overflow-sanitizer')
1505
def ub_unsigned_overflow_san():
1506
return True
1507
1508
1509
add_old_configure_assignment('MOZ_UNSIGNED_OVERFLOW_SANITIZE', ub_unsigned_overflow_san)
1510
1511
# Security Hardening
1512
# ==============================================================
1513
1514
option('--enable-hardening', env='MOZ_SECURITY_HARDENING',
1515
help='Enables security hardening compiler options')
1516
1517
1518
# This function is a bit confusing. It adds or removes hardening flags in
1519
# three stuations: if --enable-hardening is passed; if --disable-hardening
1520
# is passed, and if no flag is passed.
1521
#
1522
# At time of this comment writing, all flags are actually added in the
1523
# default no-flag case; making --enable-hardening the same as omitting the
1524
# flag. --disable-hardening will omit the security flags. (However, not all
1525
# possible security flags will be omitted by --disable-hardening, as many are
1526
# compiler-default options we do not explicitly enable.)
1527
@depends('--enable-hardening', '--enable-address-sanitizer',
1528
'--enable-debug', '--enable-optimize', c_compiler, target)
1529
def security_hardening_cflags(hardening_flag, asan, debug, optimize, c_compiler,
1530
target):
1531
compiler_is_gccish = c_compiler.type in ('gcc', 'clang')
1532
1533
flags = []
1534
ldflags = []
1535
js_flags = []
1536
js_ldflags = []
1537
1538
# ----------------------------------------------------------
1539
# If hardening is explicitly enabled, or not explicitly disabled
1540
if hardening_flag.origin == "default" or hardening_flag:
1541
# FORTIFY_SOURCE ------------------------------------
1542
# Require optimization for FORTIFY_SOURCE. See Bug 1417452
1543
# Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
1544
if compiler_is_gccish and optimize and not asan:
1545
# Don't enable FORTIFY_SOURCE on Android on the top-level, but do enable in js/
1546
if target.os != 'Android':
1547
flags.append("-U_FORTIFY_SOURCE")
1548
flags.append("-D_FORTIFY_SOURCE=2")
1549
js_flags.append("-U_FORTIFY_SOURCE")
1550
js_flags.append("-D_FORTIFY_SOURCE=2")
1551
1552
# fstack-protector ------------------------------------
1553
# Enable only if hardening is not disabled and ASAN is
1554
# not on as ASAN will catch the crashes for us
1555
# mingw-clang cross-compile toolchain has bugs with stack protector
1556
mingw_clang = c_compiler.type == 'clang' and target.os == 'WINNT'
1557
if compiler_is_gccish and not asan:
1558
if not mingw_clang:
1559
flags.append("-fstack-protector-strong")
1560
ldflags.append("-fstack-protector-strong")
1561
js_flags.append("-fstack-protector-strong")
1562
js_ldflags.append("-fstack-protector-strong")
1563
1564
# ftrivial-auto-var-init ------------------------------
1565
# Initialize local variables with a 0xAA pattern in clang debug builds.
1566
# Linux32 fails some xpcshell tests with -ftrivial-auto-var-init
1567
linux32 = target.kernel == 'Linux' and target.cpu == 'x86'
1568
if (c_compiler.type == 'clang' or c_compiler.type == 'clang-cl') and \
1569
c_compiler.version >= '8' and debug and not linux32:
1570
if c_compiler.type == 'clang-cl':
1571
flags.append('-Xclang')
1572
js_flags.append('-Xclang')
1573
flags.append('-ftrivial-auto-var-init=pattern')
1574
js_flags.append('-ftrivial-auto-var-init=pattern')
1575
1576
# ASLR ------------------------------------------------
1577
# ASLR (dynamicbase) is enabled by default in clang-cl; but the
1578
# mingw-clang build requires it to be explicitly enabled
1579
if mingw_clang:
1580
ldflags.append("-Wl,--dynamicbase")
1581
js_ldflags.append("-Wl,--dynamicbase")
1582
1583
# Control Flow Guard (CFG) ----------------------------
1584
# On aarch64, this is enabled only with explicit --enable-hardening
1585
# (roughly: automation) due to a dependency on a patched clang-cl.
1586
if c_compiler.type == 'clang-cl' and c_compiler.version >= '8' and \
1587
(target.cpu != 'aarch64' or hardening_flag):
1588
flags.append("-guard:cf")
1589
js_flags.append("-guard:cf")
1590
# nolongjmp is needed because clang doesn't emit the CFG tables of
1591
# setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
1592
ldflags.append("-guard:cf,nolongjmp")
1593
js_ldflags.append("-guard:cf,nolongjmp")
1594
1595
# ----------------------------------------------------------
1596
# If ASAN _is_ on, undefine FORTIFY_SOURCE just to be safe
1597
if asan:
1598
flags.append("-U_FORTIFY_SOURCE")
1599
js_flags.append("-U_FORTIFY_SOURCE")
1600
1601
# fno-common -----------------------------------------
1602
# Do not merge variables for ASAN; can detect some subtle bugs
1603
if asan:
1604
# clang-cl does not recognize the flag, it must be passed down to clang
1605
if c_compiler.type == 'clang-cl':
1606
flags.append("-Xclang")
1607
flags.append("-fno-common")
1608
1609
return namespace(
1610
flags=flags,
1611
ldflags=ldflags,
1612
js_flags=js_flags,
1613
js_ldflags=js_ldflags,
1614
)
1615
1616
1617
set_config('MOZ_HARDENING_CFLAGS', security_hardening_cflags.flags)
1618
set_config('MOZ_HARDENING_LDFLAGS', security_hardening_cflags.ldflags)
1619
set_config('MOZ_HARDENING_CFLAGS_JS', security_hardening_cflags.js_flags)
1620
set_config('MOZ_HARDENING_LDFLAGS_JS', security_hardening_cflags.js_ldflags)
1621
1622
1623
# Frame pointers
1624
# ==============================================================
1625
@depends(c_compiler)
1626
def frame_pointer_flags(compiler):
1627
if compiler.type == 'clang-cl':
1628
return namespace(
1629
enable=['-Oy-'],
1630
disable=['-Oy'],
1631
)
1632
return namespace(
1633
enable=['-fno-omit-frame-pointer', '-funwind-tables'],
1634
disable=['-fomit-frame-pointer', '-funwind-tables'],
1635
)
1636
1637
1638
@depends(moz_optimize.optimize, moz_debug, target,
1639
'--enable-memory-sanitizer', '--enable-address-sanitizer',
1640
'--enable-undefined-sanitizer')
1641
def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
1642
return bool(not optimize or debug or msan or asan or ubsan or \
1643
(target.os == 'WINNT' and target.cpu in ('x86', 'aarch64')))
1644
1645
1646
js_option('--enable-frame-pointers', default=frame_pointer_default,
1647
help='{Enable|Disable} frame pointers')
1648
1649
1650
@depends('--enable-frame-pointers', frame_pointer_flags)
1651
def frame_pointer_flags(enable, flags):
1652
if enable:
1653
return flags.enable
1654
return flags.disable
1655
1656
1657
set_config('MOZ_FRAMEPTR_FLAGS', frame_pointer_flags)
1658
1659
1660
# nasm detection
1661
# ==============================================================
1662
nasm = check_prog('NASM', ['nasm'], allow_missing=True, paths=toolchain_search_path)
1663
1664
1665
@depends_if(nasm)
1666
@checking('nasm version')
1667
def nasm_version(nasm):
1668
(retcode, stdout, _) = get_cmd_output(nasm, '-v')
1669
if retcode:
1670
# mac stub binary
1671
return None
1672
1673
version = stdout.splitlines()[0].split()[2]
1674
return Version(version)
1675
1676
1677
@depends_if(nasm_version)
1678
def nasm_major_version(nasm_version):
1679
return str(nasm_version.major)
1680
1681
1682
@depends_if(nasm_version)
1683
def nasm_minor_version(nasm_version):
1684
return str(nasm_version.minor)
1685
1686
1687
set_config('NASM_MAJOR_VERSION', nasm_major_version)
1688
set_config('NASM_MINOR_VERSION', nasm_minor_version)
1689
1690
1691
@depends(nasm, target)
1692
def nasm_asflags(nasm, target):
1693
if nasm:
1694
asflags = {
1695
('OSX', 'x86'): ['-f', 'macho32'],
1696
('OSX', 'x86_64'): ['-f', 'macho64'],
1697
('WINNT', 'x86'): ['-f', 'win32'],
1698
('WINNT', 'x86_64'): ['-f', 'win64'],
1699
}.get((target.os, target.cpu), None)
1700
if asflags is None:
1701
# We're assuming every x86 platform we support that's
1702
# not Windows or Mac is ELF.
1703
if target.cpu == 'x86':
1704
asflags = ['-f', 'elf32']
1705
elif target.cpu == 'x86_64':
1706
asflags = ['-f', 'elf64']
1707
return asflags
1708
1709
1710
set_config('NASM_ASFLAGS', nasm_asflags)
1711
1712
@depends(nasm_asflags)
1713
def have_nasm(value):
1714
if value:
1715
return True
1716
1717
1718
@depends(yasm_asflags)
1719
def have_yasm(yasm_asflags):
1720
if yasm_asflags:
1721
return True
1722
1723
set_config('HAVE_NASM', have_nasm)
1724
1725
set_config('HAVE_YASM', have_yasm)
1726
# Until the YASM variable is not necessary in old-configure.
1727
add_old_configure_assignment('YASM', have_yasm)
1728
1729
1730
# clang-cl integrated assembler support
1731
# ==============================================================
1732
@depends(target)
1733
def clangcl_asflags(target):
1734
asflags = None
1735
if target.os == 'WINNT' and target.cpu == 'aarch64':
1736
asflags = ['--target=aarch64-windows-msvc']
1737
return asflags
1738
1739
1740
set_config('CLANGCL_ASFLAGS', clangcl_asflags)
1741
1742
1743
# Code Coverage
1744
# ==============================================================
1745
1746
js_option('--enable-coverage', env='MOZ_CODE_COVERAGE',
1747
help='Enable code coverage')
1748
1749
1750
@depends('--enable-coverage')
1751
def code_coverage(value):
1752
if value:
1753
return True
1754
1755
1756
set_config('MOZ_CODE_COVERAGE', code_coverage)
1757
set_define('MOZ_CODE_COVERAGE', code_coverage)
1758
1759
@depends(target, c_compiler, vc_path, check_build_environment, when=code_coverage)
1760
@imports('re')
1761
@imports('mozpack.path')
1762
@imports(_from='__builtin__', _import='open')
1763
def coverage_cflags(target, c_compiler, vc_path, build_env):
1764
cflags = ['--coverage']
1765
1766
if c_compiler.type in ('clang', 'clang-cl'):
1767
cflags += [
1768
'-Xclang', '-coverage-no-function-names-in-data',
1769
]
1770
1771
if target.os == 'WINNT' and c_compiler.type == 'clang-cl':
1772
# The Visual Studio directory is the parent of the Visual C++ directory.
1773
vs_path = os.path.dirname(vc_path)
1774
1775
# We need to get the real path of Visual Studio, which can be in a
1776
# symlinked directory (for example, on automation).
1777
vs_path = mozpack.path.readlink(vs_path)
1778
# Since the -fprofile-exclude-files option in LLVM is a regex, we need to
1779
# have the same path separators.
1780
vs_path = vs_path.replace('/', '\\')
1781
1782
cflags += [
1783
'-fprofile-exclude-files=^{}.*$'.format(re.escape(vs_path)),
1784
]
1785
1786
response_file_path = os.path.join(build_env.topobjdir, 'code_coverage_cflags')
1787
1788
with open(response_file_path, 'w') as f:
1789
f.write(' '.join(cflags))
1790
1791
return ['@{}'.format(response_file_path)]
1792
1793
set_config('COVERAGE_CFLAGS', coverage_cflags)
1794
1795
# ==============================================================
1796
1797
option(env='RUSTFLAGS',
1798
nargs=1,
1799
help='Rust compiler flags')
1800
set_config('RUSTFLAGS', depends('RUSTFLAGS')(lambda flags: flags))
1801
1802
1803
# Rust compiler flags
1804
# ==============================================================
1805
1806
js_option(env='RUSTC_OPT_LEVEL',
1807
nargs=1,
1808
help='Rust compiler optimization level (-C opt-level=%s)')
1809
1810
# --enable-release kicks in full optimizations.
1811
imply_option('RUSTC_OPT_LEVEL', '2', when='--enable-release')
1812
1813
1814
@depends('RUSTC_OPT_LEVEL', moz_optimize)
1815
def rustc_opt_level(opt_level_option, moz_optimize):
1816
if opt_level_option:
1817
return opt_level_option[0]
1818
else:
1819
return '1' if moz_optimize.optimize else '0'
1820
1821
1822
@depends(rustc_opt_level, debug_rust, '--enable-debug-symbols', '--enable-frame-pointers')
1823
def rust_compile_flags(opt_level, debug_rust, debug_symbols, frame_pointers):
1824
# Cargo currently supports only two interesting profiles for building:
1825
# development and release. Those map (roughly) to --enable-debug and
1826
# --disable-debug in Gecko, respectively.
1827
#
1828
# But we'd also like to support an additional axis of control for
1829
# optimization level. Since Cargo only supports 2 profiles, we're in
1830
# a bit of a bind.
1831
#
1832
# Code here derives various compiler options given other configure options.
1833
# The options defined here effectively override defaults specified in
1834
# Cargo.toml files.
1835
1836
debug_assertions = None
1837
debug_info = None
1838
1839
# opt-level=0 implies -C debug-assertions, which may not be desired
1840
# unless Rust debugging is enabled.
1841
if opt_level == '0' and not debug_rust:
1842
debug_assertions = False
1843
1844
if debug_symbols:
1845
debug_info = '2'
1846
1847
opts = []
1848
1849
if opt_level is not None:
1850
opts.append('opt-level=%s' % opt_level)
1851
if debug_assertions is not None:
1852
opts.append('debug-assertions=%s' %
1853
('yes' if debug_assertions else 'no'))
1854
if debug_info is not None:
1855
opts.append('debuginfo=%s' % debug_info)
1856
if frame_pointers:
1857
opts.append('force-frame-pointers=yes')
1858
1859
flags = []
1860
for opt in opts:
1861
flags.extend(['-C', opt])
1862
1863
return flags
1864
1865
1866
# Rust incremental compilation
1867
# ==============================================================
1868
1869
1870
js_option('--disable-cargo-incremental',
1871
help='Disable incremental rust compilation.')
1872
1873
@depends(rustc_opt_level, debug_rust, 'MOZ_AUTOMATION', code_coverage,
1874
'--disable-cargo-incremental', using_sccache, 'RUSTC_WRAPPER')
1875
@imports('os')
1876
def cargo_incremental(opt_level, debug_rust, automation, code_coverage,
1877
enabled, using_sccache, rustc_wrapper):
1878
"""Return a value for the CARGO_INCREMENTAL environment variable."""
1879
1880
if not enabled:
1881
return '0'
1882
1883
# We never want to use incremental compilation in automation. sccache
1884
# handles our automation use case much better than incremental compilation
1885
# would.
1886
if automation:
1887
return '0'
1888
1889
# Coverage instrumentation doesn't play well with incremental compilation
1891
if code_coverage:
1892
return '0'
1893
1894
# Incremental compilation doesn't work as well as it should, and if we're
1895
# using sccache, it's better to use sccache than incremental compilation.
1896
if not using_sccache and rustc_wrapper:
1897
rustc_wrapper = os.path.basename(rustc_wrapper[0])
1898
if os.path.splitext(rustc_wrapper)[0].lower() == 'sccache':
1899
using_sccache = True
1900
if using_sccache:
1901
return '0'
1902
1903
# Incremental compilation is automatically turned on for debug builds, so
1904
# we don't need to do anything special here.
1905
if debug_rust:
1906
return
1907
1908
# --enable-release automatically sets -O2 for Rust code, and people can
1909
# set RUSTC_OPT_LEVEL to 2 or even 3 if they want to profile Rust code.
1910
# Let's assume that if Rust code is using -O2 or higher, we shouldn't
1911
# be using incremental compilation, because we'd be imposing a
1912
# significant runtime cost.
1913
if opt_level not in ('0', '1'):
1914
return
1915
1916
# We're clear to use incremental compilation!
1917
return '1'
1918
1919
1920
set_config('CARGO_INCREMENTAL', cargo_incremental)
1921
1922
# Linker detection
1923
# ==============================================================
1924
1925
1926
@depends(target)
1927
def is_linker_option_enabled(target):
1928
if target.kernel not in ('WINNT', 'SunOS'):
1929
return True
1930
1931
1932
option('--enable-gold',
1933
env='MOZ_FORCE_GOLD',
1934
help='Enable GNU Gold Linker when it is not already the default',
1935
when=is_linker_option_enabled)
1936
1937
imply_option('--enable-linker', 'gold', when='--enable-gold')
1938
1939
@depends(target, developer_options)
1940
def enable_linker_default(target, developer_options):
1941
# x86-64 gold has bugs in how it lays out .note.* sections. See bug 1573820.
1942
# lld is faster, so prefer that for developer builds.
1943
if target.os == 'Android' and target.cpu == 'x86_64':
1944
return 'lld' if developer_options else 'bfd'
1945
1946
1947
js_option('--enable-linker', nargs=1,
1948
help='Select the linker {bfd, gold, ld64, lld, lld-*}{|}',
1949
default=enable_linker_default,
1950
when=is_linker_option_enabled)
1951
1952
1953
@depends('--enable-linker', c_compiler, developer_options, '--enable-gold',
1954
extra_toolchain_flags, target, when=is_linker_option_enabled)
1955
@checking('for linker', lambda x: x.KIND)
1956
@imports('os')
1957
@imports('shutil')
1958
def select_linker(linker, c_compiler, developer_options, enable_gold,
1959
toolchain_flags, target):
1960
1961
if linker:
1962
linker = linker[0]
1963
else:
1964
linker = None
1965
1966
def is_valid_linker(linker):
1967
if target.kernel == 'Darwin':
1968
valid_linkers = ('ld64', 'lld')
1969
else:
1970
valid_linkers = ('bfd', 'gold', 'lld')
1971
if linker in valid_linkers:
1972
return True
1973
if 'lld' in valid_linkers and linker.startswith('lld-'):
1974
return True
1975
return False
1976
1977
if linker and not is_valid_linker(linker):
1978
# Check that we are trying to use a supported linker
1979
die('Unsupported linker ' + linker)
1980
1981
# Check the kind of linker
1982
version_check = ['-Wl,--version']
1983
cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
1984
1985
def try_linker(linker):
1986
# Generate the compiler flag
1987
if linker == 'ld64':
1988
linker_flag = ['-fuse-ld=ld']
1989
elif linker:
1990
linker_flag = ["-fuse-ld=" + linker]
1991
else:
1992
linker_flag = []
1993
cmd = cmd_base + linker_flag + version_check
1994
if toolchain_flags:
1995
cmd += toolchain_flags
1996
1997
# ld64 doesn't have anything to print out a version. It does print out
1998
# "ld64: For information on command line options please use 'man ld'."
1999
# but that would require doing two attempts, one with --version, that
2000
# would fail, and another with --help.
2001
# Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
2002
# specific to it on stderr when it fails to process --version.
2003
env = dict(os.environ)
2004
env['LD_PRINT_OPTIONS'] = '1'
2005
retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
2006
if retcode == 1 and 'Logging ld64 options' in stderr:
2007
kind = 'ld64'
2008
2009
elif retcode != 0:
2010
return None
2011
2012
elif 'GNU ld' in stdout:
2013
# We are using the normal linker
2014
kind = 'bfd'
2015
2016
elif 'GNU gold' in stdout:
2017
kind = 'gold'
2018
2019
elif 'LLD' in stdout:
2020
kind = 'lld'
2021
2022
else:
2023
kind = 'unknown'
2024
2025
return namespace(
2026
KIND=kind,
2027
LINKER_FLAG=linker_flag,
2028
)
2029
2030
result = try_linker(linker)
2031
if result is None:
2032
if linker:
2033
die("Could not use {} as linker".format(linker))
2034
die("Failed to find a linker")
2035
2036
if (linker is None and enable_gold.origin == 'default' and
2037
developer_options and result.KIND in ('bfd', 'gold')):
2038
# try and use lld if available.
2039
tried = try_linker('lld')
2040
if result.KIND != 'gold' and (tried is None or tried.KIND != 'lld'):
2041
tried = try_linker('gold')
2042
if tried is None or tried.KIND != 'gold':
2043
tried = None
2044
if tried:
2045
result = tried
2046
2047
# If an explicit linker was given, error out if what we found is different.
2048
if linker and not linker.startswith(result.KIND):
2049
die("Could not use {} as linker".format(linker))
2050
2051
return result
2052
2053
2054
set_config('LINKER_KIND', select_linker.KIND)
2055
2056
2057
@depends_if(select_linker, macos_sdk)
2058
def linker_ldflags(linker, macos_sdk):
2059
flags = list(linker.LINKER_FLAG or [])
2060
if macos_sdk:
2061
if linker.KIND == 'ld64':
2062
flags.append('-Wl,-syslibroot,%s' % macos_sdk)
2063
else:
2064
flags.append('-Wl,--sysroot=%s' % macos_sdk)
2065
2066
return flags
2067
2068
2069
add_old_configure_assignment('LINKER_LDFLAGS', linker_ldflags)
2070
2071
2072
# There's a wrinkle with MinGW: linker configuration is not enabled, so
2073
# `select_linker` is never invoked. Hard-code around it.
2074
@depends(select_linker, target, c_compiler)
2075
def gcc_use_gnu_ld(select_linker, target, c_compiler):
2076
if select_linker is not None:
2077
return select_linker.KIND in ('bfd', 'gold', 'lld')
2078
if target.kernel == 'WINNT' and c_compiler.type == 'clang':
2079
return True
2080
return None
2081
2082
2083
# GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
2084
set_config('GCC_USE_GNU_LD', gcc_use_gnu_ld)
2085
add_old_configure_assignment('GCC_USE_GNU_LD', gcc_use_gnu_ld)
2086
2087
# Assembler detection
2088
# ==============================================================
2089
2090
js_option(env='AS', nargs=1, help='Path to the assembler')
2091
2092
@depends(target, c_compiler)
2093
def as_info(target, c_compiler):
2094
if c_compiler.type == 'clang-cl':
2095
ml = {
2096
'x86': 'ml',
2097
'x86_64': 'ml64',
2098
'aarch64': 'armasm64.exe',
2099
}.get(target.cpu)
2100
return namespace(
2101
type='masm',
2102
names=(ml, )
2103
)
2104
# When building with anything but clang-cl, we just use the C compiler as the assembler.
2105
return namespace(
2106
type='gcc',
2107
names=(c_compiler.compiler, )
2108
)
2109
2110
# One would expect the assembler to be specified merely as a program. But in
2111
# cases where the assembler is passed down into js/, it can be specified in
2112
# the same way as CC: a program + a list of argument flags. We might as well
2113
# permit the same behavior in general, even though it seems somewhat unusual.
2114
# So we have to do the same sort of dance as we did above with
2115
# `provided_compiler`.
2116
provided_assembler = provided_program('AS')
2117
assembler = check_prog('_AS', input=provided_assembler.program,
2118
what='the assembler', progs=as_info.names,
2119
paths=toolchain_search_path)
2120
2121
@depends(as_info, assembler, provided_assembler, c_compiler)
2122
def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
2123
if provided_assembler:
2124
return provided_assembler.wrapper + \
2125
[provided_assembler.program] + \
2126
provided_assembler.flags
2127
2128
if as_info.type == 'masm':
2129
return assembler
2130
2131
assert as_info.type == 'gcc'
2132
2133
# Need to add compiler wrappers and flags as appropriate.
2134
return c_compiler.wrapper + [assembler] + c_compiler.flags
2135
2136
2137
add_old_configure_assignment('AS', as_with_flags)
2138
add_old_configure_assignment('ac_cv_prog_AS', as_with_flags)
2139
2140
2141
@depends(assembler, c_compiler, extra_toolchain_flags)
2142
@imports('subprocess')
2143
@imports(_from='os', _import='devnull')
2144
def gnu_as(assembler, c_compiler, toolchain_flags):
2145
# clang uses a compatible GNU assembler.
2146
if c_compiler.type == 'clang':
2147
return True
2148
2149
if c_compiler.type == 'gcc':
2150
cmd = [assembler] + c_compiler.flags
2151
if toolchain_flags:
2152
cmd += toolchain_flags
2153
cmd += ['-Wa,--version', '-c', '-o', devnull, '-x', 'assembler', '-']
2154
# We don't actually have to provide any input on stdin, `Popen.communicate` will
2155
# close the stdin pipe.
2156
# clang will error if it uses its integrated assembler for this target,
2157
# so handle failures gracefully.
2158
if 'GNU' in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: ''):
2159
return True
2160
2161
2162
set_config('GNU_AS', gnu_as)
2163
add_old_configure_assignment('GNU_AS', gnu_as)
2164
2165
2166
@depends(as_info, target)
2167
def as_dash_c_flag(as_info, target):
2168
# armasm64 doesn't understand -c.
2169
if as_info.type == 'masm' and target.cpu == 'aarch64':
2170
return ''
2171
else:
2172
return '-c'
2173
2174
2175
set_config('AS_DASH_C_FLAG', as_dash_c_flag)
2176
2177
2178
@depends(as_info, target)
2179
def as_outoption(as_info, target):
2180
# The uses of ASOUTOPTION depend on the spacing for -o/-Fo.
2181
if as_info.type == 'masm' and target.cpu != 'aarch64':
2182
return '-Fo'
2183
2184
return '-o '
2185
2186
2187
set_config('ASOUTOPTION', as_outoption)
2188
2189
# clang plugin handling
2190
# ==============================================================
2191
2192
js_option('--enable-clang-plugin', env='ENABLE_CLANG_PLUGIN',
2193
help="Enable building with the Clang plugin (gecko specific static analyzers)")
2194
2195
add_old_configure_assignment('ENABLE_CLANG_PLUGIN',
2196
depends_if('--enable-clang-plugin')(lambda _: True))
2197
2198
js_option('--enable-mozsearch-plugin', env='ENABLE_MOZSEARCH_PLUGIN',
2199
help="Enable building with the mozsearch indexer plugin")
2200
2201
add_old_configure_assignment('ENABLE_MOZSEARCH_PLUGIN',
2202
depends_if('--enable-mozsearch-plugin')(lambda _: True))
2203
2204
# Libstdc++ compatibility hacks
2205
# ==============================================================
2206
#
2207
js_option('--enable-stdcxx-compat', env='MOZ_STDCXX_COMPAT',
2208
help='Enable compatibility with older libstdc++')
2209
2210
2211
@template
2212
def libstdcxx_version(var, compiler):
2213
@depends(compiler, when='--enable-stdcxx-compat')
2214
@checking(var, lambda v: v and "GLIBCXX_%s" % v.dotted)
2215
@imports(_from='mozbuild.configure.libstdcxx', _import='find_version')
2216
@imports(_from='__builtin__', _import='Exception')
2217
def version(compiler):
2218
try:
2219
result = find_version(
2220
compiler.wrapper + [compiler.compiler] + compiler.flags)
2221
except Exception:
2222
die("Couldn't determine libstdc++ version")
2223
if result:
2224
return namespace(
2225
dotted=result[0],
2226
encoded=str(result[1]),
2227
)
2228
2229
set_config(var, version.encoded)
2230
return version
2231
2232
2233
add_gcc_flag(
2234
'-D_GLIBCXX_USE_CXX11_ABI=0', cxx_compiler,
2235
when=libstdcxx_version(
2236
'MOZ_LIBSTDCXX_TARGET_VERSION', cxx_compiler))
2237
add_gcc_flag(
2238
'-D_GLIBCXX_USE_CXX11_ABI=0', host_cxx_compiler,
2239
when=libstdcxx_version(
2240
'MOZ_LIBSTDCXX_HOST_VERSION', host_cxx_compiler))
2241
2242
2243
# Support various fuzzing options
2244
# ==============================================================
2245
js_option('--enable-fuzzing', help='Enable fuzzing support')
2246
2247
@depends('--enable-fuzzing')
2248
def enable_fuzzing(value):
2249
if value:
2250
return True
2251
2252
@depends(try_compile(body='__AFL_COMPILER;',
2253
check_msg='for AFL compiler',
2254
when='--enable-fuzzing'))
2255
def enable_aflfuzzer(afl):
2256
if afl:
2257
return True
2258
2259
@depends(enable_fuzzing,
2260
enable_aflfuzzer,
2261
c_compiler,
2262
target)
2263
def enable_libfuzzer(fuzzing, afl, c_compiler, target):
2264
if fuzzing and not afl and c_compiler.type == 'clang' and target.os != 'Android':
2265
return True
2266
2267
@depends(enable_fuzzing,
2268
enable_aflfuzzer,
2269
enable_libfuzzer)
2270
def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer):
2271
if fuzzing and (afl or libfuzzer):
2272
return True
2273
2274
set_config('FUZZING', enable_fuzzing)
2275
set_define('FUZZING', enable_fuzzing)
2276
2277
set_config('LIBFUZZER', enable_libfuzzer)
2278
set_define('LIBFUZZER', enable_libfuzzer)
2279
add_old_configure_assignment('LIBFUZZER', enable_libfuzzer)
2280
2281
set_config('FUZZING_INTERFACES', enable_fuzzing_interfaces)
2282
set_define('FUZZING_INTERFACES', enable_fuzzing_interfaces)
2283
add_old_configure_assignment('FUZZING_INTERFACES', enable_fuzzing_interfaces)
2284
2285
2286
@depends(c_compiler.try_compile(flags=['-fsanitize=fuzzer-no-link'],
2287
when=enable_fuzzing,
2288
check_msg='whether the C compiler supports -fsanitize=fuzzer-no-link'), tsan)
2289
def libfuzzer_flags(value, tsan):
2290
if tsan:
2291
# With ThreadSanitizer, we should not use any libFuzzer instrumentation because
2292
# it is incompatible (e.g. there are races on global sanitizer coverage counters).
2293
# Instead we use an empty set of flags here but still build the fuzzing targets.
2294
# With this setup, we can still run files through these targets in TSan builds,
2295
# e.g. those obtained from regular fuzzing.
2296
# This code can be removed once libFuzzer has been made compatible with TSan.
2297
return namespace(no_link_flag_supported=False, use_flags=[])
2298
2299
if value:
2300
no_link_flag_supported = True
2301
# recommended for (and only supported by) clang >= 6
2302
use_flags = ['-fsanitize=fuzzer-no-link']
2303
else:
2304
no_link_flag_supported = False
2305
use_flags = ['-fsanitize-coverage=trace-pc-guard,trace-cmp']
2306
2307
return namespace(
2308
no_link_flag_supported=no_link_flag_supported,
2309
use_flags=use_flags,
2310
)
2311
2312
set_config('HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK', libfuzzer_flags.no_link_flag_supported)
2313
set_config('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags)
2314
add_old_configure_assignment('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags)
2315
2316
# Shared library building
2317
# ==============================================================
2318
2319
# XXX: The use of makefile constructs in these variables is awful.
2320
@depends(target, c_compiler)
2321
def make_shared_library(target, compiler):
2322
if target.os == 'WINNT':
2323
if compiler.type == 'gcc':
2324
return namespace(
2325
mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-o', '$@'],
2326
mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-o', '$@'],
2327
)
2328
elif compiler.type == 'clang':
2329
return namespace(
2330
mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
2331
mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
2332
)
2333
else:
2334
linker = [
2335
'$(LINKER)',
2336
'-NOLOGO', '-DLL',
2337
'-OUT:$@',
2338
'-PDB:$(LINK_PDBFILE)',
2339
'$(DSO_LDOPTS)'
2340
]
2341
return namespace(
2342
mkshlib=linker,
2343
mkcshlib=linker,
2344
)
2345
2346
cc = ['$(CC)', '$(COMPUTED_C_LDFLAGS)']
2347
cxx = ['$(CXX)', '$(COMPUTED_CXX_LDFLAGS)']
2348
flags = ['$(PGO_CFLAGS)', '$(DSO_PIC_C