blob: 963918575956a73accce59bbed6a7a9e5da8bcc7 [file] [log] [blame]
Fumitoshi Ukai8599cf52024-02-15 04:34:521#!/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
7Expected to run it at repository root, where top DEP, .gn etc exists.
8Not intended to run by user.
9See go/reqs-for-peep
10"""
11
12import argparse
13import os
14import re
15import subprocess
16import sys
17
18def _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
Fumitoshi Ukai128f48d2024-06-04 08:25:2041
Fumitoshi Ukai8599cf52024-02-15 04:34:5242def _use_reclient(outdir):
Fumitoshi Ukai128f48d2024-06-04 08:25:2043 use_remoteexec = False
44 use_reclient = None
Fumitoshi Ukai8599cf52024-02-15 04:34:5245 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]
Fumitoshi Ukai128f48d2024-06-04 08:25:2050 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
Fumitoshi Ukai8599cf52024-02-15 04:34:5263
64def main():
65 parser = argparse.ArgumentParser()
Philipp Wollermannc5830102024-04-19 04:35:2366 parser.add_argument('source', nargs='+',
Fumitoshi Ukai8599cf52024-02-15 04:34:5267 help=('The source file being analyzed.'
Philipp Wollermannc5830102024-04-19 04:35:2368 'Multiple source arguments can be passed in order to batch '
69 'process if desired.'))
Fumitoshi Ukai8599cf52024-02-15 04:34:5270 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',
Philipp Wollermannc5830102024-04-19 04:35:2377 help=('Output directory, containing args.gn, which specifies the build '
Fumitoshi Ukai8599cf52024-02-15 04:34:5278 'configuration.'))
Richard Wang02fbd2dd2024-12-03 08:29:3179 parser.add_argument('--log-dir', help=('Directory to save log files to.'))
Fumitoshi Ukai3788c9e2025-02-06 06:05:4780 parser.add_argument('--format', choices=['proto', 'prototext', 'json'],
81 default='proto', help=('Output format.'))
Fumitoshi Ukai8599cf52024-02-15 04:34:5282 options = parser.parse_args()
83
84 this_dir = os.path.dirname(__file__)
85 repo_root = os.path.join(this_dir, '..', '..')
86
87 targets = []
Fumitoshi Ukai82a82622025-02-06 02:49:1688 use_prepare = True
Fumitoshi Ukai3fb1a042024-10-08 00:58:2589 use_prepare_header_only = True
Fumitoshi Ukai8599cf52024-02-15 04:34:5290 for source in options.source:
Fumitoshi Ukai3fb1a042024-10-08 00:58:2591 _, ext = os.path.splitext(source)
Fumitoshi Ukai82a82622025-02-06 02:49:1692 if ext == '.java':
93 # need to include generated *.jar for java.
94 use_prepare = False
Fumitoshi Ukai3fb1a042024-10-08 00:58:2595 if ext not in ('.c', '.cc', '.cxx', '.cpp', '.m', '.mm', '.S',
96 '.h', '.hxx', '.hpp', '.inc'):
97 use_prepare_header_only = False
Fumitoshi Ukai8599cf52024-02-15 04:34:5298 # 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
Fumitoshi Ukaid1f5e3352024-05-09 04:31:04103 if _use_reclient(options.out_dir):
104 # b/335795623 ide_query compiler_arguments contain non-compiler arguments
105 sys.stderr.write(
Fumitoshi Ukai128f48d2024-06-04 08:25:20106 'ide_query won\'t work well with "use_reclient=true"\n'
107 'Set "use_reclient=false" in args.gn.\n')
Fumitoshi Ukaid1f5e3352024-05-09 04:31:04108 sys.exit(1)
Fumitoshi Ukai8599cf52024-02-15 04:34:52109 if options.perform_build:
Fumitoshi Ukai9ac4c6212025-03-19 03:31:32110 # 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'))
Fumitoshi Ukaid1f5e3352024-05-09 04:31:04114 args = ['siso', 'ninja']
Fumitoshi Ukai4ec43222024-04-30 08:51:36115 # use `-k=0` to build generated files as much as possible.
116 args.extend([
117 '-k=0',
Fumitoshi Ukai4ec43222024-04-30 08:51:36118 '-C',
119 options.out_dir,
120 ])
Fumitoshi Ukai82a82622025-02-06 02:49:16121 if use_prepare:
122 args.extend(['--prepare'])
Richard Wang02fbd2dd2024-12-03 08:29:31123 if options.log_dir:
124 args.extend(['-log_dir', options.log_dir])
Fumitoshi Ukai8599cf52024-02-15 04:34:52125 args.extend(targets)
Fumitoshi Ukai35d16de2024-05-02 09:18:32126 env = os.environ.copy()
Fumitoshi Ukai3fb1a042024-10-08 00:58:25127 if use_prepare_header_only:
Fumitoshi Ukai86ca44c2025-04-11 04:41:52128 env['SISO_EXPERIMENTS'] = 'no-fast-deps,prepare-header-only,ignore-missing-targets'
Fumitoshi Ukai3fb1a042024-10-08 00:58:25129 else:
Fumitoshi Ukai86ca44c2025-04-11 04:41:52130 env['SISO_EXPERIMENTS'] = 'no-fast-deps,ignore-missing-targets'
Richard Wang95de51a62024-05-08 08:20:45131 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.
Fumitoshi Ukai8599cf52024-02-15 04:34:52147
148 args = ['siso', 'query', 'ideanalysis', '-C', options.out_dir]
Fumitoshi Ukai3788c9e2025-02-06 06:05:47149 if options.format:
150 args.extend(['--format', options.format])
Fumitoshi Ukai8599cf52024-02-15 04:34:52151 args.extend(targets)
152 subprocess.run(args, cwd=repo_root, check=True)
153
154if __name__ == '__main__':
155 sys.exit(main())