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
include('util.configure')
8
include('checks.configure')
9
10
# Make `toolkit` available when toolkit/moz.configure is not included.
11
toolkit = dependable(None)
12
# Likewise with `bindgen_config_paths` when
13
# build/moz.configure/bindgen.configure is not included.
14
bindgen_config_paths = dependable(None)
15
16
option(env='DIST', nargs=1, help='DIST directory')
17
18
19
# Do not allow objdir == srcdir builds.
20
# ==============================================================
21
@depends('--help', 'DIST')
22
@imports(_from='__builtin__', _import='open')
23
@imports(_from='os.path', _import='exists')
24
def check_build_environment(help, dist):
25
topobjdir = os.path.realpath(os.path.abspath('.'))
26
topsrcdir = os.path.realpath(os.path.abspath(
27
os.path.join(os.path.dirname(__file__), '..', '..')))
28
29
if dist:
30
dist = normsep(dist[0])
31
else:
32
dist = os.path.join(topobjdir, 'dist')
33
34
result = namespace(
35
topsrcdir=topsrcdir,
36
topobjdir=topobjdir,
37
dist=dist,
38
)
39
40
if help:
41
return result
42
43
# This limitation has mostly to do with GNU make. Since make can't represent
44
# variables with spaces without correct quoting and many paths are used
45
# without proper quoting, using paths with spaces commonly results in
46
# targets or dependencies being treated as multiple paths. This, of course,
47
# undermines the ability for make to perform up-to-date checks and makes
48
# the build system not work very efficiently. In theory, a non-make build
49
# backend will make this limitation go away. But there is likely a long tail
50
# of things that will need fixing due to e.g. lack of proper path quoting.
51
if len(topsrcdir.split()) > 1:
52
die('Source directory cannot be located in a path with spaces: %s' %
53
topsrcdir)
54
if len(topobjdir.split()) > 1:
55
die('Object directory cannot be located in a path with spaces: %s' %
56
topobjdir)
57
58
if topsrcdir == topobjdir:
59
die(' ***\n'
60
' * Building directly in the main source directory is not allowed.\n'
61
' *\n'
62
' * To build, you must run configure from a separate directory\n'
63
' * (referred to as an object directory).\n'
64
' *\n'
65
' * If you are building with a mozconfig, you will need to change your\n'
66
' * mozconfig to point to a different object directory.\n'
67
' ***'
68
)
69
70
# Check for CRLF line endings.
71
with open(os.path.join(topsrcdir, 'configure.py'), 'rb') as fh:
72
data = fh.read()
73
if '\r' in data:
74
die('\n ***\n'
75
' * The source tree appears to have Windows-style line endings.\n'
76
' *\n'
77
' * If using Git, Git is likely configured to use Windows-style\n'
78
' * line endings.\n'
79
' *\n'
80
' * To convert the working copy to UNIX-style line endings, run\n'
81
' * the following:\n'
82
' *\n'
83
' * $ git config core.autocrlf false\n'
84
' * $ git config core.eof lf\n'
85
' * $ git rm --cached -r .\n'
86
' * $ git reset --hard\n'
87
' *\n'
88
' * If not using Git, the tool you used to obtain the source\n'
89
' * code likely converted files to Windows line endings. See\n'
90
' * usage information for that tool for more.\n'
91
' ***')
92
93
# Check for a couple representative files in the source tree
94
conflict_files = [
95
'* %s' % f for f in ('Makefile', 'config/autoconf.mk')
96
if exists(os.path.join(topsrcdir, f))
97
]
98
if conflict_files:
99
die(' ***\n'
100
' * Your source tree contains these files:\n'
101
' %s\n'
102
' * This indicates that you previously built in the source tree.\n'
103
' * A source tree build can confuse the separate objdir build.\n'
104
' *\n'
105
' * To clean up the source tree:\n'
106
' * 1. cd %s\n'
107
' * 2. gmake distclean\n'
108
' ***'
109
% ('\n '.join(conflict_files), topsrcdir)
110
)
111
112
return result
113
114
115
set_config('TOPSRCDIR', check_build_environment.topsrcdir)
116
set_config('TOPOBJDIR', check_build_environment.topobjdir)
117
set_config('MOZ_BUILD_ROOT', check_build_environment.topobjdir)
118
set_config('DIST', check_build_environment.dist)
119
120
add_old_configure_assignment(
121
'_topsrcdir', check_build_environment.topsrcdir)
122
add_old_configure_assignment(
123
'_objdir', check_build_environment.topobjdir)
124
add_old_configure_assignment(
125
'MOZ_BUILD_ROOT', check_build_environment.topobjdir)
126
add_old_configure_assignment(
127
'DIST', check_build_environment.dist)
128
129
option(env='MOZ_AUTOMATION', help='Enable options for automated builds')
130
set_config('MOZ_AUTOMATION', depends_if('MOZ_AUTOMATION')(lambda x: True))
131
132
133
option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script')
134
135
option(env='MOZCONFIG', nargs=1, help='Mozconfig location')
136
137
option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1,
138
help='External directory containing additional build files')
139
140
141
@depends('--with-external-source-dir')
142
def external_source_dir(value):
143
if value:
144
return value[0]
145
146
147
set_config('EXTERNAL_SOURCE_DIR', external_source_dir)
148
add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir)
149
150
# Read user mozconfig
151
# ==============================================================
152
# Note: the dependency on --help is only there to always read the mozconfig,
153
# even when --help is passed. Without this dependency, the function wouldn't
154
# be called when --help is passed, and the mozconfig wouldn't be read.
155
156
157
@depends('MOZCONFIG', 'OLD_CONFIGURE',
158
check_build_environment, '--with-external-source-dir',
159
'--help')
160
@imports(_from='mozbuild.mozconfig', _import='MozconfigLoader')
161
def mozconfig(mozconfig, old_configure, build_env,
162
external_source_dir, help):
163
if not old_configure and not help:
164
die('The OLD_CONFIGURE environment variable must be set')
165
166
# Don't read the mozconfig for the js configure (yay backwards
167
# compatibility)
168
# While the long term goal is that js and top-level use the same configure
169
# and the same overall setup, including the possibility to use mozconfigs,
170
# figuring out what we want to do wrt mozconfig vs. command line and
171
# environment variable is not a clear-cut case, and it's more important to
172
# fix the immediate problem mozconfig causes to js developers by
173
# "temporarily" returning to the previous behavior of not loading the
174
# mozconfig for the js configure.
175
# Separately to the immediate problem for js developers, there is also the
176
# need to not load a mozconfig when running js configure as a subconfigure.
177
# Unfortunately, there is no direct way to tell whether the running
178
# configure is the js configure. The indirect way is to look at the
179
# OLD_CONFIGURE path, which points to js/src/old-configure.
180
# I expect we'll have figured things out for mozconfigs well before
181
# old-configure dies.
182
if old_configure and os.path.dirname(os.path.abspath(old_configure[0])).endswith('/js/src'):
183
return {'path': None}
184
185
topsrcdir = build_env.topsrcdir
186
if external_source_dir:
187
topsrcdir = external_source_dir[0]
188
loader = MozconfigLoader(topsrcdir)
189
mozconfig = mozconfig[0] if mozconfig else None
190
mozconfig = loader.find_mozconfig(env={'MOZCONFIG': mozconfig})
191
mozconfig = loader.read_mozconfig(mozconfig)
192
193
return mozconfig
194
195
196
set_config('MOZCONFIG', depends(mozconfig)(lambda m: m['path']))
197
198
199
option(env='PYTHON', nargs=1, help='Python interpreter')
200
201
# Setup python virtualenv
202
# ==============================================================
203
204
205
@depends('PYTHON', check_build_environment, mozconfig, '--help')
206
@imports('os')
207
@imports('sys')
208
@imports('subprocess')
209
@imports(_from='mozbuild.configure.util', _import='LineIO')
210
@imports(_from='mozbuild.virtualenv', _import='VirtualenvManager')
211
@imports(_from='mozbuild.virtualenv', _import='verify_python_version')
212
@imports('distutils.sysconfig')
213
def virtualenv_python(env_python, build_env, mozconfig, help):
214
if help:
215
return
216
217
python = env_python[0] if env_python else None
218
219
# Ideally we'd rely on the mozconfig injection from mozconfig_options,
220
# but we'd rather avoid the verbosity when we need to reexecute with
221
# a different python.
222
if mozconfig['path']:
223
if 'PYTHON' in mozconfig['env']['added']:
224
python = mozconfig['env']['added']['PYTHON']
225
elif 'PYTHON' in mozconfig['env']['modified']:
226
python = mozconfig['env']['modified']['PYTHON'][1]
227
elif 'PYTHON' in mozconfig['vars']['added']:
228
python = mozconfig['vars']['added']['PYTHON']
229
elif 'PYTHON' in mozconfig['vars']['modified']:
230
python = mozconfig['vars']['modified']['PYTHON'][1]
231
232
with LineIO(lambda l: log.error(l)) as out:
233
verify_python_version(out)
234
topsrcdir, topobjdir = build_env.topsrcdir, build_env.topobjdir
235
if topobjdir.endswith('/js/src'):
236
topobjdir = topobjdir[:-7]
237
238
virtualenvs_root = os.path.join(topobjdir, '_virtualenvs')
239
with LineIO(lambda l: log.info(l), 'replace') as out:
240
manager = VirtualenvManager(
241
topsrcdir, topobjdir,
242
os.path.join(virtualenvs_root, 'init'), out,
243
os.path.join(topsrcdir, 'build', 'virtualenv_packages.txt'))
244
245
if python:
246
# If we're not in the virtualenv, we need the which module for
247
# find_program.
248
if normsep(sys.executable) != normsep(manager.python_path):
249
sys.path.append(os.path.join(
250
topsrcdir, 'third_party', 'python', 'which'))
251
found_python = find_program(python)
252
if not found_python:
253
die('The PYTHON environment variable does not contain '
254
'a valid path. Cannot find %s', python)
255
python = found_python
256
else:
257
python = sys.executable
258
259
if not manager.up_to_date(python):
260
log.info('Creating Python environment')
261
manager.build(python)
262
263
python = normsep(manager.python_path)
264
265
if not normsep(sys.executable).startswith(normsep(virtualenvs_root)):
266
log.info('Reexecuting in the virtualenv')
267
if env_python:
268
del os.environ['PYTHON']
269
# One would prefer to use os.execl, but that's completely borked on
270
# Windows.
271
sys.exit(subprocess.call([python] + sys.argv))
272
273
# We are now in the virtualenv
274
if not distutils.sysconfig.get_python_lib():
275
die('Could not determine python site packages directory')
276
277
return python
278
279
280
set_config('PYTHON', virtualenv_python)
281
add_old_configure_assignment('PYTHON', virtualenv_python)
282
283
# Inject mozconfig options
284
# ==============================================================
285
# All options defined above this point can't be injected in mozconfig_options
286
# below, so collect them.
287
288
289
@template
290
def early_options():
291
@dependable
292
@imports('__sandbox__')
293
def early_options():
294
return set(
295
option.env
296
for option in __sandbox__._options.itervalues()
297
if option.env
298
)
299
return early_options
300
301
302
early_options = early_options()
303
304
305
@depends(mozconfig, 'MOZ_AUTOMATION', '--help')
306
# This gives access to the sandbox. Don't copy this blindly.
307
@imports('__sandbox__')
308
@imports('os')
309
@imports('six')
310
def mozconfig_options(mozconfig, automation, help):
311
if mozconfig['path']:
312
if 'MOZ_AUTOMATION_MOZCONFIG' in mozconfig['env']['added']:
313
if not automation:
314
log.error('%s directly or indirectly includes an in-tree '
315
'mozconfig.', mozconfig['path'])
316
log.error('In-tree mozconfigs make strong assumptions about '
317
'and are only meant to be used by Mozilla '
318
'automation.')
319
die("Please don't use them.")
320
helper = __sandbox__._helper
321
log.info('Adding configure options from %s' % mozconfig['path'])
322
for arg in mozconfig['configure_args']:
323
log.info(' %s' % arg)
324
# We could be using imply_option() here, but it has other
325
# contraints that don't really apply to the command-line
326
# emulation that mozconfig provides.
327
helper.add(arg, origin='mozconfig', args=helper._args)
328
329
def add(key, value):
330
if key.isupper():
331
arg = '%s=%s' % (key, value)
332
log.info(' %s' % arg)
333
helper.add(arg, origin='mozconfig', args=helper._args)
334
335
for key, value in six.iteritems(mozconfig['env']['added']):
336
add(key, value)
337
os.environ[key] = value
338
for key, (_, value) in six.iteritems(mozconfig['env']['modified']):
339
add(key, value)
340
os.environ[key] = value
341
for key, value in six.iteritems(mozconfig['vars']['added']):
342
add(key, value)
343
for key, (_, value) in six.iteritems(mozconfig['vars']['modified']):
344
add(key, value)
345
346
347
# Mozilla-Build
348
# ==============================================================
349
option(env='MOZILLABUILD', nargs=1,
350
help='Path to Mozilla Build (Windows-only)')
351
352
option(env='CONFIG_SHELL', nargs=1, help='Path to a POSIX shell')
353
354
# It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py,
355
# but the end goal being that the configure script would go away...
356
357
358
@depends('CONFIG_SHELL', 'MOZILLABUILD')
359
@checking('for a shell')
360
@imports('sys')
361
def shell(value, mozillabuild):
362
if value:
363
return find_program(value[0])
364
shell = 'sh'
365
if mozillabuild:
366
shell = mozillabuild[0] + '/msys/bin/sh'
367
if sys.platform == 'win32':
368
shell = shell + '.exe'
369
return find_program(shell)
370
371
372
# This defines a reasonable shell for when running with --help.
373
# If one was passed in the environment, though, fall back to that.
374
@depends('--help', 'CONFIG_SHELL')
375
def help_shell(help, shell):
376
if help and not shell:
377
return 'sh'
378
379
380
shell = help_shell | shell
381
382
383
# Python 3
384
# ========
385
386
option(env='PYTHON3', nargs=1, help='Python 3 interpreter (3.5 or later)')
387
388
389
@depends('PYTHON3', 'MOZILLABUILD')
390
@checking('for Python 3',
391
callback=lambda x: '%s (%s)' % (x.path, x.str_version) if x else 'no')
392
@imports(_from='__builtin__', _import='Exception')
393
@imports(_from='mozbuild.pythonutil', _import='find_python3_executable')
394
@imports(_from='mozbuild.pythonutil', _import='python_executable_version')
395
def python3(env_python, mozillabuild):
396
python = env_python[0] if env_python else None
397
398
# If Python given by environment variable, it must work.
399
if python:
400
try:
401
version = python_executable_version(python).version
402
except Exception as e:
403
raise FatalCheckError('could not determine version of PYTHON '
404
'(%s): %s' % (python, e))
405
elif mozillabuild:
406
# MozillaBuild provides a Python 3.
407
python = normsep('%s/python3/python3.exe' % mozillabuild)
408
409
try:
410
version = python_executable_version(python).version
411
except Exception as e:
412
raise FatalCheckError('could not determine version of '
413
'MozillaBuild python: %s' % e)
414
else:
415
# Fall back to the search routine.
416
python, version = find_python3_executable(min_version='3.5.0')
417
418
# The API returns a bytes whereas everything in configure is unicode.
419
if python:
420
python = python.decode('utf-8')
421
422
if not python:
423
raise FatalCheckError('Python 3.5 or newer is required to build. '
424
'Ensure a `python3.x` executable is in your '
425
'PATH or define PYTHON3 to point to a Python '
426
'3.5 executable.')
427
428
if version < (3, 5, 0):
429
raise FatalCheckError('Python 3.5 or newer is required to build; '
430
'%s is Python %d.%d' % (python, version[0],
431
version[1]))
432
433
return namespace(
434
path=python,
435
version=version,
436
str_version='.'.join(str(v) for v in version),
437
)
438
439
440
set_config('PYTHON3', depends_if(python3)(lambda p: p.path))
441
set_config('PYTHON3_VERSION', depends_if(python3)(lambda p: p.str_version))
442
443
# Source checkout and version control integration.
444
# ================================================
445
446
447
@depends(check_build_environment, 'MOZ_AUTOMATION', '--help')
448
@checking('for vcs source checkout')
449
@imports('os')
450
def vcs_checkout_type(build_env, automation, help):
451
if os.path.exists(os.path.join(build_env.topsrcdir, '.hg')):
452
return 'hg'
453
elif os.path.exists(os.path.join(build_env.topsrcdir, '.git')):
454
return 'git'
455
elif automation and not help:
456
raise FatalCheckError('unable to resolve VCS type; must run '
457
'from a source checkout when MOZ_AUTOMATION '
458
'is set')
459
460
# Resolve VCS binary for detected repository type.
461
462
463
# TODO remove hg.exe once bug 1382940 addresses ambiguous executables case.
464
hg = check_prog('HG', ('hg.exe', 'hg',), allow_missing=True,
465
when=depends(vcs_checkout_type)(lambda x: x == 'hg'))
466
git = check_prog('GIT', ('git',), allow_missing=True,
467
when=depends(vcs_checkout_type)(lambda x: x == 'git'))
468
469
470
@depends_if(hg)
471
@checking('for Mercurial version')
472
@imports('os')
473
@imports('re')
474
def hg_version(hg):
475
# HGPLAIN in Mercurial 1.5+ forces stable output, regardless of set
476
# locale or encoding.
477
env = dict(os.environ)
478
env['HGPLAIN'] = '1'
479
480
out = check_cmd_output(hg, '--version', env=env)
481
482
match = re.search(r'Mercurial Distributed SCM \(version ([^\)]+)', out)
483
484
if not match:
485
raise FatalCheckError(
486
'unable to determine Mercurial version: %s' % out)
487
488
# The version string may be "unknown" for Mercurial run out of its own
489
# source checkout or for bad builds. But LooseVersion handles it.
490
491
return Version(match.group(1))
492
493
# Resolve Mercurial config items so other checks have easy access.
494
# Do NOT set this in the config because it may contain sensitive data
495
# like API keys.
496
497
498
@depends_all(check_build_environment, hg, hg_version)
499
@imports('os')
500
def hg_config(build_env, hg, version):
501
env = dict(os.environ)
502
env['HGPLAIN'] = '1'
503
504
# Warnings may get sent to stderr. But check_cmd_output() ignores
505
# stderr if exit code is 0. And the command should always succeed if
506
# `hg version` worked.
507
out = check_cmd_output(hg, 'config', env=env, cwd=build_env.topsrcdir)
508
509
# out is bytes. However, unicode literals are in effect, so implicit
510
# type coercion can occur. The underlying Mercurial config file may be
511
# in a user-defined encoding. However, HGPLAIN both overrides the decoding
512
# inside Mercurial *and* ensures output is utf-8. Because moz.configure
513
# is using unicode literals, our returned config object uses unicode
514
# keys and values to limit potential for coercion.
515
516
# Mercurial should emit utf-8. But be paranoid and ignore invalid utf-8
517
# byte sequences.
518
out = out.decode('utf-8', 'replace')
519
520
config = {}
521
522
for line in out.strip().splitlines():
523
key, value = [s.strip() for s in line.split('=', 1)]
524
config[key] = value
525
526
return config
527
528
529
@depends_if(git)
530
@checking('for Git version')
531
@imports('re')
532
def git_version(git):
533
out = check_cmd_output(git, '--version').rstrip()
534
535
match = re.search('git version (.*)$', out)
536
537
if not match:
538
raise FatalCheckError('unable to determine Git version: %s' % out)
539
540
return Version(match.group(1))
541
542
# Only set VCS_CHECKOUT_TYPE if we resolved the VCS binary.
543
# Require resolved VCS info when running in automation so automation's
544
# environment is more well-defined.
545
546
547
@depends(vcs_checkout_type, hg_version, git_version, 'MOZ_AUTOMATION')
548
def exposed_vcs_checkout_type(vcs_checkout_type, hg, git, automation):
549
if vcs_checkout_type == 'hg':
550
if hg:
551
return 'hg'
552
553
if automation:
554
raise FatalCheckError('could not resolve Mercurial binary info')
555
556
elif vcs_checkout_type == 'git':
557
if git:
558
return 'git'
559
560
if automation:
561
raise FatalCheckError('could not resolve Git binary info')
562
elif vcs_checkout_type:
563
raise FatalCheckError('unhandled VCS type: %s' % vcs_checkout_type)
564
565
566
set_config('VCS_CHECKOUT_TYPE', exposed_vcs_checkout_type)
567
568
# Obtain a Repository interface for the current VCS repository.
569
570
571
@depends(check_build_environment, exposed_vcs_checkout_type, hg, git)
572
@imports(_from='mozversioncontrol', _import='get_repository_object')
573
def vcs_repository(build_env, vcs_checkout_type, hg, git):
574
if vcs_checkout_type == 'hg':
575
return get_repository_object(build_env.topsrcdir, hg=hg)
576
elif vcs_checkout_type == 'git':
577
return get_repository_object(build_env.topsrcdir, git=git)
578
elif vcs_checkout_type:
579
raise FatalCheckError('unhandled VCS type: %s' % vcs_checkout_type)
580
581
582
@depends_if(vcs_repository)
583
@checking('for sparse checkout')
584
def vcs_sparse_checkout(repo):
585
return repo.sparse_checkout_present()
586
587
588
set_config('VCS_SPARSE_CHECKOUT', vcs_sparse_checkout)
589
590
# The application/project to build
591
# ==============================================================
592
option('--enable-application', nargs=1, env='MOZ_BUILD_APP',
593
help='Application to build. Same as --enable-project.')
594
595
596
@depends('--enable-application')
597
def application(app):
598
if app:
599
return app
600
601
602
imply_option('--enable-project', application)
603
604
605
@depends(check_build_environment)
606
def default_project(build_env):
607
if build_env.topobjdir.endswith('/js/src'):
608
return 'js'
609
return 'browser'
610
611
612
option('--enable-project', nargs=1, default=default_project,
613
help='Project to build')
614
615
616
# Host and target systems
617
# ==============================================================
618
option('--host', nargs=1, help='Define the system type performing the build')
619
620
option('--target', nargs=1,
621
help='Define the system type where the resulting executables will be '
622
'used')
623
624
625
@imports(_from='mozbuild.configure.constants', _import='CPU')
626
@imports(_from='mozbuild.configure.constants', _import='CPU_bitness')
627
@imports(_from='mozbuild.configure.constants', _import='Endianness')
628
@imports(_from='mozbuild.configure.constants', _import='Kernel')
629
@imports(_from='mozbuild.configure.constants', _import='OS')
630
@imports(_from='__builtin__', _import='KeyError')
631
@imports(_from='__builtin__', _import='ValueError')
632
def split_triplet(triplet, allow_unknown=False):
633
# The standard triplet is defined as
634
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
635
# There is also a quartet form:
636
# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
637
# But we can consider the "KERNEL-OPERATING_SYSTEM" as one.
638
# Additionally, some may omit "unknown" when the manufacturer
639
# is not specified and emit
640
# CPU_TYPE-OPERATING_SYSTEM
641
parts = triplet.split('-', 2)
642
if len(parts) == 3:
643
cpu, _, os = parts
644
elif len(parts) == 2:
645
cpu, os = parts
646
else:
647
raise ValueError("Unexpected triplet string: %s" % triplet)
648
649
# Autoconf uses config.sub to validate and canonicalize those triplets,
650
# but the granularity of its results has never been satisfying to our
651
# use, so we've had our own, different, canonicalization. We've also
652
# historically not been very consistent with how we use the canonicalized
653
# values. Hopefully, this will help us make things better.
654
# The tests are inherited from our decades-old autoconf-based configure,
655
# which can probably be improved/cleaned up because they are based on a
656
# mix of uname and config.guess output, while we now only use the latter,
657
# which presumably has a cleaner and leaner output. Let's refine later.
658
os = os.replace('/', '_')
659
if 'android' in os:
660
canonical_os = 'Android'
661
canonical_kernel = 'Linux'
662
elif os.startswith('linux'):
663
canonical_os = 'GNU'
664
canonical_kernel = 'Linux'
665
elif os.startswith('kfreebsd') and os.endswith('-gnu'):
666
canonical_os = 'GNU'
667
canonical_kernel = 'kFreeBSD'
668
elif os.startswith('gnu'):
669
canonical_os = canonical_kernel = 'GNU'
670
elif os.startswith('mingw'):
671
canonical_os = canonical_kernel = 'WINNT'
672
elif os.startswith('darwin'):
673
canonical_kernel = 'Darwin'
674
canonical_os = 'OSX'
675
elif os.startswith('ios'):
676
canonical_kernel = 'Darwin'
677
canonical_os = 'iOS'
678
elif os.startswith('dragonfly'):
679
canonical_os = canonical_kernel = 'DragonFly'
680
elif os.startswith('freebsd'):
681
canonical_os = canonical_kernel = 'FreeBSD'
682
elif os.startswith('netbsd'):
683
canonical_os = canonical_kernel = 'NetBSD'
684
elif os.startswith('openbsd'):
685
canonical_os = canonical_kernel = 'OpenBSD'
686
elif os.startswith('solaris'):
687
canonical_os = canonical_kernel = 'SunOS'
688
elif allow_unknown:
689
canonical_os = canonical_kernel = os
690
else:
691
raise ValueError('Unknown OS: %s' % os)
692
693
# The CPU granularity is probably not enough. Moving more things from
694
# old-configure will tell us if we need more
695
if cpu.endswith('86') or (cpu.startswith('i') and '86' in cpu):
696
canonical_cpu = 'x86'
697
endianness = 'little'
698
elif cpu in ('x86_64', 'ia64'):
699
canonical_cpu = cpu
700
endianness = 'little'
701
elif cpu in ('s390', 's390x'):
702
canonical_cpu = cpu
703
endianness = 'big'
704
elif cpu in ('powerpc64', 'ppc64', 'powerpc64le', 'ppc64le'):
705
canonical_cpu = 'ppc64'
706
endianness = 'little' if 'le' in cpu else 'big'
707
elif cpu in ('powerpc', 'ppc', 'rs6000') or cpu.startswith('powerpc'):
708
canonical_cpu = 'ppc'
709
endianness = 'big'
710
elif cpu in ('Alpha', 'alpha', 'ALPHA'):
711
canonical_cpu = 'Alpha'
712
endianness = 'little'
713
elif cpu.startswith('hppa') or cpu == 'parisc':
714
canonical_cpu = 'hppa'
715
endianness = 'big'
716
elif cpu.startswith('sparc64') or cpu.startswith('sparcv9'):
717
canonical_cpu = 'sparc64'
718
endianness = 'big'
719
elif cpu.startswith('sparc') or cpu == 'sun4u':
720
canonical_cpu = 'sparc'
721
endianness = 'big'
722
elif cpu.startswith('arm'):
723
canonical_cpu = 'arm'
724
endianness = 'big' if cpu.startswith(('armeb', 'armbe')) else 'little'
725
elif cpu in ('mips', 'mipsel'):
726
canonical_cpu = 'mips32'
727
endianness = 'little' if 'el' in cpu else 'big'
728
elif cpu in ('mips64', 'mips64el'):
729
canonical_cpu = 'mips64'
730
endianness = 'little' if 'el' in cpu else 'big'
731
elif cpu.startswith('aarch64'):
732
canonical_cpu = 'aarch64'
733
endianness = 'little'
734
elif cpu == 'sh4':
735
canonical_cpu = 'sh4'
736
endianness = 'little'
737
elif allow_unknown:
738
canonical_cpu = cpu
739
endianness = 'unknown'
740
else:
741
raise ValueError('Unknown CPU type: %s' % cpu)
742
743
def sanitize(cls, value):
744
try:
745
return cls(value)
746
except (KeyError, ValueError):
747
if allow_unknown:
748
return value
749
raise
750
751
def bitness(cpu):
752
return CPU_bitness[cpu]
753
754
# Toolchains, most notably for cross compilation may use cpu-os
755
# prefixes. We need to be more specific about the LLVM target on Mac
756
# so cross-language LTO will work correctly.
757
758
if os.startswith('darwin'):
759
toolchain = '%s-apple-%s' % (cpu, os)
760
elif canonical_cpu == 'aarch64' and canonical_os == 'WINNT':
761
toolchain = 'aarch64-windows-msvc'
762
else:
763
toolchain = '%s-%s' % (cpu, os)
764
765
return namespace(
766
alias=triplet,
767
cpu=sanitize(CPU, canonical_cpu),
768
bitness=sanitize(bitness, canonical_cpu),
769
kernel=sanitize(Kernel, canonical_kernel),
770
os=sanitize(OS, canonical_os),
771
endianness=sanitize(Endianness, endianness),
772
raw_cpu=cpu,
773
raw_os=os,
774
toolchain=toolchain,
775
)
776
777
778
# This defines a fake target/host namespace for when running with --help
779
# If either --host or --target is passed on the command line, then fall
780
# back to the real deal.
781
@depends('--help', '--host', '--target')
782
def help_host_target(help, host, target):
783
if help and not host and not target:
784
return namespace(
785
alias='unknown-unknown-unknown',
786
cpu='unknown',
787
bitness='unknown',
788
kernel='unknown',
789
os='unknown',
790
endianness='unknown',
791
raw_cpu='unknown',
792
raw_os='unknown',
793
toolchain='unknown-unknown',
794
)
795
796
797
@imports('subprocess')
798
def config_sub(shell, triplet):
799
config_sub = os.path.join(os.path.dirname(__file__), '..',
800
'autoconf', 'config.sub')
801
return subprocess.check_output([shell, config_sub, triplet]).strip()
802
803
804
@depends('--host', shell)
805
@checking('for host system type', lambda h: h.alias)
806
@imports('os')
807
@imports('subprocess')
808
@imports('sys')
809
@imports(_from='__builtin__', _import='ValueError')
810
def real_host(value, shell):
811
if not value and sys.platform == 'win32':
812
arch = (os.environ.get('PROCESSOR_ARCHITEW6432') or
813
os.environ.get('PROCESSOR_ARCHITECTURE'))
814
if arch == 'AMD64':
815
return split_triplet('x86_64-pc-mingw32')
816
elif arch == 'x86':
817
return split_triplet('i686-pc-mingw32')
818
819
if not value:
820
config_guess = os.path.join(os.path.dirname(__file__), '..',
821
'autoconf', 'config.guess')
822
host = subprocess.check_output([shell, config_guess]).strip()
823
try:
824
return split_triplet(host)
825
except ValueError:
826
pass
827
else:
828
host = value[0]
829
830
host = config_sub(shell, host)
831
832
try:
833
return split_triplet(host)
834
except ValueError as e:
835
die(e.message)
836
837
838
host = help_host_target | real_host
839
840
841
@depends('--target', real_host, shell, '--enable-project', '--enable-application')
842
@checking('for target system type', lambda t: t.alias)
843
@imports(_from='__builtin__', _import='ValueError')
844
def real_target(value, host, shell, project, application):
845
# Because --enable-project is implied by --enable-application, and
846
# implied options are not currently handled during --help, which is
847
# used get the build target in mozbuild.base, we manually check
848
# whether --enable-application was given, and fall back to
849
# --enable-project if not. Both can't be given contradictory values
850
# under normal circumstances, so it's fine.
851
if application:
852
project = application[0]
853
elif project:
854
project = project[0]
855
if not value:
856
if project == 'mobile/android':
857
return split_triplet('arm-unknown-linux-androideabi')
858
return host
859
# If --target was only given a cpu arch, expand it with the
860
# non-cpu part of the host. For mobile/android, expand it with
861
# unknown-linux-android.
862
target = value[0]
863
if '-' not in target:
864
if project == 'mobile/android':
865
rest = 'unknown-linux-android'
866
if target.startswith('arm'):
867
rest += 'eabi'
868
else:
869
cpu, rest = host.alias.split('-', 1)
870
target = '-'.join((target, rest))
871
try:
872
return split_triplet(target)
873
except ValueError:
874
pass
875
876
try:
877
return split_triplet(config_sub(shell, target))
878
except ValueError as e:
879
die(e.message)
880
881
882
target = help_host_target | real_target
883
884
885
@depends(host, target)
886
@checking('whether cross compiling')
887
def cross_compiling(host, target):
888
return host != target
889
890
891
set_config('CROSS_COMPILE', cross_compiling)
892
set_define('CROSS_COMPILE', cross_compiling)
893
add_old_configure_assignment('CROSS_COMPILE', cross_compiling)
894
895
896
@depends(target)
897
def have_64_bit(target):
898
if target.bitness == 64:
899
return True
900
901
902
set_config('HAVE_64BIT_BUILD', have_64_bit)
903
set_define('HAVE_64BIT_BUILD', have_64_bit)
904
add_old_configure_assignment('HAVE_64BIT_BUILD', have_64_bit)
905
906
907
@depends(host)
908
def host_os_kernel_major_version(host):
909
versions = host.raw_os.split('.')
910
version = ''.join(x for x in versions[0] if x.isdigit())
911
return version
912
913
914
set_config('HOST_MAJOR_VERSION', host_os_kernel_major_version)
915
916
# Autoconf needs these set
917
918
919
@depends(host)
920
def host_for_old_configure(host):
921
return '--host=%s' % host.alias
922
923
924
add_old_configure_arg(host_for_old_configure)
925
926
927
@depends(target)
928
def target_for_old_configure(target):
929
target_alias = target.alias
930
# old-configure does plenty of tests against $target and $target_os
931
# and expects darwin for iOS, so make it happy.
932
if target.os == 'iOS':
933
target_alias = target_alias.replace('-ios', '-darwin')
934
return '--target=%s' % target_alias
935
936
937
add_old_configure_arg(target_for_old_configure)
938
939
940
# These variables are for compatibility with the current moz.builds and
941
# old-configure. Eventually, we'll want to canonicalize better.
942
@depends(target)
943
def target_variables(target):
944
if target.kernel == 'kFreeBSD':
945
os_target = 'GNU/kFreeBSD'
946
os_arch = 'GNU_kFreeBSD'
947
elif target.kernel == 'Darwin' or (target.kernel == 'Linux' and
948
target.os == 'GNU'):
949
os_target = target.kernel
950
os_arch = target.kernel
951
else:
952
os_target = target.os
953
os_arch = target.kernel
954
955
return namespace(
956
OS_TARGET=os_target,
957
OS_ARCH=os_arch,
958
INTEL_ARCHITECTURE=target.cpu in ('x86', 'x86_64') or None,
959
)
960
961
962
set_config('OS_TARGET', target_variables.OS_TARGET)
963
add_old_configure_assignment('OS_TARGET',
964
target_variables.OS_TARGET)
965
set_config('OS_ARCH', target_variables.OS_ARCH)
966
add_old_configure_assignment('OS_ARCH',
967
target_variables.OS_ARCH)
968
set_config('CPU_ARCH', target.cpu)
969
add_old_configure_assignment('CPU_ARCH', target.cpu)
970
set_config('INTEL_ARCHITECTURE', target_variables.INTEL_ARCHITECTURE)
971
set_config('TARGET_CPU', target.raw_cpu)
972
set_config('TARGET_OS', target.raw_os)
973
set_config('TARGET_ENDIANNESS', target.endianness)
974
975
976
@depends(host)
977
def host_variables(host):
978
if host.kernel == 'kFreeBSD':
979
os_arch = 'GNU_kFreeBSD'
980
else:
981
os_arch = host.kernel
982
return namespace(
983
HOST_OS_ARCH=os_arch,
984
)
985
986
987
set_config('HOST_CPU_ARCH', host.cpu)
988
set_config('HOST_OS_ARCH', host_variables.HOST_OS_ARCH)
989
add_old_configure_assignment('HOST_OS_ARCH',
990
host_variables.HOST_OS_ARCH)
991
992
993
@depends(target)
994
def target_is_windows(target):
995
if target.kernel == 'WINNT':
996
return True
997
998
999
set_define('_WINDOWS', target_is_windows)
1000
set_define('WIN32', target_is_windows)
1001
set_define('XP_WIN', target_is_windows)
1002
1003
1004
@depends(target)
1005
def target_is_unix(target):
1006
if target.kernel != 'WINNT':
1007
return True
1008
1009
1010
set_define('XP_UNIX', target_is_unix)
1011
1012
1013
@depends(target)
1014
def target_is_darwin(target):
1015
if target.kernel == 'Darwin':
1016
return True
1017
1018
1019
set_define('XP_DARWIN', target_is_darwin)
1020
1021
1022
@depends(target)
1023
def target_is_ios(target):
1024
if target.kernel == 'Darwin' and target.os == 'iOS':
1025
return True
1026
1027
1028
set_define('XP_IOS', target_is_ios)
1029
1030
1031
@depends(target)
1032
def target_is_osx(target):
1033
if target.kernel == 'Darwin' and target.os == 'OSX':
1034
return True
1035
1036
1037
set_define('XP_MACOSX', target_is_osx)
1038
1039
1040
@depends(target)
1041
def target_is_linux(target):
1042
if target.kernel == 'Linux':
1043
return True
1044
1045
1046
set_define('XP_LINUX', target_is_linux)
1047
1048
1049
@depends(target)
1050
def target_is_openbsd(target):
1051
if target.kernel == 'OpenBSD':
1052
return True
1053
1054
1055
set_define('XP_OPENBSD', target_is_openbsd)
1056
1057
@depends(target)
1058
def target_is_netbsd(target):
1059
if target.kernel == 'NetBSD':
1060
return True
1061
1062
1063
set_define('XP_NETBSD', target_is_netbsd)
1064
1065
@depends(target)
1066
def target_is_freebsd(target):
1067
if target.kernel == 'FreeBSD':
1068
return True
1069
1070
1071
set_define('XP_FREEBSD', target_is_freebsd)
1072
1073
@depends(target)
1074
def target_is_solaris(target):
1075
if target.kernel == 'SunOS':
1076
return True
1077
1078
1079
set_define('XP_SOLARIS', target_is_solaris)
1080
1081
1082
@depends(target)
1083
def target_is_sparc(target):
1084
if target.cpu == 'sparc64':
1085
return True
1086
1087
set_define('SPARC64', target_is_sparc)
1088
1089
1090
@depends('--enable-project', '--with-external-source-dir',
1091
check_build_environment, '--help')
1092
@imports(_from='os.path', _import='exists')
1093
def include_project_configure(project, external_source_dir, build_env, help):
1094
if not project:
1095
die('--enable-project is required.')
1096
1097
base_dir = build_env.topsrcdir
1098
if external_source_dir:
1099
base_dir = os.path.join(base_dir, external_source_dir[0])
1100
1101
path = os.path.join(base_dir, project[0], 'moz.configure')
1102
if not exists(path):
1103
die('Cannot find project %s', project[0])
1104
return path
1105
1106
1107
@depends(include_project_configure, check_build_environment)
1108
def build_project(include_project_configure, build_env):
1109
ret = os.path.dirname(os.path.relpath(include_project_configure,
1110
build_env.topsrcdir))
1111
return ret
1112
1113
1114
set_config('MOZ_BUILD_APP', build_project)
1115
set_define('MOZ_BUILD_APP', build_project)
1116
add_old_configure_assignment('MOZ_BUILD_APP', build_project)
1117
1118
1119
# This is temporary until js/src/configure and configure are merged.
1120
# Use instead of option() in js/moz.configure and more generally, for
1121
# options that are shared between configure and js/src/configure.
1122
@template
1123
def js_option(*args, **kwargs):
1124
opt = option(*args, **kwargs)
1125
1126
@depends(opt.option, build_project, when=kwargs.get('when'))
1127
def js_option(value, build_project):
1128
if build_project != 'js':
1129
return value.format(opt.option)
1130
1131
add_old_configure_arg(js_option)
1132
1133
1134
js_option(env='MOZILLA_OFFICIAL',
1135
help='Build an official release')
1136
1137
1138
@depends('MOZILLA_OFFICIAL')
1139
def mozilla_official(official):
1140
if official:
1141
return True
1142
1143
1144
set_config('MOZILLA_OFFICIAL', mozilla_official)
1145
set_define('MOZILLA_OFFICIAL', mozilla_official)
1146
add_old_configure_assignment('MOZILLA_OFFICIAL', mozilla_official)
1147
1148
1149
# Allow specifying custom paths to the version files used by the milestone() function below.
1150
option('--with-version-file-path',
1151
nargs=1,
1152
help='Specify a custom path to app version files instead of auto-detecting',
1153
default=None)
1154
1155
@depends('--with-version-file-path')
1156
def version_path(path):
1157
return path
1158
1159
# set RELEASE_OR_BETA and NIGHTLY_BUILD variables depending on the cycle we're in
1160
# The logic works like this:
1161
# - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD)
1162
# - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora
1163
# - otherwise, we're building Release/Beta (define RELEASE_OR_BETA)
1164
@depends(check_build_environment, build_project, version_path, '--help')
1165
@imports(_from='__builtin__', _import='open')
1166
@imports('os')
1167
@imports('re')
1168
def milestone(build_env, build_project, version_path, _):
1169
versions = []
1170
paths = ['config/milestone.txt']
1171
if build_project == 'js':
1172
paths = paths * 3
1173
else:
1174
paths += [
1175
'browser/config/version.txt',
1176
'browser/config/version_display.txt',
1177
]
1178
if version_path:
1179
version_path = version_path[0]
1180
else:
1181
version_path = os.path.join(build_project, 'config')
1182
for f in ('version.txt', 'version_display.txt'):
1183
f = os.path.join(version_path, f)
1184
if not os.path.exists(os.path.join(build_env.topsrcdir, f)):
1185
break
1186
paths.append(f)
1187
1188
for p in paths:
1189
with open(os.path.join(build_env.topsrcdir, p), 'r') as fh:
1190
content = fh.read().splitlines()
1191
if not content:
1192
die('Could not find a version number in {}'.format(p))
1193
versions.append(content[-1])
1194
1195
milestone, firefox_version, firefox_version_display = versions[:3]
1196
1197
# version.txt content from the project directory if there is one, otherwise
1198
# the firefox version.
1199
app_version = versions[3] if len(versions) > 3 else firefox_version
1200
# version_display.txt content from the project directory if there is one,
1201
# otherwise version.txt content from the project directory, otherwise the
1202
# firefox version for display.
1203
app_version_display = versions[-1] if len(versions) > 3 else firefox_version_display
1204
1205
is_nightly = is_release_or_beta = None
1206
1207
if 'a1' in milestone:
1208
is_nightly = True
1209
elif 'a' not in milestone:
1210
is_release_or_beta = True
1211
1212
major_version = milestone.split('.')[0]
1213
m = re.search(r"([ab]\d+)", milestone)
1214
ab_patch = m.group(1) if m else ''
1215
1216
# Only expose the major version milestone in the UA string and hide the
1217
# patch leve (bugs 572659 and 870868).
1218
#
1219
# Only expose major milestone and alpha version in the symbolversion
1220
# string; as the name suggests, we use it for symbol versioning on Linux.
1221
return namespace(version=milestone,
1222
uaversion='%s.0' % major_version,
1223
symbolversion='%s%s' % (major_version, ab_patch),
1224
is_nightly=is_nightly,
1225
is_release_or_beta=is_release_or_beta,
1226
app_version=app_version,
1227
app_version_display=app_version_display)
1228
1229
1230
set_config('GRE_MILESTONE', milestone.version)
1231
set_config('NIGHTLY_BUILD', milestone.is_nightly)
1232
set_define('NIGHTLY_BUILD', milestone.is_nightly)
1233
set_config('RELEASE_OR_BETA', milestone.is_release_or_beta)
1234
set_define('RELEASE_OR_BETA', milestone.is_release_or_beta)
1235
add_old_configure_assignment('RELEASE_OR_BETA',
1236
milestone.is_release_or_beta)
1237
set_define('MOZILLA_VERSION', depends(milestone)(lambda m: '"%s"' % m.version))
1238
set_config('MOZILLA_VERSION', milestone.version)
1239
set_define('MOZILLA_VERSION_U', milestone.version)
1240
set_define('MOZILLA_UAVERSION', depends(milestone)(lambda m: '"%s"' % m.uaversion))
1241
set_config('MOZILLA_SYMBOLVERSION', milestone.symbolversion)
1242
# JS configure still wants to look at these.
1243
add_old_configure_assignment('MOZILLA_VERSION', milestone.version)
1244
add_old_configure_assignment('MOZILLA_SYMBOLVERSION', milestone.symbolversion)
1245
1246
set_config('MOZ_APP_VERSION', milestone.app_version)
1247
set_config('MOZ_APP_VERSION_DISPLAY', milestone.app_version_display)
1248
add_old_configure_assignment('MOZ_APP_VERSION', milestone.app_version)
1249
1250
1251
# Dummy function for availability in toolkit/moz.configure. Overridden in
1252
# mobile/android/moz.configure.
1253
@depends(milestone.is_nightly)
1254
def fennec_nightly(is_nightly):
1255
return is_nightly
1256
1257
1258
# The app update channel is 'default' when not supplied. The value is used in
1259
# the application's confvars.sh (and is made available to a project specific
1260
# moz.configure).
1261
option('--enable-update-channel',
1262
nargs=1,
1263
help='Select application update channel',
1264
default='default')
1265
1266
1267
@depends('--enable-update-channel')
1268
def update_channel(channel):
1269
if channel[0] == '':
1270
return 'default'
1271
return channel[0].lower()
1272
1273
1274
set_config('MOZ_UPDATE_CHANNEL', update_channel)
1275
set_define('MOZ_UPDATE_CHANNEL', update_channel)
1276
add_old_configure_assignment('MOZ_UPDATE_CHANNEL', update_channel)
1277
1278
1279
js_option(env='MOZBUILD_STATE_PATH', nargs=1,
1280
help='Path to a persistent state directory for the build system '
1281
'and related tools')
1282
1283
1284
@depends('MOZBUILD_STATE_PATH', '--help')
1285
@imports('os')
1286
def mozbuild_state_path(path, _):
1287
if path:
1288
return path[0]
1289
return os.path.expanduser(os.path.join('~', '.mozbuild'))
1290
1291
1292
# A template providing a shorthand for setting a variable. The created
1293
# option will only be settable with imply_option.
1294
# It is expected that a project-specific moz.configure will call imply_option
1295
# to set a value other than the default.
1296
# If required, the set_as_define and set_for_old_configure arguments
1297
# will additionally cause the variable to be set using set_define and
1298
# add_old_configure_assignment. util.configure would be an appropriate place for
1299
# this, but it uses add_old_configure_assignment, which is defined in this file.
1300
@template
1301
def project_flag(env=None, set_for_old_configure=False,
1302
set_as_define=False, **kwargs):
1303
1304
if not env:
1305
configure_error(
1306
"A project_flag must be passed a variable name to set.")
1307
1308
opt = option(env=env, possible_origins=('implied',), **kwargs)
1309
1310
@depends(opt.option)
1311
def option_implementation(value):
1312
if value:
1313
if len(value):
1314
return value
1315
return bool(value)
1316
1317
set_config(env, option_implementation)
1318
if set_as_define:
1319
set_define(env, option_implementation)
1320
if set_for_old_configure:
1321
add_old_configure_assignment(env, option_implementation)
1322
1323
# milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set.
1324
1325
1326
@depends(milestone)
1327
def enabled_in_nightly(milestone):
1328
return milestone.is_nightly