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