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