11cb0ef41Sopenharmony_ci#!/usr/bin/env python 21cb0ef41Sopenharmony_ci# Copyright 2016 the V8 project authors. All rights reserved. 31cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 41cb0ef41Sopenharmony_ci# found in the LICENSE file. 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ci"""Script to generate V8's gn arguments based on common developer defaults 71cb0ef41Sopenharmony_cior builder configurations. 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciGoma is used by default if detected. The compiler proxy is assumed to run. 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ciThis script can be added to the PATH and be used on other checkouts. It always 121cb0ef41Sopenharmony_ciruns for the checkout nesting the CWD. 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_ciConfigurations of this script live in infra/mb/mb_config.pyl. 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ciAvailable actions are: {gen,list}. Omitting the action defaults to "gen". 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci------------------------------------------------------------------------------- 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ciExamples: 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci# Generate the ia32.release config in out.gn/ia32.release. 231cb0ef41Sopenharmony_civ8gen.py ia32.release 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci# Generate into out.gn/foo without goma auto-detect. 261cb0ef41Sopenharmony_civ8gen.py gen -b ia32.release foo --no-goma 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci# Pass additional gn arguments after -- (don't use spaces within gn args). 291cb0ef41Sopenharmony_civ8gen.py ia32.optdebug -- v8_enable_slow_dchecks=true 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci# Generate gn arguments of 'V8 Linux64 - builder' from 'client.v8'. To switch 321cb0ef41Sopenharmony_ci# off goma usage here, the args.gn file must be edited manually. 331cb0ef41Sopenharmony_civ8gen.py -m client.v8 -b 'V8 Linux64 - builder' 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci# Show available configurations. 361cb0ef41Sopenharmony_civ8gen.py list 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci------------------------------------------------------------------------------- 391cb0ef41Sopenharmony_ci""" 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ci# for py2/py3 compatibility 421cb0ef41Sopenharmony_cifrom __future__ import print_function 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ciimport argparse 451cb0ef41Sopenharmony_ciimport os 461cb0ef41Sopenharmony_ciimport re 471cb0ef41Sopenharmony_ciimport subprocess 481cb0ef41Sopenharmony_ciimport sys 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ciCONFIG = os.path.join('infra', 'mb', 'mb_config.pyl') 511cb0ef41Sopenharmony_ciGOMA_DEFAULT = os.path.join(os.path.expanduser("~"), 'goma') 521cb0ef41Sopenharmony_ciOUT_DIR = 'out.gn' 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ciTOOLS_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 551cb0ef41Sopenharmony_cisys.path.append(os.path.join(TOOLS_PATH, 'mb')) 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ciimport mb 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_cidef _sanitize_nonalpha(text): 611cb0ef41Sopenharmony_ci return re.sub(r'[^a-zA-Z0-9.]', '_', text) 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ciclass GenerateGnArgs(object): 651cb0ef41Sopenharmony_ci def __init__(self, args): 661cb0ef41Sopenharmony_ci # Split args into this script's arguments and gn args passed to the 671cb0ef41Sopenharmony_ci # wrapped gn. 681cb0ef41Sopenharmony_ci index = args.index('--') if '--' in args else len(args) 691cb0ef41Sopenharmony_ci self._options = self._parse_arguments(args[:index]) 701cb0ef41Sopenharmony_ci self._gn_args = args[index + 1:] 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci def _parse_arguments(self, args): 731cb0ef41Sopenharmony_ci self.parser = argparse.ArgumentParser( 741cb0ef41Sopenharmony_ci description=__doc__, 751cb0ef41Sopenharmony_ci formatter_class=argparse.RawTextHelpFormatter, 761cb0ef41Sopenharmony_ci ) 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci def add_common_options(p): 791cb0ef41Sopenharmony_ci p.add_argument( 801cb0ef41Sopenharmony_ci '-m', '--master', default='developer_default', 811cb0ef41Sopenharmony_ci help='config group or master from mb_config.pyl - default: ' 821cb0ef41Sopenharmony_ci 'developer_default') 831cb0ef41Sopenharmony_ci p.add_argument( 841cb0ef41Sopenharmony_ci '-v', '--verbosity', action='count', 851cb0ef41Sopenharmony_ci help='print wrapped commands (use -vv to print output of wrapped ' 861cb0ef41Sopenharmony_ci 'commands)') 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_ci subps = self.parser.add_subparsers() 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci # Command: gen. 911cb0ef41Sopenharmony_ci gen_cmd = subps.add_parser( 921cb0ef41Sopenharmony_ci 'gen', help='generate a new set of build files (default)') 931cb0ef41Sopenharmony_ci gen_cmd.set_defaults(func=self.cmd_gen) 941cb0ef41Sopenharmony_ci add_common_options(gen_cmd) 951cb0ef41Sopenharmony_ci gen_cmd.add_argument( 961cb0ef41Sopenharmony_ci 'outdir', nargs='?', 971cb0ef41Sopenharmony_ci help='optional gn output directory') 981cb0ef41Sopenharmony_ci gen_cmd.add_argument( 991cb0ef41Sopenharmony_ci '-b', '--builder', 1001cb0ef41Sopenharmony_ci help='build configuration or builder name from mb_config.pyl, e.g. ' 1011cb0ef41Sopenharmony_ci 'x64.release') 1021cb0ef41Sopenharmony_ci gen_cmd.add_argument( 1031cb0ef41Sopenharmony_ci '-p', '--pedantic', action='store_true', 1041cb0ef41Sopenharmony_ci help='run gn over command-line gn args to catch errors early') 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci goma = gen_cmd.add_mutually_exclusive_group() 1071cb0ef41Sopenharmony_ci goma.add_argument( 1081cb0ef41Sopenharmony_ci '-g' , '--goma', 1091cb0ef41Sopenharmony_ci action='store_true', default=None, dest='goma', 1101cb0ef41Sopenharmony_ci help='force using goma') 1111cb0ef41Sopenharmony_ci goma.add_argument( 1121cb0ef41Sopenharmony_ci '--nogoma', '--no-goma', 1131cb0ef41Sopenharmony_ci action='store_false', default=None, dest='goma', 1141cb0ef41Sopenharmony_ci help='don\'t use goma auto detection - goma might still be used if ' 1151cb0ef41Sopenharmony_ci 'specified as a gn arg') 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci # Command: list. 1181cb0ef41Sopenharmony_ci list_cmd = subps.add_parser( 1191cb0ef41Sopenharmony_ci 'list', help='list available configurations') 1201cb0ef41Sopenharmony_ci list_cmd.set_defaults(func=self.cmd_list) 1211cb0ef41Sopenharmony_ci add_common_options(list_cmd) 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci # Default to "gen" unless global help is requested. 1241cb0ef41Sopenharmony_ci if not args or args[0] not in list(subps.choices) + ['-h', '--help']: 1251cb0ef41Sopenharmony_ci args = ['gen'] + args 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci return self.parser.parse_args(args) 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci def cmd_gen(self): 1301cb0ef41Sopenharmony_ci if not self._options.outdir and not self._options.builder: 1311cb0ef41Sopenharmony_ci self.parser.error('please specify either an output directory or ' 1321cb0ef41Sopenharmony_ci 'a builder/config name (-b), e.g. x64.release') 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci if not self._options.outdir: 1351cb0ef41Sopenharmony_ci # Derive output directory from builder name. 1361cb0ef41Sopenharmony_ci self._options.outdir = _sanitize_nonalpha(self._options.builder) 1371cb0ef41Sopenharmony_ci else: 1381cb0ef41Sopenharmony_ci # Also, if this should work on windows, we might need to use \ where 1391cb0ef41Sopenharmony_ci # outdir is used as path, while using / if it's used in a gn context. 1401cb0ef41Sopenharmony_ci if self._options.outdir.startswith('/'): 1411cb0ef41Sopenharmony_ci self.parser.error( 1421cb0ef41Sopenharmony_ci 'only output directories relative to %s are supported' % OUT_DIR) 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ci if not self._options.builder: 1451cb0ef41Sopenharmony_ci # Derive builder from output directory. 1461cb0ef41Sopenharmony_ci self._options.builder = self._options.outdir 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci # Check for builder/config in mb config. 1491cb0ef41Sopenharmony_ci if self._options.builder not in self._mbw.builder_groups[self._options.master]: 1501cb0ef41Sopenharmony_ci print('%s does not exist in %s for %s' % ( 1511cb0ef41Sopenharmony_ci self._options.builder, CONFIG, self._options.master)) 1521cb0ef41Sopenharmony_ci return 1 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci # TODO(machenbach): Check if the requested configurations has switched to 1551cb0ef41Sopenharmony_ci # gn at all. 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci # The directories are separated with slashes in a gn context (platform 1581cb0ef41Sopenharmony_ci # independent). 1591cb0ef41Sopenharmony_ci gn_outdir = '/'.join([OUT_DIR, self._options.outdir]) 1601cb0ef41Sopenharmony_ci 1611cb0ef41Sopenharmony_ci # Call MB to generate the basic configuration. 1621cb0ef41Sopenharmony_ci self._call_cmd([ 1631cb0ef41Sopenharmony_ci sys.executable, 1641cb0ef41Sopenharmony_ci '-u', os.path.join('tools', 'mb', 'mb.py'), 1651cb0ef41Sopenharmony_ci 'gen', 1661cb0ef41Sopenharmony_ci '-f', CONFIG, 1671cb0ef41Sopenharmony_ci '-m', self._options.master, 1681cb0ef41Sopenharmony_ci '-b', self._options.builder, 1691cb0ef41Sopenharmony_ci gn_outdir, 1701cb0ef41Sopenharmony_ci ]) 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci # Handle extra gn arguments. 1731cb0ef41Sopenharmony_ci gn_args_path = os.path.join(OUT_DIR, self._options.outdir, 'args.gn') 1741cb0ef41Sopenharmony_ci 1751cb0ef41Sopenharmony_ci # Append command-line args. 1761cb0ef41Sopenharmony_ci modified = self._append_gn_args( 1771cb0ef41Sopenharmony_ci 'command-line', gn_args_path, '\n'.join(self._gn_args)) 1781cb0ef41Sopenharmony_ci 1791cb0ef41Sopenharmony_ci # Append goma args. 1801cb0ef41Sopenharmony_ci # TODO(machenbach): We currently can't remove existing goma args from the 1811cb0ef41Sopenharmony_ci # original config. E.g. to build like a bot that uses goma, but switch 1821cb0ef41Sopenharmony_ci # goma off. 1831cb0ef41Sopenharmony_ci modified |= self._append_gn_args( 1841cb0ef41Sopenharmony_ci 'goma', gn_args_path, self._goma_args) 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_ci # Regenerate ninja files to check for errors in the additional gn args. 1871cb0ef41Sopenharmony_ci if modified and self._options.pedantic: 1881cb0ef41Sopenharmony_ci self._call_cmd(['gn', 'gen', gn_outdir]) 1891cb0ef41Sopenharmony_ci return 0 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci def cmd_list(self): 1921cb0ef41Sopenharmony_ci print('\n'.join(sorted(self._mbw.builder_groups[self._options.master]))) 1931cb0ef41Sopenharmony_ci return 0 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci def verbose_print_1(self, text): 1961cb0ef41Sopenharmony_ci if self._options.verbosity and self._options.verbosity >= 1: 1971cb0ef41Sopenharmony_ci print('#' * 80) 1981cb0ef41Sopenharmony_ci print(text) 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci def verbose_print_2(self, text): 2011cb0ef41Sopenharmony_ci if self._options.verbosity and self._options.verbosity >= 2: 2021cb0ef41Sopenharmony_ci indent = ' ' * 2 2031cb0ef41Sopenharmony_ci for l in text.splitlines(): 2041cb0ef41Sopenharmony_ci if type(l) == bytes: 2051cb0ef41Sopenharmony_ci l = l.decode() 2061cb0ef41Sopenharmony_ci print(indent + l) 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci def _call_cmd(self, args): 2091cb0ef41Sopenharmony_ci self.verbose_print_1(' '.join(args)) 2101cb0ef41Sopenharmony_ci try: 2111cb0ef41Sopenharmony_ci output = subprocess.check_output( 2121cb0ef41Sopenharmony_ci args=args, 2131cb0ef41Sopenharmony_ci stderr=subprocess.STDOUT, 2141cb0ef41Sopenharmony_ci ) 2151cb0ef41Sopenharmony_ci self.verbose_print_2(output) 2161cb0ef41Sopenharmony_ci except subprocess.CalledProcessError as e: 2171cb0ef41Sopenharmony_ci self.verbose_print_2(e.output) 2181cb0ef41Sopenharmony_ci raise 2191cb0ef41Sopenharmony_ci 2201cb0ef41Sopenharmony_ci def _find_work_dir(self, path): 2211cb0ef41Sopenharmony_ci """Find the closest v8 root to `path`.""" 2221cb0ef41Sopenharmony_ci if os.path.exists(os.path.join(path, 'tools', 'dev', 'v8gen.py')): 2231cb0ef41Sopenharmony_ci # Approximate the v8 root dir by a folder where this script exists 2241cb0ef41Sopenharmony_ci # in the expected place. 2251cb0ef41Sopenharmony_ci return path 2261cb0ef41Sopenharmony_ci elif os.path.dirname(path) == path: 2271cb0ef41Sopenharmony_ci raise Exception( 2281cb0ef41Sopenharmony_ci 'This appears to not be called from a recent v8 checkout') 2291cb0ef41Sopenharmony_ci else: 2301cb0ef41Sopenharmony_ci return self._find_work_dir(os.path.dirname(path)) 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci @property 2331cb0ef41Sopenharmony_ci def _goma_dir(self): 2341cb0ef41Sopenharmony_ci return os.path.normpath(os.environ.get('GOMA_DIR') or GOMA_DEFAULT) 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci @property 2371cb0ef41Sopenharmony_ci def _need_goma_dir(self): 2381cb0ef41Sopenharmony_ci return self._goma_dir != GOMA_DEFAULT 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci @property 2411cb0ef41Sopenharmony_ci def _use_goma(self): 2421cb0ef41Sopenharmony_ci if self._options.goma is None: 2431cb0ef41Sopenharmony_ci # Auto-detect. 2441cb0ef41Sopenharmony_ci return os.path.exists(self._goma_dir) and os.path.isdir(self._goma_dir) 2451cb0ef41Sopenharmony_ci else: 2461cb0ef41Sopenharmony_ci return self._options.goma 2471cb0ef41Sopenharmony_ci 2481cb0ef41Sopenharmony_ci @property 2491cb0ef41Sopenharmony_ci def _goma_args(self): 2501cb0ef41Sopenharmony_ci """Gn args for using goma.""" 2511cb0ef41Sopenharmony_ci # Specify goma args if we want to use goma and if goma isn't specified 2521cb0ef41Sopenharmony_ci # via command line already. The command-line always has precedence over 2531cb0ef41Sopenharmony_ci # any other specification. 2541cb0ef41Sopenharmony_ci if (self._use_goma and 2551cb0ef41Sopenharmony_ci not any(re.match(r'use_goma\s*=.*', x) for x in self._gn_args)): 2561cb0ef41Sopenharmony_ci if self._need_goma_dir: 2571cb0ef41Sopenharmony_ci return 'use_goma=true\ngoma_dir="%s"' % self._goma_dir 2581cb0ef41Sopenharmony_ci else: 2591cb0ef41Sopenharmony_ci return 'use_goma=true' 2601cb0ef41Sopenharmony_ci else: 2611cb0ef41Sopenharmony_ci return '' 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci def _append_gn_args(self, type, gn_args_path, more_gn_args): 2641cb0ef41Sopenharmony_ci """Append extra gn arguments to the generated args.gn file.""" 2651cb0ef41Sopenharmony_ci if not more_gn_args: 2661cb0ef41Sopenharmony_ci return False 2671cb0ef41Sopenharmony_ci self.verbose_print_1('Appending """\n%s\n""" to %s.' % ( 2681cb0ef41Sopenharmony_ci more_gn_args, os.path.abspath(gn_args_path))) 2691cb0ef41Sopenharmony_ci with open(gn_args_path, 'a') as f: 2701cb0ef41Sopenharmony_ci f.write('\n# Additional %s args:\n' % type) 2711cb0ef41Sopenharmony_ci f.write(more_gn_args) 2721cb0ef41Sopenharmony_ci f.write('\n') 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_ci # Artificially increment modification time as our modifications happen too 2751cb0ef41Sopenharmony_ci # fast. This makes sure that gn is properly rebuilding the ninja files. 2761cb0ef41Sopenharmony_ci mtime = os.path.getmtime(gn_args_path) + 1 2771cb0ef41Sopenharmony_ci with open(gn_args_path, 'a'): 2781cb0ef41Sopenharmony_ci os.utime(gn_args_path, (mtime, mtime)) 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci return True 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci def main(self): 2831cb0ef41Sopenharmony_ci # Always operate relative to the base directory for better relative-path 2841cb0ef41Sopenharmony_ci # handling. This script can be used in any v8 checkout. 2851cb0ef41Sopenharmony_ci workdir = self._find_work_dir(os.getcwd()) 2861cb0ef41Sopenharmony_ci if workdir != os.getcwd(): 2871cb0ef41Sopenharmony_ci self.verbose_print_1('cd ' + workdir) 2881cb0ef41Sopenharmony_ci os.chdir(workdir) 2891cb0ef41Sopenharmony_ci 2901cb0ef41Sopenharmony_ci # Initialize MB as a library. 2911cb0ef41Sopenharmony_ci self._mbw = mb.MetaBuildWrapper() 2921cb0ef41Sopenharmony_ci 2931cb0ef41Sopenharmony_ci # TODO(machenbach): Factor out common methods independent of mb arguments. 2941cb0ef41Sopenharmony_ci self._mbw.ParseArgs(['lookup', '-f', CONFIG]) 2951cb0ef41Sopenharmony_ci self._mbw.ReadConfigFile() 2961cb0ef41Sopenharmony_ci 2971cb0ef41Sopenharmony_ci if not self._options.master in self._mbw.builder_groups: 2981cb0ef41Sopenharmony_ci print('%s not found in %s\n' % (self._options.master, CONFIG)) 2991cb0ef41Sopenharmony_ci print('Choose one of:\n%s\n' % ( 3001cb0ef41Sopenharmony_ci '\n'.join(sorted(self._mbw.builder_groups.keys())))) 3011cb0ef41Sopenharmony_ci return 1 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci return self._options.func() 3041cb0ef41Sopenharmony_ci 3051cb0ef41Sopenharmony_ci 3061cb0ef41Sopenharmony_ciif __name__ == "__main__": 3071cb0ef41Sopenharmony_ci gen = GenerateGnArgs(sys.argv[1:]) 3081cb0ef41Sopenharmony_ci try: 3091cb0ef41Sopenharmony_ci sys.exit(gen.main()) 3101cb0ef41Sopenharmony_ci except Exception: 3111cb0ef41Sopenharmony_ci if not gen._options.verbosity or gen._options.verbosity < 2: 3121cb0ef41Sopenharmony_ci print ('\nHint: You can raise verbosity (-vv) to see the output of ' 3131cb0ef41Sopenharmony_ci 'failed commands.\n') 3141cb0ef41Sopenharmony_ci raise 315