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