tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ide_query (5581B)


      1 #!/usr/bin/env python3
      2 # Copyright 2024 The Chromium 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 """A script gets the information needed by lDE language services.
      6 
      7 Expected to run it at repository root,  where top DEP, .gn etc exists.
      8 Not intended to run by user.
      9 See go/reqs-for-peep
     10 """
     11 
     12 import argparse
     13 import os
     14 import re
     15 import subprocess
     16 import sys
     17 
     18 def _gn_lines(output_dir, path):
     19     """
     20     Generator function that returns args.gn lines one at a time, following
     21     import directives as needed.
     22     """
     23     import_re = re.compile(r'\s*import\("(.*)"\)')
     24     with open(path, encoding="utf-8") as f:
     25         for line in f:
     26             match = import_re.match(line)
     27             if match:
     28                 raw_import_path = match.groups()[0]
     29                 if raw_import_path[:2] == "//":
     30                     import_path = os.path.normpath(
     31                         os.path.join(output_dir, "..", "..",
     32                                      raw_import_path[2:]))
     33                 else:
     34                     import_path = os.path.normpath(
     35                         os.path.join(os.path.dirname(path), raw_import_path))
     36                 for import_line in _gn_lines(output_dir, import_path):
     37                     yield import_line
     38             else:
     39                 yield line
     40 
     41 
     42 def _use_reclient(outdir):
     43   use_remoteexec = False
     44   use_reclient = None
     45   args_gn = os.path.join(outdir, 'args.gn')
     46   if not os.path.exists(args_gn):
     47     return False
     48   for line in _gn_lines(outdir, args_gn):
     49     line_without_comment = line.split('#')[0]
     50     m = re.match(r"(^|\s*)use_remoteexec\s*=\s*(true|false)\s*$",
     51                  line_without_comment)
     52     if m:
     53       use_remoteexec = m.group(2) == 'true'
     54       continue
     55     m = re.match(r"(^|\s*)use_reclient\s*=\s*(true|false)\s*$",
     56                  line_without_comment)
     57     if m:
     58       use_reclient = m.group(2) == 'true'
     59   if use_reclient == None:
     60       use_reclient = use_remoteexec
     61   return use_reclient
     62 
     63 
     64 def main():
     65   parser = argparse.ArgumentParser()
     66   parser.add_argument('source', nargs='+',
     67     help=('The source file being analyzed.'
     68           'Multiple source arguments can be passed in order to batch '
     69           'process if desired.'))
     70   parser.add_argument('--perform-build', action='store_true',
     71     help=('If specified, actually build the target, including any generated '
     72           'prerequisite files. '
     73           'If --perform-build is not passed, the contents of '
     74           'the GeneratedFile results will only be returned if a build has '
     75           'been previously completed, and may be stale.'))
     76   parser.add_argument('--out-dir',
     77     help=('Output directory, containing args.gn, which specifies the build '
     78           'configuration.'))
     79   parser.add_argument('--log-dir', help=('Directory to save log files to.'))
     80   parser.add_argument('--format', choices=['proto', 'prototext', 'json'],
     81                       default='proto', help=('Output format.'))
     82   options = parser.parse_args()
     83 
     84   this_dir = os.path.dirname(__file__)
     85   repo_root = os.path.join(this_dir, '..', '..')
     86 
     87   targets = []
     88   use_prepare = True
     89   use_prepare_header_only = True
     90   for source in options.source:
     91     _, ext = os.path.splitext(source)
     92     if ext == '.java':
     93         # need to include generated *.jar for java.
     94         use_prepare = False
     95     if ext not in ('.c', '.cc', '.cxx', '.cpp', '.m', '.mm', '.S',
     96                    '.h', '.hxx', '.hpp', '.inc'):
     97         use_prepare_header_only = False
     98     # source is repo root (cwd) relative,
     99     # but siso uses out dir relative target.
    100     target = os.path.relpath(source, start=options.out_dir) + "^"
    101     targets.append(target)
    102 
    103   if _use_reclient(options.out_dir):
    104     # b/335795623 ide_query compiler_arguments contain non-compiler arguments
    105     sys.stderr.write(
    106         'ide_query won\'t work well with "use_reclient=true"\n'
    107         'Set "use_reclient=false" in args.gn.\n')
    108     sys.exit(1)
    109   if options.perform_build:
    110     # forget last targets of normal build as this build will update
    111     # .siso_fs_state.
    112     if os.path.exists(os.path.join(options.out_dir, '.siso_last_targets')):
    113         os.remove(os.path.join(options.out_dir, '.siso_last_targets'))
    114     args = ['siso', 'ninja']
    115     # use `-k=0` to build generated files as much as possible.
    116     args.extend([
    117         '-k=0',
    118         '-C',
    119         options.out_dir,
    120     ])
    121     if use_prepare:
    122         args.extend(['--prepare'])
    123     if options.log_dir:
    124         args.extend(['-log_dir', options.log_dir])
    125     args.extend(targets)
    126     env = os.environ.copy()
    127     if use_prepare_header_only:
    128         env['SISO_EXPERIMENTS'] = 'no-fast-deps,prepare-header-only'
    129     else:
    130         env['SISO_EXPERIMENTS'] = 'no-fast-deps'
    131     with subprocess.Popen(
    132         args,
    133         cwd=repo_root,
    134         env=env,
    135         stderr=subprocess.STDOUT,
    136         stdout=subprocess.PIPE,
    137         universal_newlines=True
    138     ) as p:
    139       for line in p.stdout:
    140           print(line, end='', file=sys.stderr)
    141       # loop ends when program finishes, but must wait else returncode is None.
    142       p.wait()
    143       if p.returncode != 0:
    144         # TODO: report error in IdeAnalysis.Status?
    145         sys.stderr.write('build failed with %d\n' % p.returncode)
    146         # even if build fails, it should report ideanalysis back.
    147 
    148   args = ['siso', 'query', 'ideanalysis', '-C', options.out_dir]
    149   if options.format:
    150       args.extend(['--format', options.format])
    151   args.extend(targets)
    152   subprocess.run(args, cwd=repo_root, check=True)
    153 
    154 if __name__ == '__main__':
    155   sys.exit(main())