OLD | NEW |
(Empty) | |
| 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)) |
OLD | NEW |