1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2023 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker"""Given a .build_config.json file, generates a .classpath file that can be 6*6777b538SAndroid Build Coastguard Workerused with the "Language Support for Java™ by Red Hat" Visual Studio Code 7*6777b538SAndroid Build Coastguard Workerextension. See //docs/vscode.md for details. 8*6777b538SAndroid Build Coastguard Worker""" 9*6777b538SAndroid Build Coastguard Worker 10*6777b538SAndroid Build Coastguard Workerimport argparse 11*6777b538SAndroid Build Coastguard Workerimport logging 12*6777b538SAndroid Build Coastguard Workerimport json 13*6777b538SAndroid Build Coastguard Workerimport os 14*6777b538SAndroid Build Coastguard Workerimport sys 15*6777b538SAndroid Build Coastguard Workerimport xml.etree.ElementTree 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard Workersys.path.append(os.path.join(os.path.dirname(__file__), 'gyp')) 18*6777b538SAndroid Build Coastguard Workerfrom util import build_utils 19*6777b538SAndroid Build Coastguard Worker 20*6777b538SAndroid Build Coastguard Workersys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) 21*6777b538SAndroid Build Coastguard Workerimport gn_helpers 22*6777b538SAndroid Build Coastguard Worker 23*6777b538SAndroid Build Coastguard Worker 24*6777b538SAndroid Build Coastguard Workerdef _WithoutSuffix(string, suffix): 25*6777b538SAndroid Build Coastguard Worker if not string.endswith(suffix): 26*6777b538SAndroid Build Coastguard Worker raise ValueError(f'{string!r} does not end with {suffix!r}') 27*6777b538SAndroid Build Coastguard Worker return string[:-len(suffix)] 28*6777b538SAndroid Build Coastguard Worker 29*6777b538SAndroid Build Coastguard Worker 30*6777b538SAndroid Build Coastguard Workerdef _GetJavaRoot(path): 31*6777b538SAndroid Build Coastguard Worker # The authoritative way to determine the Java root for a given source file is 32*6777b538SAndroid Build Coastguard Worker # to parse the source code and extract the package and class names, but let's 33*6777b538SAndroid Build Coastguard Worker # keep things simple and use some heuristics to try to guess the Java root 34*6777b538SAndroid Build Coastguard Worker # from the file path instead. 35*6777b538SAndroid Build Coastguard Worker while True: 36*6777b538SAndroid Build Coastguard Worker dirname, basename = os.path.split(path) 37*6777b538SAndroid Build Coastguard Worker if not basename: 38*6777b538SAndroid Build Coastguard Worker raise RuntimeError(f'Unable to determine the Java root for {path!r}') 39*6777b538SAndroid Build Coastguard Worker if basename in ('java', 'src'): 40*6777b538SAndroid Build Coastguard Worker return path 41*6777b538SAndroid Build Coastguard Worker if basename in ('javax', 'org', 'com'): 42*6777b538SAndroid Build Coastguard Worker return dirname 43*6777b538SAndroid Build Coastguard Worker path = dirname 44*6777b538SAndroid Build Coastguard Worker 45*6777b538SAndroid Build Coastguard Worker 46*6777b538SAndroid Build Coastguard Workerdef _ProcessSourceFile(output_dir, source_file_path, source_dirs): 47*6777b538SAndroid Build Coastguard Worker source_file_path = os.path.normpath(os.path.join(output_dir, 48*6777b538SAndroid Build Coastguard Worker source_file_path)) 49*6777b538SAndroid Build Coastguard Worker java_root = _GetJavaRoot(source_file_path) 50*6777b538SAndroid Build Coastguard Worker logging.debug('Extracted java root `%s` from source file path `%s`', 51*6777b538SAndroid Build Coastguard Worker java_root, source_file_path) 52*6777b538SAndroid Build Coastguard Worker source_dirs.add(java_root) 53*6777b538SAndroid Build Coastguard Worker 54*6777b538SAndroid Build Coastguard Worker 55*6777b538SAndroid Build Coastguard Workerdef _ProcessSourcesFile(output_dir, sources_file_path, source_dirs): 56*6777b538SAndroid Build Coastguard Worker for source_file_path in build_utils.ReadSourcesList( 57*6777b538SAndroid Build Coastguard Worker os.path.join(output_dir, sources_file_path)): 58*6777b538SAndroid Build Coastguard Worker _ProcessSourceFile(output_dir, source_file_path, source_dirs) 59*6777b538SAndroid Build Coastguard Worker 60*6777b538SAndroid Build Coastguard Worker 61*6777b538SAndroid Build Coastguard Workerdef _ProcessBuildConfigFile(output_dir, build_config_path, source_dirs, libs, 62*6777b538SAndroid Build Coastguard Worker already_processed_build_config_files, 63*6777b538SAndroid Build Coastguard Worker android_sdk_build_tools_version): 64*6777b538SAndroid Build Coastguard Worker if build_config_path in already_processed_build_config_files: 65*6777b538SAndroid Build Coastguard Worker return 66*6777b538SAndroid Build Coastguard Worker already_processed_build_config_files.add(build_config_path) 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Worker logging.info('Processing build config: %s', build_config_path) 69*6777b538SAndroid Build Coastguard Worker 70*6777b538SAndroid Build Coastguard Worker with open(os.path.join(output_dir, build_config_path)) as build_config_file: 71*6777b538SAndroid Build Coastguard Worker build_config = json.load(build_config_file) 72*6777b538SAndroid Build Coastguard Worker 73*6777b538SAndroid Build Coastguard Worker deps_info = build_config['deps_info'] 74*6777b538SAndroid Build Coastguard Worker target_sources_file = deps_info.get('target_sources_file') 75*6777b538SAndroid Build Coastguard Worker if target_sources_file is not None: 76*6777b538SAndroid Build Coastguard Worker _ProcessSourcesFile(output_dir, target_sources_file, source_dirs) 77*6777b538SAndroid Build Coastguard Worker else: 78*6777b538SAndroid Build Coastguard Worker unprocessed_jar_path = deps_info.get('unprocessed_jar_path') 79*6777b538SAndroid Build Coastguard Worker if unprocessed_jar_path is not None: 80*6777b538SAndroid Build Coastguard Worker lib_path = os.path.normpath(os.path.join(output_dir, 81*6777b538SAndroid Build Coastguard Worker unprocessed_jar_path)) 82*6777b538SAndroid Build Coastguard Worker logging.debug('Found lib `%s', lib_path) 83*6777b538SAndroid Build Coastguard Worker libs.add(lib_path) 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Worker source_dirs.add( 86*6777b538SAndroid Build Coastguard Worker os.path.join(output_dir, 87*6777b538SAndroid Build Coastguard Worker _WithoutSuffix(build_config_path, '.build_config.json'), 88*6777b538SAndroid Build Coastguard Worker 'generated_java', 'input_srcjars')) 89*6777b538SAndroid Build Coastguard Worker 90*6777b538SAndroid Build Coastguard Worker android = build_config.get('android') 91*6777b538SAndroid Build Coastguard Worker if android is not None: 92*6777b538SAndroid Build Coastguard Worker # This works around an issue where the language server complains about 93*6777b538SAndroid Build Coastguard Worker # `java.lang.invoke.LambdaMetafactory` not being found. The normal Android 94*6777b538SAndroid Build Coastguard Worker # build process is fine with this class being missing because d8 removes 95*6777b538SAndroid Build Coastguard Worker # references to LambdaMetafactory from the bytecode - see: 96*6777b538SAndroid Build Coastguard Worker # https://jakewharton.com/androids-java-8-support/#native-lambdas 97*6777b538SAndroid Build Coastguard Worker # When JDT builds the code, d8 doesn't run, so the references are still 98*6777b538SAndroid Build Coastguard Worker # there. Fortunately, the Android SDK provides a convenience JAR to fill 99*6777b538SAndroid Build Coastguard Worker # that gap in: 100*6777b538SAndroid Build Coastguard Worker # //third_party/android_sdk/public/build-tools/*/core-lambda-stubs.jar 101*6777b538SAndroid Build Coastguard Worker libs.add( 102*6777b538SAndroid Build Coastguard Worker os.path.normpath( 103*6777b538SAndroid Build Coastguard Worker os.path.join( 104*6777b538SAndroid Build Coastguard Worker output_dir, 105*6777b538SAndroid Build Coastguard Worker os.path.dirname(build_config['android']['sdk_jars'][0]), 106*6777b538SAndroid Build Coastguard Worker os.pardir, os.pardir, 'build-tools', 107*6777b538SAndroid Build Coastguard Worker android_sdk_build_tools_version, 'core-lambda-stubs.jar'))) 108*6777b538SAndroid Build Coastguard Worker 109*6777b538SAndroid Build Coastguard Worker for dep_config in deps_info['deps_configs']: 110*6777b538SAndroid Build Coastguard Worker _ProcessBuildConfigFile(output_dir, dep_config, source_dirs, libs, 111*6777b538SAndroid Build Coastguard Worker already_processed_build_config_files, 112*6777b538SAndroid Build Coastguard Worker android_sdk_build_tools_version) 113*6777b538SAndroid Build Coastguard Worker 114*6777b538SAndroid Build Coastguard Worker 115*6777b538SAndroid Build Coastguard Workerdef _GenerateClasspathEntry(kind, path): 116*6777b538SAndroid Build Coastguard Worker classpathentry = xml.etree.ElementTree.Element('classpathentry') 117*6777b538SAndroid Build Coastguard Worker classpathentry.set('kind', kind) 118*6777b538SAndroid Build Coastguard Worker classpathentry.set('path', f'_/{path}') 119*6777b538SAndroid Build Coastguard Worker return classpathentry 120*6777b538SAndroid Build Coastguard Worker 121*6777b538SAndroid Build Coastguard Worker 122*6777b538SAndroid Build Coastguard Workerdef _GenerateClasspathFile(source_dirs, libs): 123*6777b538SAndroid Build Coastguard Worker classpath = xml.etree.ElementTree.Element('classpath') 124*6777b538SAndroid Build Coastguard Worker for source_dir in source_dirs: 125*6777b538SAndroid Build Coastguard Worker classpath.append(_GenerateClasspathEntry('src', source_dir)) 126*6777b538SAndroid Build Coastguard Worker for lib in libs: 127*6777b538SAndroid Build Coastguard Worker classpath.append(_GenerateClasspathEntry('lib', lib)) 128*6777b538SAndroid Build Coastguard Worker 129*6777b538SAndroid Build Coastguard Worker xml.etree.ElementTree.ElementTree(classpath).write(sys.stdout, 130*6777b538SAndroid Build Coastguard Worker encoding='unicode') 131*6777b538SAndroid Build Coastguard Worker 132*6777b538SAndroid Build Coastguard Worker 133*6777b538SAndroid Build Coastguard Workerdef _ParseArguments(argv): 134*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 135*6777b538SAndroid Build Coastguard Worker description= 136*6777b538SAndroid Build Coastguard Worker 'Given Chromium Java build config files, dumps an Eclipse JDT classpath ' 137*6777b538SAndroid Build Coastguard Worker 'file to standard output that can be used with the "Language Support for ' 138*6777b538SAndroid Build Coastguard Worker 'Java™ by Red Hat" Visual Studio Code extension. See //docs/vscode.md ' 139*6777b538SAndroid Build Coastguard Worker 'for details.') 140*6777b538SAndroid Build Coastguard Worker parser.add_argument( 141*6777b538SAndroid Build Coastguard Worker '--output-dir', 142*6777b538SAndroid Build Coastguard Worker required=True, 143*6777b538SAndroid Build Coastguard Worker help='Relative path to the output directory, e.g. "out/Debug"') 144*6777b538SAndroid Build Coastguard Worker parser.add_argument( 145*6777b538SAndroid Build Coastguard Worker '--build-config', 146*6777b538SAndroid Build Coastguard Worker action='append', 147*6777b538SAndroid Build Coastguard Worker required=True, 148*6777b538SAndroid Build Coastguard Worker help='Path to the .build_config.json file to use as input, relative to ' 149*6777b538SAndroid Build Coastguard Worker '`--output-dir`. May be repeated.') 150*6777b538SAndroid Build Coastguard Worker return parser.parse_args(argv) 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Worker 153*6777b538SAndroid Build Coastguard Workerdef main(argv): 154*6777b538SAndroid Build Coastguard Worker build_utils.InitLogging('GENERATE_VSCODE_CLASSPATH_DEBUG') 155*6777b538SAndroid Build Coastguard Worker args = _ParseArguments(argv) 156*6777b538SAndroid Build Coastguard Worker output_dir = args.output_dir 157*6777b538SAndroid Build Coastguard Worker 158*6777b538SAndroid Build Coastguard Worker build_vars = gn_helpers.ReadBuildVars(output_dir) 159*6777b538SAndroid Build Coastguard Worker 160*6777b538SAndroid Build Coastguard Worker source_dirs = set() 161*6777b538SAndroid Build Coastguard Worker libs = set() 162*6777b538SAndroid Build Coastguard Worker already_processed_build_config_files = set() 163*6777b538SAndroid Build Coastguard Worker for build_config_path in args.build_config: 164*6777b538SAndroid Build Coastguard Worker _ProcessBuildConfigFile(output_dir, build_config_path, source_dirs, libs, 165*6777b538SAndroid Build Coastguard Worker already_processed_build_config_files, 166*6777b538SAndroid Build Coastguard Worker build_vars['android_sdk_build_tools_version']) 167*6777b538SAndroid Build Coastguard Worker 168*6777b538SAndroid Build Coastguard Worker logging.info('Done processing %d build config files', 169*6777b538SAndroid Build Coastguard Worker len(already_processed_build_config_files)) 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker _GenerateClasspathFile(source_dirs, libs) 172*6777b538SAndroid Build Coastguard Worker 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 175*6777b538SAndroid Build Coastguard Worker sys.exit(main(sys.argv[1:])) 176