1 #!/usr/bin/env python 2 # Copyright 2017 the V8 project authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 """\ 6 Convenience wrapper for compiling V8 with gn/ninja and running tests. 7 Sets up build output directories if they don't exist. 8 Produces simulator builds for non-Intel target architectures. 9 Uses Goma by default if it is detected (at output directory setup time). 10 Expects to be run from the root of a V8 checkout. 11 12 Usage: 13 gm.py [<arch>].[<mode>].[<target>] [testname...] 14 15 All arguments are optional. Most combinations should work, e.g.: 16 gm.py ia32.debug x64.release d8 17 gm.py x64 mjsunit/foo cctest/test-bar/* 18 """ 19 # See HELP below for additional documentation. 20 21 import os 22 import subprocess 23 import sys 24 25 BUILD_OPTS_DEFAULT = "" 26 BUILD_OPTS_GOMA = "-j1000 -l50" 27 BUILD_TARGETS_TEST = ["d8", "cctest", "unittests"] 28 BUILD_TARGETS_ALL = ["all"] 29 30 # All arches that this script understands. 31 ARCHES = ["ia32", "x64", "arm", "arm64", "mipsel", "mips64el", "ppc", "ppc64", 32 "s390", "s390x", "x87"] 33 # Arches that get built/run when you don't specify any. 34 DEFAULT_ARCHES = ["ia32", "x64", "arm", "arm64"] 35 # Modes that this script understands. 36 MODES = ["release", "debug", "optdebug"] 37 # Modes that get built/run when you don't specify any. 38 DEFAULT_MODES = ["release", "debug"] 39 # Build targets that can be manually specified. 40 TARGETS = ["d8", "cctest", "unittests", "v8_fuzzers"] 41 # Build targets that get built when you don't specify any (and specified tests 42 # don't imply any other targets). 43 DEFAULT_TARGETS = ["d8"] 44 # Tests that run-tests.py would run by default that can be run with 45 # BUILD_TARGETS_TESTS. 46 DEFAULT_TESTS = ["cctest", "debugger", "intl", "message", "mjsunit", 47 "preparser", "unittests"] 48 # These can be suffixed to any <arch>.<mode> combo, or used standalone, 49 # or used as global modifiers (affecting all <arch>.<mode> combos). 50 ACTIONS = { 51 "all": {"targets": BUILD_TARGETS_ALL, "tests": []}, 52 "tests": {"targets": BUILD_TARGETS_TEST, "tests": []}, 53 "check": {"targets": BUILD_TARGETS_TEST, "tests": DEFAULT_TESTS}, 54 "checkall": {"targets": BUILD_TARGETS_ALL, "tests": ["ALL"]}, 55 } 56 57 HELP = """<arch> can be any of: %(arches)s 58 <mode> can be any of: %(modes)s 59 <target> can be any of: 60 - cctest, d8, unittests, v8_fuzzers (build respective binary) 61 - all (build all binaries) 62 - tests (build test binaries) 63 - check (build test binaries, run most tests) 64 - checkall (build all binaries, run more tests) 65 """ % {"arches": " ".join(ARCHES), 66 "modes": " ".join(MODES)} 67 68 TESTSUITES_TARGETS = {"benchmarks": "d8", 69 "cctest": "cctest", 70 "debugger": "d8", 71 "fuzzer": "v8_fuzzers", 72 "intl": "d8", 73 "message": "d8", 74 "mjsunit": "d8", 75 "mozilla": "d8", 76 "preparser": "d8", 77 "test262": "d8", 78 "unittests": "unittests", 79 "webkit": "d8"} 80 81 OUTDIR = "out" 82 83 IS_GOMA_MACHINE = (os.path.exists(os.path.expanduser("~/goma")) or 84 os.environ.get('GOMADIR')) 85 86 USE_GOMA = "true" if IS_GOMA_MACHINE else "false" 87 BUILD_OPTS = BUILD_OPTS_GOMA if IS_GOMA_MACHINE else BUILD_OPTS_DEFAULT 88 89 RELEASE_ARGS_TEMPLATE = """\ 90 is_component_build = false 91 is_debug = false 92 %s 93 use_goma = {GOMA} 94 v8_enable_backtrace = true 95 v8_enable_disassembler = true 96 v8_enable_object_print = true 97 v8_enable_verify_heap = true 98 """.replace("{GOMA}", USE_GOMA) 99 100 DEBUG_ARGS_TEMPLATE = """\ 101 gdb_index = true 102 is_component_build = true 103 is_debug = true 104 symbol_level = 2 105 %s 106 use_goma = {GOMA} 107 v8_enable_backtrace = true 108 v8_enable_slow_dchecks = true 109 v8_optimized_debug = false 110 """.replace("{GOMA}", USE_GOMA) 111 112 OPTDEBUG_ARGS_TEMPLATE = """\ 113 gdb_index = false 114 is_component_build = true 115 is_debug = true 116 symbol_level = 1 117 %s 118 use_goma = {GOMA} 119 v8_enable_backtrace = true 120 v8_enable_verify_heap = true 121 v8_optimized_debug = true 122 """.replace("{GOMA}", USE_GOMA) 123 124 ARGS_TEMPLATES = { 125 "release": RELEASE_ARGS_TEMPLATE, 126 "debug": DEBUG_ARGS_TEMPLATE, 127 "optdebug": OPTDEBUG_ARGS_TEMPLATE 128 } 129 130 def PrintHelpAndExit(): 131 print(__doc__) 132 print(HELP) 133 sys.exit(0) 134 135 def _Call(cmd, silent=False): 136 if not silent: print("# %s" % cmd) 137 return subprocess.call(cmd, shell=True) 138 139 def _Write(filename, content): 140 print("# echo > %s << EOF\n%sEOF" % (filename, content)) 141 with open(filename, "w") as f: 142 f.write(content) 143 144 def GetPath(arch, mode): 145 subdir = "%s.%s" % (arch, mode) 146 return os.path.join(OUTDIR, subdir) 147 148 class Config(object): 149 def __init__(self, arch, mode, targets, tests=[]): 150 self.arch = arch 151 self.mode = mode 152 self.targets = set(targets) 153 self.tests = set(tests) 154 155 def Extend(self, targets, tests=[]): 156 self.targets.update(targets) 157 self.tests.update(tests) 158 159 def GetTargetCpu(self): 160 cpu = "x86" 161 if self.arch.endswith("64") or self.arch == "s390x": 162 cpu = "x64" 163 return "target_cpu = \"%s\"" % cpu 164 165 def GetV8TargetCpu(self): 166 if self.arch in ("arm", "arm64", "mipsel", "mips64el", "ppc", "ppc64", 167 "s390", "s390x"): 168 return "\nv8_target_cpu = \"%s\"" % self.arch 169 return "" 170 171 def GetGnArgs(self): 172 template = ARGS_TEMPLATES[self.mode] 173 arch_specific = self.GetTargetCpu() + self.GetV8TargetCpu() 174 return template % arch_specific 175 176 def Build(self): 177 path = GetPath(self.arch, self.mode) 178 args_gn = os.path.join(path, "args.gn") 179 if not os.path.exists(path): 180 print("# mkdir -p %s" % path) 181 os.makedirs(path) 182 if not os.path.exists(args_gn): 183 _Write(args_gn, self.GetGnArgs()) 184 code = _Call("gn gen %s" % path) 185 if code != 0: return code 186 targets = " ".join(self.targets) 187 return _Call("ninja -C %s %s %s" % (path, BUILD_OPTS, targets)) 188 189 def RunTests(self): 190 if not self.tests: return 0 191 if "ALL" in self.tests: 192 tests = "" 193 else: 194 tests = " ".join(self.tests) 195 return _Call("tools/run-tests.py --arch=%s --mode=%s %s" % 196 (self.arch, self.mode, tests)) 197 198 def GetTestBinary(argstring): 199 for suite in TESTSUITES_TARGETS: 200 if argstring.startswith(suite): return TESTSUITES_TARGETS[suite] 201 return None 202 203 class ArgumentParser(object): 204 def __init__(self): 205 self.global_targets = set() 206 self.global_tests = set() 207 self.global_actions = set() 208 self.configs = {} 209 210 def PopulateConfigs(self, arches, modes, targets, tests): 211 for a in arches: 212 for m in modes: 213 path = GetPath(a, m) 214 if path not in self.configs: 215 self.configs[path] = Config(a, m, targets, tests) 216 else: 217 self.configs[path].Extend(targets, tests) 218 219 def ProcessGlobalActions(self): 220 have_configs = len(self.configs) > 0 221 for action in self.global_actions: 222 impact = ACTIONS[action] 223 if (have_configs): 224 for c in self.configs: 225 self.configs[c].Extend(**impact) 226 else: 227 self.PopulateConfigs(DEFAULT_ARCHES, DEFAULT_MODES, **impact) 228 229 def ParseArg(self, argstring): 230 if argstring in ("-h", "--help", "help"): 231 PrintHelpAndExit() 232 arches = [] 233 modes = [] 234 targets = [] 235 actions = [] 236 tests = [] 237 words = argstring.split('.') 238 if len(words) == 1: 239 word = words[0] 240 if word in ACTIONS: 241 self.global_actions.add(word) 242 return 243 if word in TARGETS: 244 self.global_targets.add(word) 245 return 246 maybe_target = GetTestBinary(word) 247 if maybe_target is not None: 248 self.global_tests.add(word) 249 self.global_targets.add(maybe_target) 250 return 251 for word in words: 252 if word in ARCHES: 253 arches.append(word) 254 elif word in MODES: 255 modes.append(word) 256 elif word in TARGETS: 257 targets.append(word) 258 elif word in ACTIONS: 259 actions.append(word) 260 else: 261 print("Didn't understand: %s" % word) 262 sys.exit(1) 263 # Process actions. 264 for action in actions: 265 impact = ACTIONS[action] 266 targets += impact["targets"] 267 tests += impact["tests"] 268 # Fill in defaults for things that weren't specified. 269 arches = arches or DEFAULT_ARCHES 270 modes = modes or DEFAULT_MODES 271 targets = targets or DEFAULT_TARGETS 272 # Produce configs. 273 self.PopulateConfigs(arches, modes, targets, tests) 274 275 def ParseArguments(self, argv): 276 if len(argv) == 0: 277 PrintHelpAndExit() 278 for argstring in argv: 279 self.ParseArg(argstring) 280 self.ProcessGlobalActions() 281 for c in self.configs: 282 self.configs[c].Extend(self.global_targets, self.global_tests) 283 return self.configs 284 285 def Main(argv): 286 parser = ArgumentParser() 287 configs = parser.ParseArguments(argv[1:]) 288 return_code = 0 289 for c in configs: 290 return_code += configs[c].Build() 291 for c in configs: 292 return_code += configs[c].RunTests() 293 if return_code == 0: 294 _Call("notify-send 'Done!' 'V8 compilation finished successfully.'", 295 silent=True) 296 else: 297 _Call("notify-send 'Error!' 'V8 compilation finished with errors.'", 298 silent=True) 299 return return_code 300 301 if __name__ == "__main__": 302 sys.exit(Main(sys.argv)) 303