# -*- 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/. @imports('codecs') @imports('sys') def encoded_open(path, mode): encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8' return codecs.open(path, mode, encoding) option(env='AUTOCONF', nargs=1, help='Path to autoconf 2.13') @depends(mozconfig, 'AUTOCONF') @checking('for autoconf') @imports(_from='os.path', _import='exists') @imports('re') def autoconf(mozconfig, autoconf): mozconfig_autoconf = None if mozconfig['path']: make_extra = mozconfig['make_extra'] if make_extra: for assignment in make_extra: m = re.match('(?:export\s+)?AUTOCONF\s*:?=\s*(.+)$', assignment) if m: mozconfig_autoconf = m.group(1) autoconf = autoconf[0] if autoconf else None for ac in (mozconfig_autoconf, autoconf, 'autoconf-2.13', 'autoconf2.13', 'autoconf213'): if ac: autoconf = find_program(ac) if autoconf: break else: fink = find_program('fink') if fink: autoconf = os.path.normpath(os.path.join( fink, '..', '..', 'lib', 'autoconf2.13', 'bin', 'autoconf')) else: brew = find_program('brew') if brew: autoconf = os.path.normpath(os.path.join( brew, '..', '..', 'Cellar', 'autoconf213', '2.13', 'bin', 'autoconf213')) if not autoconf: die('Could not find autoconf 2.13') if not exists(autoconf): die('Could not find autoconf 2.13 at %s', autoconf) return autoconf set_config('AUTOCONF', autoconf) @depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell, old_configure_assignments, build_project) @imports(_from='__builtin__', _import='open') @imports(_from='__builtin__', _import='print') @imports(_from='__builtin__', _import='sorted') @imports('glob') @imports('itertools') @imports('subprocess') # Import getmtime without overwriting the sandbox os.path. @imports(_from='os.path', _import='getmtime') @imports(_from='os.path', _import='exists') @imports(_from='mozbuild.shellutil', _import='quote') def prepare_configure(old_configure, mozconfig, autoconf, build_env, shell, old_configure_assignments, build_project): # os.path.abspath in the sandbox will ensure forward slashes on Windows, # which is actually necessary because this path actually ends up literally # as $0, and backslashes there breaks autoconf's detection of the source # directory. old_configure = os.path.abspath(old_configure[0]) if build_project == 'js': old_configure_dir = os.path.dirname(old_configure) if not old_configure_dir.endswith('/js/src'): old_configure = os.path.join(old_configure_dir, 'js', 'src', os.path.basename(old_configure)) refresh = True if exists(old_configure): mtime = getmtime(old_configure) aclocal = os.path.join(build_env.topsrcdir, 'build', 'autoconf', '*.m4') for input in itertools.chain( (old_configure + '.in', os.path.join(os.path.dirname(old_configure), 'aclocal.m4')), glob.iglob(aclocal), ): if getmtime(input) > mtime: break else: refresh = False if refresh: log.info('Refreshing %s with %s', old_configure, autoconf) script = subprocess.check_output([ shell, autoconf, '--localdir=%s' % os.path.dirname(old_configure), old_configure + '.in']) if not script: die('Generated old-configure is empty! Check that your autoconf 2.13 program works!') # Make old-configure append to config.log, where we put our own log. # This could be done with a m4 macro, but it's way easier this way script = script.replace('>./config.log', '>>./config.log') with open(old_configure, 'wb') as fh: fh.write(script) cmd = [shell, old_configure] with encoded_open('old-configure.vars', 'w') as out: log.debug('Injecting the following to old-configure:') def inject(command): print(command, file=out) # noqa Python 2vs3 log.debug('| %s', command) if mozconfig['path']: inject('# start of mozconfig values') items = {} for key, value in mozconfig['vars']['added'].items(): items[key] = (value, 'added') for key, (old, value) in mozconfig['vars']['modified'].items(): items[key] = (value, 'modified') for key, (value, action) in sorted(items.items()): inject("%s=%s # %s" % (key, quote(value), action)) for t in ('env', 'vars'): for key in sorted(mozconfig[t]['removed'].keys()): inject("unset %s # from %s" % (key, t)) inject('# end of mozconfig values') # Autoconf is special, because it might be passed from # mozconfig['make_extra'], which we don't pass automatically above. inject('export AUTOCONF=%s' % quote(autoconf)) for assignment in old_configure_assignments: inject(assignment) return cmd @template def old_configure_options(*options): for opt in options: option(opt, nargs='*', help='Help missing for old configure options') @dependable def all_options(): return list(options) return depends(prepare_configure, extra_old_configure_args, all_options, *options) @old_configure_options( '--cache-file', '--datadir', '--enable-content-sandbox', '--enable-cookies', '--enable-cpp-rtti', '--enable-crashreporter', '--enable-dbus', '--enable-debug-js-modules', '--enable-directshow', '--enable-dtrace', '--enable-dump-painting', '--enable-extensions', '--enable-feeds', '--enable-gconf', '--enable-icf', '--enable-install-strip', '--enable-ios-target', '--enable-libjpeg-turbo', '--enable-libproxy', '--enable-llvm-hacks', '--enable-logrefcnt', '--enable-memory-sanitizer', '--enable-mobile-optimize', '--enable-necko-wifi', '--enable-negotiateauth', '--enable-nfc', '--enable-nspr-build', '--enable-official-branding', '--enable-parental-controls', '--enable-posix-nspr-emulation', '--enable-pref-extensions', '--enable-readline', '--enable-sandbox', '--enable-signmar', '--enable-startup-notification', '--enable-startupcache', '--enable-strip', '--enable-synth-pico', '--enable-system-cairo', '--enable-system-extension-dirs', '--enable-system-pixman', '--enable-system-sqlite', '--enable-thread-sanitizer', '--enable-signed-overflow-sanitizer', '--enable-universalchardet', '--enable-unsigned-overflow-sanitizer', '--enable-updater', '--enable-xul', '--enable-zipwriter', '--includedir', '--libdir', '--no-create', '--prefix', '--with-android-distribution-directory', '--with-android-max-sdk', '--with-android-min-sdk', '--with-android-sdk', '--with-app-basename', '--with-app-name', '--with-arch', '--with-branding', '--with-cross-lib', '--with-debug-label', '--with-distribution-id', '--with-doc-include-dirs', '--with-doc-input-dirs', '--with-doc-output-dir', '--with-float-abi', '--with-fpu', '--with-intl-api', '--with-ios-sdk', '--with-jitreport-granularity', '--with-macbundlename-prefix', '--with-nspr-cflags', '--with-nspr-exec-prefix', '--with-nspr-libs', '--with-nspr-prefix', '--with-nss-exec-prefix', '--with-nss-prefix', '--with-qemu-exe', '--with-sixgill', '--with-soft-float', '--with-system-bz2', '--with-system-icu', '--with-system-libevent', '--with-system-nspr', '--with-system-nss', '--with-system-png', '--with-system-zlib', '--with-thumb', '--with-thumb-interwork', '--with-unify-dist', '--with-user-appdir', '--x-includes', '--x-libraries', ) @imports(_from='__builtin__', _import='compile') @imports(_from='__builtin__', _import='open') @imports('logging') @imports('os') @imports('subprocess') @imports('sys') @imports(_from='mozbuild.shellutil', _import='quote') @imports(_from='mozbuild.shellutil', _import='split') @imports(_from='mozbuild.util', _import='encode') def old_configure(prepare_configure, extra_old_configure_args, all_options, *options): cmd = prepare_configure # old-configure only supports the options listed in @old_configure_options # so we don't need to pass it every single option we've been passed. Only # the ones that are not supported by python configure need to. cmd += [ value.format(name) for name, value in zip(all_options, options) if value.origin != 'default' ] env = dict(os.environ) extra_env = {} # We also pass it the options from js/moz.configure so that it can pass # them down to js/src/configure. Note this list is empty when running # js/src/configure, in which case we don't need to pass those options # to old-configure since old-configure doesn't handle them anyways. if extra_old_configure_args: for arg in extra_old_configure_args: if arg.startswith('-'): cmd.append(arg) else: k, v = arg.split('=', 1) extra_env[k] = v if extra_env: env.update(extra_env) # For debugging purpose, in case it's not what we'd expect. log.debug('Running %s', quote(*cmd)) if extra_env: log.debug('with extra environment: %s', ' '.join('%s=%s' % pair for pair in extra_env.iteritems())) # Our logging goes to config.log, the same file old.configure uses. # We can't share the handle on the file, so close it. We assume nothing # beyond this point is going to be interesting to log to config.log from # our end, so we don't make the effort to recreate a logging.FileHandler. logger = logging.getLogger('moz.configure') for handler in logger.handlers: if isinstance(handler, logging.FileHandler): handler.close() logger.removeHandler(handler) log_size = os.path.getsize('config.log') ret = subprocess.call(cmd, env=encode(env)) if ret: with log.queue_debug(): with encoded_open('config.log', 'r') as fh: fh.seek(log_size) for line in fh: log.debug(line.rstrip()) log.error('old-configure failed') sys.exit(ret) raw_config = { 'split': split, 'unique_list': unique_list, } with encoded_open('config.data', 'r') as fh: code = compile(fh.read(), 'config.data', 'exec') # Every variation of the exec() function I tried led to: # SyntaxError: unqualified exec is not allowed in function 'main' it # contains a nested function with free variables exec code in raw_config # noqa # Ensure all the flags known to old-configure appear in the # @old_configure_options above. all_options = set(all_options) for flag in raw_config['flags']: if flag not in all_options: die('Missing option in `@old_configure_options` in %s: %s', __file__, flag) # If the code execution above fails, we want to keep the file around for # debugging. os.remove('config.data') return raw_config # set_config is only available in the global namespace, not directly in # @depends functions, but we do need to enumerate the result of # old_configure, so we cheat. @imports('__sandbox__') def set_old_configure_config(name, value): __sandbox__.set_config_impl(name, value) # Same as set_old_configure_config, but for set_define. @imports('__sandbox__') def set_old_configure_define(name, value): __sandbox__.set_define_impl(name, value) @depends(old_configure) @imports('types') def post_old_configure(raw_config): for k, v in raw_config['substs']: set_old_configure_config( k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v) for k, v in dict(raw_config['defines']).iteritems(): set_old_configure_define(k[1:-1], v[1:-1]) set_old_configure_config('non_global_defines', raw_config['non_global_defines']) # Assuming no other option is declared after this function, handle the # env options that were injected by mozconfig_options by creating dummy # Option instances and having the sandbox's CommandLineHelper handle # them. We only do so for options that haven't been declared so far, # which should be a proxy for the options that old-configure handles # and that we don't know anything about. @depends('--help') @imports('__sandbox__') @imports(_from='mozbuild.configure.options', _import='Option') def remaining_mozconfig_options(_): helper = __sandbox__._helper for arg in helper: if helper._origins[arg] != 'mozconfig': continue name = arg.split('=', 1)[0] if name.isupper() and name not in __sandbox__._options: option = Option(env=name, nargs='*', help=name) helper.handle(option) # Please do not add anything after remaining_mozconfig_options()