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