1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5# Combined with build/autoconf/config.status.m4, ConfigStatus is an almost 6# drop-in replacement for autoconf 2.13's config.status, with features 7# borrowed from autoconf > 2.5, and additional features. 8 9from __future__ import absolute_import, print_function 10 11import logging 12import os 13import subprocess 14import sys 15import time 16 17from argparse import ArgumentParser 18 19from mach.logging import LoggingManager 20from mozbuild.backend.configenvironment import ConfigEnvironment 21from mozbuild.base import MachCommandConditions 22from mozbuild.frontend.emitter import TreeMetadataEmitter 23from mozbuild.frontend.reader import BuildReader 24from mozbuild.mozinfo import write_mozinfo 25from itertools import chain 26 27from mozbuild.backend import ( 28 backends, 29 get_backend_class, 30) 31 32 33log_manager = LoggingManager() 34 35 36ANDROID_IDE_ADVERTISEMENT = ''' 37============= 38ADVERTISEMENT 39 40You are building Firefox for Android. After your build completes, you can open 41the top source directory in Android Studio directly and build using Gradle. 42See the documentation at 43 44https://developer.mozilla.org/en-US/docs/Simple_Firefox_for_Android_build 45============= 46'''.strip() 47 48VISUAL_STUDIO_ADVERTISEMENT = ''' 49=============================== 50Visual Studio Support Available 51 52You are building Firefox on Windows. You can generate Visual Studio 53files by running: 54 55 mach build-backend --backend=VisualStudio 56 57=============================== 58'''.strip() 59 60 61def config_status(topobjdir='.', topsrcdir='.', defines=None, 62 non_global_defines=None, substs=None, source=None, 63 mozconfig=None, args=sys.argv[1:]): 64 '''Main function, providing config.status functionality. 65 66 Contrary to config.status, it doesn't use CONFIG_FILES or CONFIG_HEADERS 67 variables. 68 69 Without the -n option, this program acts as config.status and considers 70 the current directory as the top object directory, even when config.status 71 is in a different directory. It will, however, treat the directory 72 containing config.status as the top object directory with the -n option. 73 74 The options to this function are passed when creating the 75 ConfigEnvironment. These lists, as well as the actual wrapper script 76 around this function, are meant to be generated by configure. 77 See build/autoconf/config.status.m4. 78 ''' 79 80 if 'CONFIG_FILES' in os.environ: 81 raise Exception('Using the CONFIG_FILES environment variable is not ' 82 'supported.') 83 if 'CONFIG_HEADERS' in os.environ: 84 raise Exception('Using the CONFIG_HEADERS environment variable is not ' 85 'supported.') 86 87 if not os.path.isabs(topsrcdir): 88 raise Exception('topsrcdir must be defined as an absolute directory: ' 89 '%s' % topsrcdir) 90 91 default_backends = ['RecursiveMake'] 92 default_backends = (substs or {}).get('BUILD_BACKENDS', ['RecursiveMake']) 93 94 parser = ArgumentParser() 95 parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', 96 help='display verbose output') 97 parser.add_argument('-n', dest='not_topobjdir', action='store_true', 98 help='do not consider current directory as top object directory') 99 parser.add_argument('-d', '--diff', action='store_true', 100 help='print diffs of changed files.') 101 parser.add_argument('-b', '--backend', nargs='+', choices=sorted(backends), 102 default=default_backends, 103 help='what backend to build (default: %s).' % 104 ' '.join(default_backends)) 105 parser.add_argument('--dry-run', action='store_true', 106 help='do everything except writing files out.') 107 options = parser.parse_args(args) 108 109 # Without -n, the current directory is meant to be the top object directory 110 if not options.not_topobjdir: 111 topobjdir = os.path.abspath('.') 112 113 env = ConfigEnvironment(topsrcdir, topobjdir, defines=defines, 114 non_global_defines=non_global_defines, substs=substs, 115 source=source, mozconfig=mozconfig) 116 117 # mozinfo.json only needs written if configure changes and configure always 118 # passes this environment variable. 119 if 'WRITE_MOZINFO' in os.environ: 120 write_mozinfo(os.path.join(topobjdir, 'mozinfo.json'), env, os.environ) 121 122 cpu_start = time.clock() 123 time_start = time.time() 124 125 # Make appropriate backend instances, defaulting to RecursiveMakeBackend, 126 # or what is in BUILD_BACKENDS. 127 selected_backends = [get_backend_class(b)(env) for b in options.backend] 128 129 if options.dry_run: 130 for b in selected_backends: 131 b.dry_run = True 132 133 reader = BuildReader(env) 134 emitter = TreeMetadataEmitter(env) 135 # This won't actually do anything because of the magic of generators. 136 definitions = emitter.emit(reader.read_topsrcdir()) 137 138 log_level = logging.DEBUG if options.verbose else logging.INFO 139 log_manager.add_terminal_logging(level=log_level) 140 log_manager.enable_unstructured() 141 142 print('Reticulating splines...', file=sys.stderr) 143 if len(selected_backends) > 1: 144 definitions = list(definitions) 145 146 for the_backend in selected_backends: 147 the_backend.consume(definitions) 148 149 execution_time = 0.0 150 for obj in chain((reader, emitter), selected_backends): 151 summary = obj.summary() 152 print(summary, file=sys.stderr) 153 execution_time += summary.execution_time 154 if hasattr(obj, 'gyp_summary'): 155 summary = obj.gyp_summary() 156 print(summary, file=sys.stderr) 157 158 cpu_time = time.clock() - cpu_start 159 wall_time = time.time() - time_start 160 efficiency = cpu_time / wall_time if wall_time else 100 161 untracked = wall_time - execution_time 162 163 print( 164 'Total wall time: {:.2f}s; CPU time: {:.2f}s; Efficiency: ' 165 '{:.0%}; Untracked: {:.2f}s'.format( 166 wall_time, cpu_time, efficiency, untracked), 167 file=sys.stderr 168 ) 169 170 if options.diff: 171 for the_backend in selected_backends: 172 for path, diff in sorted(the_backend.file_diffs.items()): 173 print('\n'.join(diff)) 174 175 # Advertise Visual Studio if appropriate. 176 if os.name == 'nt' and 'VisualStudio' not in options.backend: 177 print(VISUAL_STUDIO_ADVERTISEMENT) 178 179 # Advertise Android Studio if it is appropriate. 180 if MachCommandConditions.is_android(env): 181 print(ANDROID_IDE_ADVERTISEMENT) 182