1#!/usr/bin/env python3 2# 3# Copyright 2023 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import argparse 8import logging 9import os 10import shutil 11import sys 12import time 13 14import compile_java 15 16from util import build_utils 17import action_helpers # build_utils adds //build to sys.path. 18 19 20def _RunCompiler(args, 21 kotlinc_cmd, 22 source_files, 23 jar_path, 24 intermediates_out_dir=None): 25 """Runs the Kotlin compiler.""" 26 logging.info('Starting _RunCompiler') 27 28 source_files = source_files.copy() 29 kt_files = [f for f in source_files if f.endswith('.kt')] 30 assert len(kt_files) > 0, 'At least one .kt file must be passed in.' 31 32 java_srcjars = args.java_srcjars 33 34 # Use jar_path's directory to ensure paths are relative (needed for rbe). 35 temp_dir = jar_path + '.staging' 36 build_utils.DeleteDirectory(temp_dir) 37 os.makedirs(temp_dir) 38 try: 39 classes_dir = os.path.join(temp_dir, 'classes') 40 os.makedirs(classes_dir) 41 42 input_srcjars_dir = os.path.join(intermediates_out_dir or temp_dir, 43 'input_srcjars') 44 45 if java_srcjars: 46 logging.info('Extracting srcjars to %s', input_srcjars_dir) 47 build_utils.MakeDirectory(input_srcjars_dir) 48 for srcjar in args.java_srcjars: 49 source_files += build_utils.ExtractAll(srcjar, 50 no_clobber=True, 51 path=input_srcjars_dir, 52 pattern='*.java') 53 logging.info('Done extracting srcjars') 54 55 # Don't include the output directory in the initial set of args since it 56 # being in a temp dir makes it unstable (breaks md5 stamping). 57 cmd = list(kotlinc_cmd) 58 cmd += ['-d', classes_dir] 59 60 if args.classpath: 61 cmd += ['-classpath', ':'.join(args.classpath)] 62 63 # This a kotlinc plugin to generate header files for .kt files, similar to 64 # turbine for .java files. 65 jvm_abi_path = os.path.join(build_utils.KOTLIN_HOME, 'lib', 66 'jvm-abi-gen.jar') 67 cmd += [ 68 f'-Xplugin={jvm_abi_path}', '-P', 69 'plugin:org.jetbrains.kotlin.jvm.abi:outputDir=' + 70 args.interface_jar_path 71 ] 72 73 # Pass source paths as response files to avoid extremely long command 74 # lines that are tedius to debug. 75 source_files_rsp_path = os.path.join(temp_dir, 'files_list.txt') 76 with open(source_files_rsp_path, 'w') as f: 77 f.write(' '.join(source_files)) 78 cmd += ['@' + source_files_rsp_path] 79 80 # Explicitly set JAVA_HOME since some bots do not have this already set. 81 env = os.environ.copy() 82 env['JAVA_HOME'] = build_utils.JAVA_HOME 83 84 logging.debug('Build command %s', cmd) 85 start = time.time() 86 build_utils.CheckOutput(cmd, 87 env=env, 88 print_stdout=args.chromium_code, 89 fail_on_output=args.warnings_as_errors) 90 logging.info('Kotlin compilation took %ss', time.time() - start) 91 92 compile_java.CreateJarFile(jar_path, classes_dir) 93 94 logging.info('Completed all steps in _RunCompiler') 95 finally: 96 shutil.rmtree(temp_dir) 97 98 99def _ParseOptions(argv): 100 parser = argparse.ArgumentParser() 101 action_helpers.add_depfile_arg(parser) 102 103 parser.add_argument('--java-srcjars', 104 action='append', 105 default=[], 106 help='List of srcjars to include in compilation.') 107 parser.add_argument( 108 '--generated-dir', 109 help='Subdirectory within target_gen_dir to place extracted srcjars and ' 110 'annotation processor output for codesearch to find.') 111 parser.add_argument('--classpath', action='append', help='Classpath to use.') 112 parser.add_argument( 113 '--chromium-code', 114 action='store_true', 115 help='Whether code being compiled should be built with stricter ' 116 'warnings for chromium code.') 117 parser.add_argument('--warnings-as-errors', 118 action='store_true', 119 help='Treat all warnings as errors.') 120 parser.add_argument('--jar-path', help='Jar output path.', required=True) 121 parser.add_argument('--interface-jar-path', 122 help='Interface jar output path.', 123 required=True) 124 125 args, extra_args = parser.parse_known_args(argv) 126 127 args.classpath = action_helpers.parse_gn_list(args.classpath) 128 args.java_srcjars = action_helpers.parse_gn_list(args.java_srcjars) 129 130 source_files = [] 131 for arg in extra_args: 132 # Interpret a path prefixed with @ as a file containing a list of sources. 133 if arg.startswith('@'): 134 source_files.extend(build_utils.ReadSourcesList(arg[1:])) 135 else: 136 assert not arg.startswith('--'), f'Undefined option {arg}' 137 source_files.append(arg) 138 139 return args, source_files 140 141 142def main(argv): 143 build_utils.InitLogging('KOTLINC_DEBUG') 144 argv = build_utils.ExpandFileArgs(argv) 145 args, source_files = _ParseOptions(argv) 146 147 kotlinc_cmd = [build_utils.KOTLINC_PATH] 148 149 kotlinc_cmd += [ 150 '-no-jdk', # Avoid depending on the bundled JDK. 151 # Avoid depending on the bundled Kotlin stdlib. This may have a version 152 # skew with the one in //third_party/android_deps (which is the one we 153 # prefer to use). 154 '-no-stdlib', 155 # Avoid depending on the bundled Kotlin reflect libs. 156 '-no-reflect', 157 # We typically set a default of 1G for java commands, see 158 # build_utils.JavaCmd. This may help prevent OOMs. 159 '-J-Xmx1G', 160 ] 161 162 if args.generated_dir: 163 # Delete any stale files in the generated directory. The purpose of 164 # args.generated_dir is for codesearch. 165 shutil.rmtree(args.generated_dir, True) 166 167 _RunCompiler(args, 168 kotlinc_cmd, 169 source_files, 170 args.jar_path, 171 intermediates_out_dir=args.generated_dir) 172 173 if args.depfile: 174 # GN already knows of the source files, so avoid listing individual files 175 # in the depfile. 176 action_helpers.write_depfile(args.depfile, args.jar_path, args.classpath) 177 178 179if __name__ == '__main__': 180 sys.exit(main(sys.argv[1:])) 181