зеркало из https://github.com/mozilla/gecko-dev.git
1195 строки
40 KiB
Python
1195 строки
40 KiB
Python
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
# vim: set filetype=python:
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
# Profiling
|
|
# ==============================================================
|
|
# Some of the options here imply an option from js/moz.configure,
|
|
# so, need to be declared before the include.
|
|
|
|
option('--enable-jprof', env='MOZ_JPROF',
|
|
help='Enable jprof profiling tool (needs mozilla/tools/jprof)')
|
|
|
|
@depends('--enable-jprof')
|
|
def jprof(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('MOZ_JPROF', jprof)
|
|
set_define('MOZ_JPROF', jprof)
|
|
imply_option('--enable-profiling', jprof)
|
|
|
|
@depends(target)
|
|
def gecko_profiler(target):
|
|
if target.os == 'Android':
|
|
return target.cpu in ('aarch64', 'arm', 'x86')
|
|
elif target.kernel == 'Linux':
|
|
return target.cpu in ('x86', 'x86_64', 'mips64')
|
|
return target.os in ('OSX', 'WINNT')
|
|
|
|
@depends(gecko_profiler)
|
|
def gecko_profiler_define(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('MOZ_GECKO_PROFILER', gecko_profiler_define)
|
|
set_define('MOZ_GECKO_PROFILER', gecko_profiler_define)
|
|
|
|
|
|
option('--enable-dmd', env='MOZ_DMD',
|
|
help='Enable Dark Matter Detector (heap profiler). '
|
|
'Also enables jemalloc, replace-malloc and profiling')
|
|
|
|
@depends('--enable-dmd')
|
|
def dmd(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('MOZ_DMD', dmd)
|
|
set_define('MOZ_DMD', dmd)
|
|
add_old_configure_assignment('MOZ_DMD', dmd)
|
|
imply_option('--enable-profiling', dmd)
|
|
imply_option('--enable-jemalloc', dmd)
|
|
imply_option('--enable-replace-malloc', dmd)
|
|
|
|
# JACK cubeb backend
|
|
# ==============================================================
|
|
option('--enable-jack', env='MOZ_JACK',
|
|
help='Enable JACK audio backend.')
|
|
|
|
@depends('--enable-jack')
|
|
def jack(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('MOZ_JACK', jack)
|
|
set_define('MOZ_JACK', jack)
|
|
|
|
# Javascript engine
|
|
# ==============================================================
|
|
include('../js/moz.configure')
|
|
|
|
|
|
# Rust
|
|
# ==============================================================
|
|
include('../build/moz.configure/rust.configure',
|
|
when='--enable-compile-environment')
|
|
|
|
|
|
# L10N
|
|
# ==============================================================
|
|
option('--with-l10n-base', nargs=1, env='L10NBASEDIR',
|
|
help='Path to l10n repositories')
|
|
|
|
@depends('--with-l10n-base')
|
|
@imports(_from='os.path', _import='isdir')
|
|
@imports(_from='os.path', _import='expanduser')
|
|
@imports(_from='os', _import='environ')
|
|
def l10n_base(value):
|
|
if value:
|
|
path = value[0]
|
|
if not isdir(path):
|
|
die("Invalid value --with-l10n-base, %s doesn't exist", path)
|
|
else:
|
|
path = os.path.join(
|
|
environ.get(
|
|
'MOZBUILD_STATE_PATH',
|
|
expanduser(os.path.join('~', '.mozbuild'))),
|
|
'l10n-central')
|
|
return os.path.realpath(os.path.abspath(path))
|
|
|
|
set_config('L10NBASEDIR', l10n_base)
|
|
|
|
|
|
# Default toolkit
|
|
# ==============================================================
|
|
# Normally, we'd want to use the `default` field on the option, but that
|
|
# requires --target to be resolved at --help time, which requires to run
|
|
# config.guess, which we want to avoid. Even better, we could actually set
|
|
# `choices` depending on the target, but that doesn't pan out for the same
|
|
# reason.
|
|
option('--enable-default-toolkit', nargs=1,
|
|
choices=('cairo-windows', 'cairo-gtk3', 'cairo-gtk3-wayland',
|
|
'cairo-cocoa', 'cairo-uikit', 'cairo-android'),
|
|
help='Select default toolkit')
|
|
|
|
@depends('--enable-default-toolkit', target, '--help')
|
|
def toolkit(value, target, _):
|
|
# Define possible choices for each platform. The default is the first one
|
|
# listed when there are several.
|
|
if target.os == 'WINNT':
|
|
platform_choices = ('cairo-windows',)
|
|
elif target.os == 'OSX':
|
|
platform_choices = ('cairo-cocoa',)
|
|
elif target.os == 'iOS':
|
|
platform_choices = ('cairo-uikit',)
|
|
elif target.os == 'Android':
|
|
platform_choices = ('cairo-android',)
|
|
else:
|
|
platform_choices = ('cairo-gtk3', 'cairo-gtk3-wayland')
|
|
|
|
if value:
|
|
if value[0] not in platform_choices:
|
|
die('`%s` is not a valid value for --enable-default-toolkit on %s\n'
|
|
'Valid values: %s', value[0], target.os,
|
|
', '.join(platform_choices))
|
|
return value[0]
|
|
|
|
return platform_choices[0]
|
|
|
|
@depends(toolkit)
|
|
def wayland(toolkit):
|
|
return toolkit == 'cairo-gtk3-wayland'
|
|
|
|
@depends(toolkit)
|
|
def toolkit(toolkit):
|
|
if toolkit == 'cairo-gtk2-x11':
|
|
widget_toolkit = 'gtk2'
|
|
elif toolkit == 'cairo-gtk3-wayland' :
|
|
widget_toolkit = 'gtk3'
|
|
else:
|
|
widget_toolkit = toolkit.replace('cairo-', '')
|
|
return widget_toolkit
|
|
|
|
set_config('MOZ_WIDGET_TOOLKIT', toolkit)
|
|
add_old_configure_assignment('MOZ_WIDGET_TOOLKIT', toolkit)
|
|
|
|
@depends(toolkit)
|
|
def toolkit_gtk(toolkit):
|
|
if toolkit == 'gtk2':
|
|
return '2'
|
|
elif toolkit == 'gtk3':
|
|
return '3'
|
|
|
|
set_define('MOZ_WIDGET_GTK', toolkit_gtk)
|
|
|
|
@depends(toolkit)
|
|
def toolkit_define(toolkit):
|
|
if toolkit not in ('gtk2', 'gtk3', 'windows'):
|
|
return 'MOZ_WIDGET_%s' % toolkit.upper()
|
|
|
|
set_define(toolkit_define, True)
|
|
|
|
|
|
option('--without-x', env='WITHOUT_X', help='Disable X11 support')
|
|
|
|
@depends('--without-x', toolkit)
|
|
def x11(value, toolkit):
|
|
if not value:
|
|
die('--without-x is not supported')
|
|
|
|
x11_toolkits = ('gtk2', 'gtk3')
|
|
if value and value.origin != 'default' and toolkit not in x11_toolkits:
|
|
die('--with-x is only valid with --enable-default-toolkit={%s}',
|
|
','.join(x11_toolkits))
|
|
|
|
return True if value and toolkit in x11_toolkits else None
|
|
|
|
set_config('MOZ_ENABLE_XREMOTE', x11)
|
|
set_define('MOZ_ENABLE_XREMOTE', x11)
|
|
set_config('MOZ_X11', x11)
|
|
set_define('MOZ_X11', x11)
|
|
add_old_configure_assignment('MOZ_X11', x11)
|
|
|
|
# Wayland support
|
|
# ==============================================================
|
|
wayland_headers = pkg_check_modules('MOZ_WAYLAND', 'gtk+-wayland-3.0 >= 3.22',
|
|
when=wayland)
|
|
|
|
set_config('MOZ_WAYLAND', depends_if(wayland_headers)(lambda _: True))
|
|
set_define('MOZ_WAYLAND', depends_if(wayland_headers)(lambda _: True))
|
|
|
|
# GL Provider
|
|
# ==============================================================
|
|
option('--with-gl-provider', nargs=1, help='Set GL provider backend type')
|
|
|
|
@depends('--with-gl-provider')
|
|
def gl_provider(value):
|
|
if value:
|
|
return value[0]
|
|
|
|
@depends(gl_provider)
|
|
def gl_provider_define(provider):
|
|
if provider:
|
|
return 'GLContextProvider%s' % provider
|
|
|
|
set_define('MOZ_GL_PROVIDER', gl_provider_define)
|
|
|
|
@depends(gl_provider, x11)
|
|
def gl_default_provider(value, x11):
|
|
if value:
|
|
return value
|
|
elif x11:
|
|
return 'GLX'
|
|
|
|
set_config('MOZ_GL_PROVIDER', gl_provider)
|
|
set_config('MOZ_GL_DEFAULT_PROVIDER', gl_default_provider)
|
|
|
|
@depends(gl_default_provider)
|
|
def gl_provider_define(provider):
|
|
if provider:
|
|
return 'GL_PROVIDER_%s' % provider
|
|
|
|
set_define(gl_provider_define, True)
|
|
|
|
|
|
# PDF printing
|
|
# ==============================================================
|
|
@depends(toolkit)
|
|
def pdf_printing(toolkit):
|
|
if toolkit in ('windows', 'gtk2', 'gtk3', 'android'):
|
|
return True
|
|
|
|
@depends(pdf_printing)
|
|
def pdf_surface_feature(pdf_printing):
|
|
if pdf_printing:
|
|
return '#define CAIRO_HAS_PDF_SURFACE 1'
|
|
else:
|
|
# CONFIGURE_SUBST_FILES need explicit empty values.
|
|
return ''
|
|
|
|
set_config('MOZ_PDF_PRINTING', pdf_printing)
|
|
set_config('PDF_SURFACE_FEATURE', pdf_surface_feature)
|
|
|
|
|
|
# Event loop instrumentation
|
|
# ==============================================================
|
|
option(env='MOZ_INSTRUMENT_EVENT_LOOP',
|
|
help='Force-enable event loop instrumentation')
|
|
|
|
@depends('MOZ_INSTRUMENT_EVENT_LOOP', toolkit)
|
|
def instrument_event_loop(value, toolkit):
|
|
if value or (toolkit in ('windows', 'gtk2', 'gtk3', 'cocoa', 'android') and
|
|
value.origin == 'default'):
|
|
return True
|
|
|
|
set_config('MOZ_INSTRUMENT_EVENT_LOOP', instrument_event_loop)
|
|
set_define('MOZ_INSTRUMENT_EVENT_LOOP', instrument_event_loop)
|
|
|
|
|
|
# Fontconfig Freetype
|
|
# ==============================================================
|
|
option(env='USE_FC_FREETYPE',
|
|
help='Force-enable the use of fontconfig freetype')
|
|
|
|
@depends('USE_FC_FREETYPE', toolkit)
|
|
def fc_freetype(value, toolkit):
|
|
if value or (toolkit in ('gtk2', 'gtk3') and
|
|
value.origin == 'default'):
|
|
return True
|
|
|
|
add_old_configure_assignment('USE_FC_FREETYPE', fc_freetype)
|
|
|
|
# Pango
|
|
# ==============================================================
|
|
pkg_check_modules('MOZ_PANGO',
|
|
'pango >= 1.22.0 pangoft2 >= 1.22.0 pangocairo >= 1.22.0',
|
|
when=toolkit_gtk)
|
|
|
|
# Fontconfig
|
|
# ==============================================================
|
|
fontconfig_info = pkg_check_modules('_FONTCONFIG', 'fontconfig >= 2.7.0',
|
|
when=fc_freetype)
|
|
|
|
@depends(fc_freetype)
|
|
def check_for_freetype2(fc_freetype):
|
|
if fc_freetype:
|
|
return True
|
|
|
|
# Check for freetype2. Flags are combined with fontconfig flags.
|
|
freetype2_info = pkg_check_modules('_FT2', 'freetype2 >= 6.1.0',
|
|
when=check_for_freetype2)
|
|
|
|
@depends(fontconfig_info, freetype2_info)
|
|
def freetype2_combined_info(fontconfig_info, freetype2_info):
|
|
if not freetype2_info:
|
|
return
|
|
if not fontconfig_info:
|
|
return freetype2_info
|
|
return namespace(
|
|
cflags=freetype2_info.cflags + fontconfig_info.cflags,
|
|
libs=freetype2_info.libs + fontconfig_info.libs,
|
|
)
|
|
|
|
add_old_configure_assignment('_HAVE_FREETYPE2',
|
|
depends_if(freetype2_info)(lambda _: True))
|
|
|
|
# Apple platform decoder support
|
|
# ==============================================================
|
|
@depends(toolkit)
|
|
def applemedia(toolkit):
|
|
if toolkit in ('cocoa', 'uikit'):
|
|
return True
|
|
|
|
set_config('MOZ_APPLEMEDIA', applemedia)
|
|
set_define('MOZ_APPLEMEDIA', applemedia)
|
|
add_old_configure_assignment('MOZ_APPLEMEDIA', applemedia)
|
|
|
|
# Windows Media Foundation support
|
|
# ==============================================================
|
|
option('--disable-wmf',
|
|
help='Disable support for Windows Media Foundation')
|
|
|
|
@depends('--disable-wmf', target)
|
|
def wmf(value, target):
|
|
enabled = bool(value)
|
|
if value.origin == 'default':
|
|
# Enable Windows Media Foundation support by default.
|
|
# Note our minimum SDK version is Windows 7 SDK, so we are (currently)
|
|
# guaranteed to have a recent-enough SDK to build WMF.
|
|
enabled = target.os == 'WINNT'
|
|
if enabled and target.os != 'WINNT':
|
|
die('Cannot enable Windows Media Foundation support on %s', target.os)
|
|
if enabled:
|
|
return True
|
|
|
|
set_config('MOZ_WMF', wmf)
|
|
set_define('MOZ_WMF', wmf)
|
|
|
|
# FFmpeg H264/AAC Decoding Support
|
|
# ==============================================================
|
|
option('--disable-ffmpeg',
|
|
help='Disable FFmpeg for fragmented H264/AAC decoding')
|
|
|
|
@depends('--disable-ffmpeg', target)
|
|
def ffmpeg(value, target):
|
|
enabled = bool(value)
|
|
if value.origin == 'default':
|
|
enabled = target.os not in ('Android', 'WINNT')
|
|
if enabled:
|
|
return True
|
|
|
|
set_config('MOZ_FFMPEG', ffmpeg)
|
|
set_define('MOZ_FFMPEG', ffmpeg)
|
|
imply_option('--enable-fmp4', ffmpeg, '--enable-ffmpeg')
|
|
|
|
# Libaom AV1 Video Codec Support
|
|
# ==============================================================
|
|
option('--enable-av1',
|
|
help='Enable libaom for av1 video support')
|
|
|
|
@depends('--enable-av1', target, milestone)
|
|
def av1(value, target, milestone):
|
|
enabled = bool(value)
|
|
if value.origin == 'default' and milestone.is_nightly:
|
|
enabled = target.os != 'Android'
|
|
if enabled:
|
|
return True
|
|
|
|
set_config('MOZ_AV1', av1)
|
|
set_define('MOZ_AV1', av1)
|
|
|
|
# Built-in fragmented MP4 support.
|
|
# ==============================================================
|
|
option('--disable-fmp4', env='MOZ_FMP4',
|
|
help='Disable support for in built Fragmented MP4 parsing')
|
|
|
|
@depends('--disable-fmp4', target, wmf, applemedia)
|
|
def fmp4(value, target, wmf, applemedia):
|
|
enabled = bool(value)
|
|
if value.origin == 'default':
|
|
# target.os == 'Android' includes all B2G versions
|
|
enabled = wmf or applemedia or target.os == 'Android'
|
|
if enabled:
|
|
return True
|
|
|
|
set_config('MOZ_FMP4', fmp4)
|
|
set_define('MOZ_FMP4', fmp4)
|
|
add_old_configure_assignment('MOZ_FMP4', fmp4)
|
|
|
|
# EME Support
|
|
# ==============================================================
|
|
# Widevine is enabled by default in desktop browser builds.
|
|
@depends(build_project, '--help')
|
|
def eme_default(build_project, help):
|
|
if build_project == 'browser':
|
|
return 'widevine'
|
|
|
|
option('--enable-eme',
|
|
nargs='*',
|
|
choices=('widevine',),
|
|
default=eme_default,
|
|
help='Enable support for Encrypted Media Extensions')
|
|
|
|
@depends('--enable-eme', target)
|
|
def enable_eme(value, target):
|
|
# Widevine EME by default enabled on desktop Windows, MacOS and Linux,
|
|
# x86 and x64 builds.
|
|
if (target.kernel in ('Darwin', 'WINNT', 'Linux') and
|
|
target.os not in ('Android', 'iOS') and
|
|
target.cpu in ('x86', 'x86_64')):
|
|
return value
|
|
elif value and value.origin != 'default':
|
|
die('%s is not supported on %s' % (value.format('--enable-eme'), target.alias))
|
|
# Return the same type of OptionValue (Positive or Negative), with an empty tuple.
|
|
return value.__class__(())
|
|
|
|
@depends(enable_eme, fmp4)
|
|
def eme(value, fmp4):
|
|
enabled = bool(value)
|
|
if value.origin == 'default':
|
|
enabled = enabled or fmp4
|
|
if enabled and not fmp4:
|
|
die('Encrypted Media Extension support requires '
|
|
'Fragmented MP4 support')
|
|
if enabled:
|
|
return True
|
|
|
|
@depends(enable_eme)
|
|
def eme_modules(value):
|
|
return value
|
|
|
|
set_config('MOZ_EME_MODULES', eme_modules)
|
|
|
|
option(name='--enable-chrome-format',
|
|
help='Select FORMAT of chrome files during packaging.',
|
|
nargs=1,
|
|
choices=('omni', 'jar', 'flat'),
|
|
default='omni')
|
|
|
|
@depends('--enable-chrome-format')
|
|
def packager_format(value):
|
|
return value[0]
|
|
|
|
set_config('MOZ_PACKAGER_FORMAT', packager_format)
|
|
|
|
@depends(host, build_project)
|
|
def jar_maker_format(host, build_project):
|
|
# Multilocales for mobile/android use the same mergedirs for all locales,
|
|
# so we can't use symlinks for those builds.
|
|
if host.os == 'WINNT' or build_project == 'mobile/android':
|
|
return 'flat'
|
|
return 'symlink'
|
|
|
|
set_config('MOZ_JAR_MAKER_FILE_FORMAT', jar_maker_format)
|
|
|
|
@depends(toolkit)
|
|
def omnijar_name(toolkit):
|
|
# Fennec's static resources live in the assets/ folder of the
|
|
# APK. Adding a path to the name here works because we only
|
|
# have one omnijar file in the final package (which is not the
|
|
# case on desktop), and necessitates some contortions during
|
|
# packaging so that the resources in the omnijar are considered
|
|
# as rooted at / and not as rooted at assets/ (which again is
|
|
# not the case on desktop: there are omnijars rooted at webrtc/,
|
|
# etc). packager.mk handles changing the rooting of the single
|
|
# omnijar.
|
|
return 'assets/omni.ja' if toolkit == 'android' else 'omni.ja'
|
|
|
|
set_config('OMNIJAR_NAME', omnijar_name)
|
|
|
|
project_flag('MOZ_PLACES',
|
|
help='Build Places if required',
|
|
set_as_define=True)
|
|
|
|
project_flag('MOZ_SERVICES_HEALTHREPORT',
|
|
help='Build Firefox Health Reporter Service',
|
|
set_for_old_configure=True,
|
|
set_as_define=True)
|
|
|
|
project_flag('MOZ_SERVICES_SYNC',
|
|
help='Build Sync Services if required')
|
|
|
|
project_flag('MOZ_ANDROID_HISTORY',
|
|
help='Enable Android History instead of Places',
|
|
set_as_define=True)
|
|
|
|
option(env='MOZ_ALLOW_LEGACY_EXTENSIONS',
|
|
default=milestone.is_nightly,
|
|
help='Allow legacy browser extensions')
|
|
|
|
@depends('MOZ_ALLOW_LEGACY_EXTENSIONS')
|
|
def legacy_extensions(value):
|
|
if bool(value):
|
|
return True
|
|
|
|
set_config('MOZ_ALLOW_LEGACY_EXTENSIONS', legacy_extensions)
|
|
set_define('MOZ_ALLOW_LEGACY_EXTENSIONS', legacy_extensions)
|
|
|
|
@depends('MOZ_PLACES', 'MOZ_ANDROID_HISTORY')
|
|
def check_places_and_android_history(places, android_history):
|
|
if places and android_history:
|
|
die('Cannot use MOZ_ANDROID_HISTORY alongside MOZ_PLACES.')
|
|
|
|
# gpsd support
|
|
# ==============================================================
|
|
option('--enable-gpsd', env='MOZ_GPSD',
|
|
help='Enable gpsd support')
|
|
|
|
@depends('--enable-gpsd')
|
|
def gpsd(value):
|
|
return bool(value)
|
|
|
|
system_gpsd = pkg_check_modules('MOZ_GPSD', 'libgps >= 3.11',
|
|
when=gpsd)
|
|
|
|
set_config('MOZ_GPSD', depends_if(system_gpsd)(lambda _: True))
|
|
|
|
# Miscellaneous programs
|
|
# ==============================================================
|
|
|
|
check_prog('TAR', ('gnutar', 'gtar', 'tar'))
|
|
check_prog('UNZIP', ('unzip',))
|
|
check_prog('ZIP', ('zip',))
|
|
check_prog('GN', ('gn',), allow_missing=True)
|
|
|
|
# Key files
|
|
# ==============================================================
|
|
include('../build/moz.configure/keyfiles.configure')
|
|
|
|
simple_keyfile('Mozilla API')
|
|
|
|
simple_keyfile('Google API')
|
|
|
|
id_and_secret_keyfile('Bing API')
|
|
|
|
simple_keyfile('Adjust SDK')
|
|
|
|
id_and_secret_keyfile('Leanplum SDK')
|
|
|
|
simple_keyfile('Pocket API')
|
|
|
|
# Servo integration
|
|
# ==============================================================
|
|
option('--enable-stylo', nargs='?', choices=('build',),
|
|
help='Include Stylo in the build and/or enable it at runtime')
|
|
|
|
@depends('--enable-stylo', '--help')
|
|
def stylo_config(value, _):
|
|
build_stylo = None
|
|
enable_stylo = None
|
|
|
|
# If nothing is specified, default to building and enabling Stylo where it
|
|
# is known to work.
|
|
if value.origin == 'default':
|
|
build_stylo = True
|
|
enable_stylo = True
|
|
elif len(value) and value[0] == 'build':
|
|
# Build but disable by request.
|
|
build_stylo = True
|
|
elif bool(value):
|
|
# Build and enable.
|
|
build_stylo = True
|
|
enable_stylo = True
|
|
|
|
return namespace(
|
|
build = build_stylo,
|
|
enable = enable_stylo,
|
|
)
|
|
|
|
option('--disable-stylo-build-bindgen',
|
|
help='Disable build-time bindgen for Stylo')
|
|
|
|
@depends(stylo_config, '--enable-stylo-build-bindgen', '--enable-compile-environment')
|
|
def building_stylo_bindgen(stylo_config, bindgen_enabled, compile_environment):
|
|
if not compile_environment:
|
|
return False
|
|
if not bindgen_enabled:
|
|
return False
|
|
return stylo_config.build
|
|
|
|
# We support setting up the appropriate options for Stylo's build-time
|
|
# bindings generation via setting LLVM_CONFIG or by providing explicit
|
|
# configure options. The Windows installer of LLVM/Clang doesn't provide
|
|
# llvm-config, so we need both methods to support all of our tier-1
|
|
# platforms.
|
|
@depends(host)
|
|
@imports('which')
|
|
@imports('os')
|
|
@imports('subprocess')
|
|
def llvm_config_paths(host):
|
|
llvm_supported_versions = ['6.0', '5.0', '4.0', '3.9']
|
|
llvm_config_progs = []
|
|
for version in llvm_supported_versions:
|
|
llvm_config_progs += [
|
|
'llvm-config-%s' % version,
|
|
'llvm-config-mp-%s' % version, # MacPorts' chosen naming scheme.
|
|
'llvm-config%s' % version.replace('.', ''),
|
|
]
|
|
llvm_config_progs.append('llvm-config')
|
|
|
|
# Homebrew on macOS doesn't make clang available on PATH, so we have to
|
|
# look for it in non-standard places.
|
|
if host.kernel == 'Darwin':
|
|
try:
|
|
brew = which.which('brew')
|
|
brew_config = subprocess.check_output([brew, 'config']).strip()
|
|
|
|
for line in brew_config.splitlines():
|
|
if line.startswith('HOMEBREW_PREFIX'):
|
|
fields = line.split(None, 2)
|
|
prefix = fields[1] if len(fields) == 2 else ''
|
|
path = ['opt', 'llvm', 'bin', 'llvm-config']
|
|
llvm_config_progs.append(os.path.join(prefix, *path))
|
|
break
|
|
except which.WhichError:
|
|
# Homebrew not installed, which is fine.
|
|
pass
|
|
|
|
# Also add in the location to which `mach bootstrap` or
|
|
# `mach artifact toolchain` installs clang.
|
|
mozbuild_state_dir = os.environ.get('MOZBUILD_STATE_PATH',
|
|
os.path.expanduser(os.path.join('~', '.mozbuild')))
|
|
bootstrap_llvm_config = os.path.join(mozbuild_state_dir, 'clang', 'bin', 'llvm-config')
|
|
|
|
llvm_config_progs.append(bootstrap_llvm_config)
|
|
|
|
return llvm_config_progs
|
|
|
|
llvm_config = check_prog('LLVM_CONFIG', llvm_config_paths,
|
|
when=building_stylo_bindgen,
|
|
what='llvm-config', allow_missing=True)
|
|
|
|
with only_when(building_stylo_bindgen):
|
|
option('--with-libclang-path', nargs=1,
|
|
help='Absolute path to a directory containing Clang/LLVM libraries for Stylo (version 3.9.x or above)')
|
|
option('--with-clang-path', nargs=1,
|
|
help='Absolute path to a Clang binary for Stylo bindgen (version 3.9.x or above)')
|
|
|
|
def invoke_llvm_config(llvm_config, *options):
|
|
'''Invoke llvm_config with the given options and return the first line of
|
|
output.'''
|
|
lines = check_cmd_output(llvm_config, *options).splitlines()
|
|
return lines[0]
|
|
|
|
@imports(_from='textwrap', _import='dedent')
|
|
def check_minimum_llvm_config_version(llvm_config):
|
|
version = Version(invoke_llvm_config(llvm_config, '--version'))
|
|
min_version = Version('3.9.0')
|
|
if version < min_version:
|
|
die(dedent('''\
|
|
llvm installation {} is incompatible with Stylo bindgen.
|
|
|
|
To compile Stylo, please install version {} or greater of
|
|
Clang + LLVM and ensure that the 'llvm-config' from that
|
|
installation is first on your path.
|
|
|
|
You can verify this by typing 'llvm-config --version'.
|
|
'''.format(version, min_version)))
|
|
|
|
@depends(llvm_config, '--with-libclang-path', '--with-clang-path',
|
|
host_library_name_info, host)
|
|
@imports('os.path')
|
|
@imports('glob')
|
|
@imports(_from='textwrap', _import='dedent')
|
|
def bindgen_config_paths(llvm_config, libclang_path, clang_path,
|
|
library_name_info, host):
|
|
def search_for_libclang(path):
|
|
# Try to ensure that the clang shared library that bindgen is going
|
|
# to look for is actually present. The files that we search for
|
|
# mirror the logic in clang-sys/build.rs.
|
|
libclang_choices = []
|
|
if host.os == 'WINNT':
|
|
libclang_choices.append('libclang.dll')
|
|
libclang_choices.append('%sclang%s' % (library_name_info.dll.prefix,
|
|
library_name_info.dll.suffix))
|
|
if host.kernel == 'Linux':
|
|
libclang_choices.append('libclang.so.1')
|
|
|
|
if host.os == 'OpenBSD':
|
|
libclang_choices = glob.glob(path + '/libclang.so.*.*')
|
|
|
|
# At least one of the choices must be found.
|
|
for choice in libclang_choices:
|
|
libclang = os.path.join(path, choice)
|
|
if os.path.exists(libclang):
|
|
return (True, None)
|
|
else:
|
|
return (False, list(set(libclang_choices)))
|
|
|
|
if not libclang_path and not clang_path:
|
|
# We must have LLVM_CONFIG in this case.
|
|
if not llvm_config:
|
|
die(dedent('''\
|
|
Could not find LLVM/Clang installation for compiling stylo build-time
|
|
bindgen. Please specify the 'LLVM_CONFIG' environment variable
|
|
(recommended), pass the '--with-libclang-path' and '--with-clang-path'
|
|
options to configure, or put 'llvm-config' in your PATH. Altering your
|
|
PATH may expose 'clang' as well, potentially altering your compiler,
|
|
which may not be what you intended.'''))
|
|
|
|
check_minimum_llvm_config_version(llvm_config)
|
|
libclang_arg = '--bindir' if host.os == 'WINNT' else '--libdir'
|
|
libclang_path = invoke_llvm_config(llvm_config, libclang_arg)
|
|
clang_path = os.path.join(invoke_llvm_config(llvm_config, '--bindir'),
|
|
'clang')
|
|
libclang_path = normsep(libclang_path)
|
|
clang_path = normsep(clang_path)
|
|
|
|
# Debian-based distros, at least, can have llvm-config installed
|
|
# but not have other packages installed. Since the user is trying
|
|
# to use their system packages, we can't be more specific about what
|
|
# they need.
|
|
clang_resolved = find_program(clang_path)
|
|
if not clang_resolved:
|
|
die(dedent('''\
|
|
The file {} returned by `llvm-config {}` does not exist.
|
|
clang is required to build Stylo. Please install the necessary packages,
|
|
run `mach bootstrap`, or add --disable-stylo to your mozconfig.
|
|
'''.format(clang_path, '--bindir')))
|
|
|
|
if not os.path.exists(libclang_path):
|
|
die(dedent('''\
|
|
The directory {} returned by `llvm-config {}` does not exist.
|
|
clang is required to build Stylo. Please install the necessary packages,
|
|
run `mach bootstrap`, or add --disable-stylo to your mozconfig.
|
|
'''.format(libclang_path, libclang_arg)))
|
|
|
|
(found, searched) = search_for_libclang(libclang_path)
|
|
if not found:
|
|
die(dedent('''\
|
|
Could not find the clang shared library in the path {}
|
|
returned by `llvm-config {}` (searched for files {}).
|
|
clang is required to build Stylo. Please install the necessary packages,
|
|
run `mach bootstrap`, or add --disable-stylo to your mozconfig.
|
|
'''.format(libclang_path, libclang_arg, searched)))
|
|
|
|
return namespace(
|
|
libclang_path=libclang_path,
|
|
clang_path=clang_resolved,
|
|
)
|
|
|
|
if (not libclang_path and clang_path) or \
|
|
(libclang_path and not clang_path):
|
|
die(dedent('''\
|
|
You must provide both of --with-libclang-path and --with-clang-path
|
|
or neither of them.'''))
|
|
|
|
libclang_path = libclang_path[0]
|
|
clang_path = clang_path[0]
|
|
|
|
if not os.path.exists(libclang_path) or \
|
|
not os.path.isdir(libclang_path):
|
|
die(dedent('''\
|
|
The argument to --with-libclang-path is not a directory: {}
|
|
'''.format(libclang_path)))
|
|
|
|
(found, searched) = search_for_libclang(libclang_path)
|
|
if not found:
|
|
die(dedent('''\
|
|
Could not find the clang shared library in the path {}
|
|
specified by --with-libclang-path (searched for files {}).
|
|
'''.format(libclang_path, searched)))
|
|
|
|
clang_resolved = find_program(clang_path)
|
|
if not clang_resolved:
|
|
die(dedent('''\
|
|
The argument to --with-clang-path is not a file: {}
|
|
'''.format(clang_path)))
|
|
|
|
return namespace(
|
|
libclang_path=libclang_path,
|
|
clang_path=clang_resolved,
|
|
)
|
|
|
|
set_config('MOZ_LIBCLANG_PATH', bindgen_config_paths.libclang_path)
|
|
set_config('MOZ_CLANG_PATH', bindgen_config_paths.clang_path)
|
|
set_config('MOZ_STYLO_BINDGEN', depends_if('--enable-stylo-build-bindgen')(lambda _: True))
|
|
|
|
set_config('MOZ_STYLO', stylo_config.build)
|
|
set_define('MOZ_STYLO', stylo_config.build)
|
|
set_config('MOZ_STYLO_ENABLE', stylo_config.enable)
|
|
set_define('MOZ_STYLO_ENABLE', stylo_config.enable)
|
|
|
|
option('--with-servo', env='SERVO_TARGET_DIR', nargs=1,
|
|
help='Absolute path of the target directory where libgeckoservo can '
|
|
'be found. This is generally servo_src_dir/target/release.')
|
|
|
|
@depends_if('--with-servo')
|
|
def servo_target_dir(value):
|
|
return value[0]
|
|
|
|
set_config('SERVO_TARGET_DIR', servo_target_dir)
|
|
|
|
# WebRender integration
|
|
option('--enable-webrender', nargs='?', choices=('build',),
|
|
help='Include WebRender in the build and/or enable it at runtime')
|
|
|
|
@depends('--enable-webrender', milestone)
|
|
def webrender(value, milestone):
|
|
build_webrender = None
|
|
enable_webrender = None
|
|
|
|
if value.origin == 'default':
|
|
# if nothing is specified, default to just building on Nightly
|
|
build_webrender = milestone.is_nightly
|
|
elif len(value) and value[0] == 'build':
|
|
# if explicitly set to 'build', then we build but don't enable
|
|
build_webrender = True
|
|
elif bool(value):
|
|
# if set to true, then build and enable
|
|
build_webrender = True
|
|
enable_webrender = True
|
|
|
|
# in all other cases, don't build it or enable it (defaults are fine)
|
|
return namespace(
|
|
build = build_webrender,
|
|
enable = enable_webrender,
|
|
)
|
|
|
|
set_config('MOZ_BUILD_WEBRENDER', webrender.build)
|
|
set_define('MOZ_BUILD_WEBRENDER', webrender.build)
|
|
set_config('MOZ_ENABLE_WEBRENDER', webrender.enable)
|
|
|
|
# SIMD acceleration for Rust code (currently just encoding_rs)
|
|
|
|
option('--enable-rust-simd', env='MOZ_RUST_SIMD',
|
|
help='Enable explicit SIMD in Rust code.')
|
|
|
|
@depends('--enable-rust-simd', target)
|
|
def rust_simd(value, target):
|
|
# As of 2017-06-13, the simd crate only works on aarch64,
|
|
# x86 and x86_64. It's meant to work on 32-bit ARM, too,
|
|
# but currently does not.
|
|
if target.cpu in ('aarch64', 'x86', 'x86_64') and value:
|
|
return True
|
|
|
|
set_config('MOZ_RUST_SIMD', rust_simd)
|
|
set_define('MOZ_RUST_SIMD', rust_simd)
|
|
|
|
# Printing
|
|
# ==============================================================
|
|
@depends(target)
|
|
def ios_disable_printing(target):
|
|
if target.os == 'iOS':
|
|
return False
|
|
|
|
imply_option('--enable-printing', ios_disable_printing, reason='--target')
|
|
|
|
option('--disable-printing', help='Disable printing support')
|
|
|
|
@depends('--disable-printing')
|
|
def printing(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('NS_PRINTING', printing)
|
|
set_define('NS_PRINTING', printing)
|
|
set_define('NS_PRINT_PREVIEW', printing)
|
|
|
|
# Speech-dispatcher support
|
|
# ==============================================================
|
|
@depends(toolkit)
|
|
def no_speechd_on_non_gtk(toolkit):
|
|
if toolkit not in ('gtk2', 'gtk3'):
|
|
return False
|
|
|
|
imply_option('--enable-synth-speechd', no_speechd_on_non_gtk,
|
|
reason='--enable-default-toolkit')
|
|
|
|
option('--disable-synth-speechd', help='Disable speech-dispatcher support')
|
|
|
|
set_config('MOZ_SYNTH_SPEECHD',
|
|
depends_if('--disable-synth-speechd')(lambda _: True))
|
|
|
|
# Speech API
|
|
# ==============================================================
|
|
option('--disable-webspeech', help='Disable support for HTML Speech API')
|
|
|
|
@depends('--disable-webspeech', '--help')
|
|
def webspeech(value, _):
|
|
if value:
|
|
return True
|
|
|
|
set_config('MOZ_WEBSPEECH', webspeech)
|
|
set_define('MOZ_WEBSPEECH', webspeech)
|
|
add_old_configure_assignment('MOZ_WEBSPEECH', webspeech)
|
|
|
|
# Speech API test backend
|
|
# ==============================================================
|
|
option('--enable-webspeechtestbackend', default=webspeech,
|
|
help='Enable support for HTML Speech API Test Backend')
|
|
|
|
@depends_if('--enable-webspeechtestbackend')
|
|
def webspeech_test_backend(value):
|
|
return True
|
|
|
|
set_config('MOZ_WEBSPEECH_TEST_BACKEND', webspeech_test_backend)
|
|
set_define('MOZ_WEBSPEECH_TEST_BACKEND', webspeech_test_backend)
|
|
|
|
# Enable IPDL's "expensive" unit tests
|
|
# ==============================================================
|
|
option('--enable-ipdl-tests', help='Enable expensive IPDL tests')
|
|
|
|
set_config('MOZ_IPDL_TESTS',
|
|
depends_if('--enable-ipdl-tests')(lambda _: True))
|
|
|
|
include('nss.configure')
|
|
|
|
# Graphics
|
|
# ==============================================================
|
|
option('--disable-skia', help='Disable use of Skia')
|
|
|
|
@depends('--disable-skia')
|
|
def skia(value):
|
|
if not value:
|
|
die('--disable-skia is not supported anymore')
|
|
else:
|
|
return True
|
|
|
|
set_config('MOZ_ENABLE_SKIA', skia)
|
|
set_define('MOZ_ENABLE_SKIA', skia)
|
|
set_define('USE_SKIA', skia)
|
|
|
|
@depends(skia, target)
|
|
def skia_android(skia, target):
|
|
if skia and target.os == 'Android':
|
|
return True
|
|
|
|
set_define('SK_BUILD_FOR_ANDROID_NDK', skia_android)
|
|
|
|
option('--disable-skia-gpu', help='Disable use of Skia-GPU')
|
|
|
|
@depends('--disable-skia-gpu', skia, target)
|
|
def skia_gpu(value, skia, target):
|
|
if value.origin == 'default':
|
|
if not skia:
|
|
return None
|
|
# Skia GPU support may not reliably build on certain *BSDs (see bug 1234494)
|
|
if target.os in ('NetBSD', 'OpenBSD'):
|
|
return None
|
|
elif value and not skia:
|
|
die('Cannot enable Skia-GPU without enabling Skia')
|
|
if skia and value:
|
|
return True
|
|
|
|
set_config('MOZ_ENABLE_SKIA_GPU', skia_gpu)
|
|
set_define('USE_SKIA_GPU', skia_gpu)
|
|
|
|
option('--enable-skia-pdf', help='Enable Skia PDF')
|
|
|
|
@depends('--enable-skia-pdf', skia, milestone)
|
|
def skia_pdf(value, skia, milestone):
|
|
if value.origin == 'default':
|
|
if not skia:
|
|
return None
|
|
if milestone.is_nightly:
|
|
return True
|
|
elif value and not skia:
|
|
die('Cannot enable Skia PDF without enabling Skia')
|
|
if skia and value:
|
|
return True
|
|
|
|
set_config('MOZ_ENABLE_SKIA_PDF', skia_pdf)
|
|
set_define('MOZ_ENABLE_SKIA_PDF', skia_pdf)
|
|
|
|
option('--enable-skia-pdf-sfntly', help='Enable SFNTLY font subsetting in Skia PDF')
|
|
|
|
@depends('--enable-skia-pdf-sfntly', skia_pdf)
|
|
def skia_pdf_sfntly(value, skia_pdf):
|
|
if value.origin == 'default':
|
|
return skia_pdf
|
|
if value and not skia_pdf:
|
|
die('Cannot enable SFNTLY subsetting without enabling Skia PDF')
|
|
if skia_pdf and value:
|
|
return True
|
|
|
|
set_config('MOZ_ENABLE_SKIA_PDF_SFNTLY', skia_pdf_sfntly)
|
|
set_define('MOZ_ENABLE_SKIA_PDF_SFNTLY', skia_pdf_sfntly)
|
|
|
|
@depends(skia_pdf_sfntly)
|
|
def sfntly_includes(skia_pdf_sfntly):
|
|
includes = []
|
|
if skia_pdf_sfntly:
|
|
includes += [
|
|
'/gfx/sfntly/cpp/src',
|
|
]
|
|
return includes
|
|
|
|
set_config('SFNTLY_INCLUDES', sfntly_includes)
|
|
|
|
@depends(skia, skia_gpu)
|
|
def skia_includes(skia, skia_gpu):
|
|
includes = []
|
|
if skia:
|
|
includes += [
|
|
'/gfx/skia',
|
|
'/gfx/skia/skia/include/config',
|
|
'/gfx/skia/skia/include/core',
|
|
]
|
|
|
|
if skia_gpu:
|
|
includes += [
|
|
'/gfx/skia/skia/include/gpu',
|
|
'/gfx/skia/skia/include/utils',
|
|
]
|
|
|
|
return includes
|
|
|
|
set_config('SKIA_INCLUDES', skia_includes)
|
|
|
|
# Build Freetype in the tree
|
|
# ==============================================================
|
|
@depends(target, skia_pdf)
|
|
def tree_freetype(target, skia_pdf):
|
|
if target.os == 'Android' or (skia_pdf and target.os == 'WINNT'):
|
|
return True
|
|
|
|
set_define('MOZ_TREE_FREETYPE', tree_freetype)
|
|
set_config('MOZ_TREE_FREETYPE', tree_freetype)
|
|
add_old_configure_assignment('MOZ_TREE_FREETYPE', tree_freetype)
|
|
|
|
set_define('HAVE_FT_BITMAP_SIZE_Y_PPEM', tree_freetype)
|
|
set_define('HAVE_FT_GLYPHSLOT_EMBOLDEN', tree_freetype)
|
|
set_define('HAVE_FT_LOAD_SFNT_TABLE', tree_freetype)
|
|
|
|
@depends(freetype2_combined_info, tree_freetype, check_build_environment)
|
|
def ft2_info(freetype2_combined_info, tree_freetype, build_env):
|
|
if tree_freetype:
|
|
return namespace(cflags=('-I%s/modules/freetype2/include' % build_env.topsrcdir,),
|
|
libs=())
|
|
if freetype2_combined_info:
|
|
return freetype2_combined_info
|
|
|
|
set_config('FT2_LIBS', ft2_info.libs)
|
|
add_old_configure_assignment('FT2_LIBS',
|
|
ft2_info.libs)
|
|
add_old_configure_assignment('FT2_CFLAGS',
|
|
ft2_info.cflags)
|
|
|
|
# Mortar
|
|
# ==============================================================
|
|
option('--enable-mortar', help='Enable mortar extension')
|
|
|
|
set_config('MOZ_MORTAR', True, when='--enable-mortar')
|
|
|
|
# Marionette remote protocol
|
|
# ==============================================================
|
|
#
|
|
# Marionette is the Gecko remote protocol used for various remote control,
|
|
# automation, and testing purposes throughout Gecko, Firefox, and Fennec.
|
|
# Marionette lives in ../testing/marionette.
|
|
#
|
|
# Marionette is not really a toolkit feature, as much as a Gecko engine
|
|
# feature. But it is enabled based on the toolkit (and target), so here
|
|
# it lives.
|
|
#
|
|
# It also backs ../testing/geckodriver, which is Mozilla's WebDriver
|
|
# implementation.
|
|
#
|
|
# For more information, see
|
|
# https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette.
|
|
|
|
@depends(target)
|
|
def marionette(target):
|
|
"""Enable Marionette by default, except on Android."""
|
|
if target.os != 'Android':
|
|
return True
|
|
|
|
imply_option('--enable-marionette', marionette, reason='Not Android')
|
|
|
|
option('--enable-marionette',
|
|
help='Enable Marionette remote protocol')
|
|
|
|
@depends('--enable-marionette')
|
|
def marionette(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('ENABLE_MARIONETTE', marionette)
|
|
|
|
# geckodriver WebDriver implementation
|
|
# ==============================================================
|
|
option('--enable-geckodriver', help='Enable WebDriver implementation')
|
|
|
|
@depends('--enable-geckodriver',
|
|
'MOZ_AUTOMATION',
|
|
compile_environment,
|
|
cross_compiling,
|
|
hazard_analysis)
|
|
def geckodriver(enable, automation, compile_env, cross_compile, hazard):
|
|
"""
|
|
geckodriver is implied on supported platforms when MOZ_AUTOMATION
|
|
is set, but we also provide the --enable-geckodriver option for
|
|
developers to use.
|
|
|
|
At the present time, we want individual developers to be able to
|
|
opt-in to building geckodriver locally, and for it to be enabled by
|
|
default on supported CI build platforms.
|
|
"""
|
|
if enable:
|
|
if not compile_env:
|
|
die("--enable-geckodriver is not available without a compile "
|
|
"environment. A geckodriver binary will be downloaded during "
|
|
"an artifact build by default where available.")
|
|
return True
|
|
|
|
if enable.origin == 'default':
|
|
broken_platforms = cross_compile or hazard
|
|
|
|
if automation and compile_env and not broken_platforms:
|
|
return True
|
|
|
|
set_config('ENABLE_GECKODRIVER', geckodriver)
|
|
|
|
# WebRTC
|
|
# ========================================================
|
|
@depends(target)
|
|
def webrtc_default(target):
|
|
# Turn off webrtc for OS's we don't handle yet, but allow
|
|
# --enable-webrtc to override.
|
|
os_match = False
|
|
for os_fragment in ('linux', 'mingw', 'android', 'linuxandroid',
|
|
'dragonfly', 'freebsd', 'netbsd', 'openbsd',
|
|
'darwin'):
|
|
if target.raw_os.startswith(os_fragment):
|
|
os_match = True
|
|
|
|
cpu_match = False
|
|
if (target.cpu in ('x86_64', 'arm', 'aarch64', 'x86', 'ia64', 'mips32', 'mips64') or
|
|
target.cpu.startswith('ppc')):
|
|
cpu_match = True
|
|
|
|
if os_match and cpu_match:
|
|
return True
|
|
return False
|
|
|
|
option('--disable-webrtc', default=webrtc_default,
|
|
help='Disable support for WebRTC')
|
|
|
|
@depends('--disable-webrtc')
|
|
def webrtc(enabled):
|
|
if enabled:
|
|
return True
|
|
|
|
set_config('MOZ_WEBRTC', webrtc)
|
|
set_define('MOZ_WEBRTC', webrtc)
|
|
add_old_configure_assignment('MOZ_WEBRTC', webrtc)
|
|
set_config('MOZ_WEBRTC_SIGNALING', webrtc)
|
|
set_define('MOZ_WEBRTC_SIGNALING', webrtc)
|
|
set_config('MOZ_PEERCONNECTION', webrtc)
|
|
set_define('MOZ_PEERCONNECTION', webrtc)
|
|
# MOZ_WEBRTC_ASSERT_ALWAYS turns on a number of safety asserts in
|
|
# opt/production builds (via MOZ_CRASH())
|
|
set_config('MOZ_WEBRTC_ASSERT_ALWAYS', webrtc)
|
|
set_define('MOZ_WEBRTC_ASSERT_ALWAYS', webrtc)
|
|
|
|
# ASan Reporter Addon
|
|
# ==============================================================
|
|
option('--enable-address-sanitizer-reporter',
|
|
help='Enable Address Sanitizer Reporter Extension')
|
|
|
|
@depends('--enable-address-sanitizer-reporter')
|
|
def enable_asan_reporter(value):
|
|
if value:
|
|
return True
|
|
|
|
set_config('MOZ_ASAN_REPORTER', enable_asan_reporter)
|
|
set_define('MOZ_ASAN_REPORTER', enable_asan_reporter)
|
|
|
|
# Elfhack
|
|
# ==============================================================
|
|
@depends(host, target)
|
|
def has_elfhack(host, target):
|
|
return target.kernel == 'Linux' and host.kernel == 'Linux' and \
|
|
target.cpu in ('arm', 'x86', 'x86_64')
|
|
|
|
with only_when(has_elfhack):
|
|
option('--disable-elf-hack', help='Disable elf hacks')
|
|
|
|
set_config('USE_ELF_HACK',
|
|
depends_if('--enable-elf-hack')(lambda _: True))
|