1#!/usr/bin/env python3 2# Copyright 2020 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5"""Wraps the turbine jar and expands @FileArgs.""" 6 7import argparse 8import functools 9import logging 10import sys 11import time 12import zipfile 13 14import compile_java 15import javac_output_processor 16from util import build_utils 17import action_helpers # build_utils adds //build to sys.path. 18import zip_helpers 19 20 21def ProcessJavacOutput(output, target_name): 22 output_processor = javac_output_processor.JavacOutputProcessor(target_name) 23 lines = output_processor.Process(output.split('\n')) 24 return '\n'.join(lines) 25 26 27def main(argv): 28 build_utils.InitLogging('TURBINE_DEBUG') 29 argv = build_utils.ExpandFileArgs(argv[1:]) 30 parser = argparse.ArgumentParser() 31 action_helpers.add_depfile_arg(parser) 32 parser.add_argument('--target-name', help='Fully qualified GN target name.') 33 parser.add_argument( 34 '--turbine-jar-path', required=True, help='Path to the turbine jar file.') 35 parser.add_argument( 36 '--java-srcjars', 37 action='append', 38 default=[], 39 help='List of srcjars to include in compilation.') 40 parser.add_argument('--classpath', action='append', help='Classpath to use.') 41 parser.add_argument( 42 '--processors', 43 action='append', 44 help='GN list of annotation processor main classes.') 45 parser.add_argument( 46 '--processorpath', 47 action='append', 48 help='GN list of jars that comprise the classpath used for Annotation ' 49 'Processors.') 50 parser.add_argument( 51 '--processor-args', 52 action='append', 53 help='key=value arguments for the annotation processors.') 54 parser.add_argument('--jar-path', help='Jar output path.', required=True) 55 parser.add_argument( 56 '--generated-jar-path', 57 required=True, 58 help='Output path for generated source files.') 59 parser.add_argument('--warnings-as-errors', 60 action='store_true', 61 help='Treat all warnings as errors.') 62 parser.add_argument('--kotlin-jar-path', 63 help='Kotlin jar to be merged into the output jar.') 64 options, unknown_args = parser.parse_known_args(argv) 65 66 options.classpath = action_helpers.parse_gn_list(options.classpath) 67 options.processorpath = action_helpers.parse_gn_list(options.processorpath) 68 options.processors = action_helpers.parse_gn_list(options.processors) 69 options.java_srcjars = action_helpers.parse_gn_list(options.java_srcjars) 70 71 files = [] 72 for arg in unknown_args: 73 # Interpret a path prefixed with @ as a file containing a list of sources. 74 if arg.startswith('@'): 75 files.extend(build_utils.ReadSourcesList(arg[1:])) 76 77 # The target's .sources file contains both Java and Kotlin files. We use 78 # compile_kt.py to compile the Kotlin files to .class and header jars. 79 # Turbine is run only on .java files. 80 java_files = [f for f in files if f.endswith('.java')] 81 82 cmd = build_utils.JavaCmd() + [ 83 '-classpath', options.turbine_jar_path, 'com.google.turbine.main.Main' 84 ] 85 javac_cmd = ['--release', '17'] 86 87 # Turbine reads lists from command line args by consuming args until one 88 # starts with double dash (--). Thus command line args should be grouped 89 # together and passed in together. 90 if options.processors: 91 cmd += ['--processors'] 92 cmd += options.processors 93 94 if options.processorpath: 95 cmd += ['--processorpath'] 96 cmd += options.processorpath 97 98 if options.processor_args: 99 for arg in options.processor_args: 100 javac_cmd.extend(['-A%s' % arg]) 101 102 if options.classpath: 103 cmd += ['--classpath'] 104 cmd += options.classpath 105 106 if options.java_srcjars: 107 cmd += ['--source_jars'] 108 cmd += options.java_srcjars 109 110 if java_files: 111 # Use jar_path to ensure paths are relative (needed for goma). 112 files_rsp_path = options.jar_path + '.java_files_list.txt' 113 with open(files_rsp_path, 'w') as f: 114 f.write('\n'.join(java_files)) 115 # Pass source paths as response files to avoid extremely long command 116 # lines that are tedius to debug. 117 cmd += ['--sources'] 118 cmd += ['@' + files_rsp_path] 119 120 cmd += ['--javacopts'] 121 cmd += javac_cmd 122 cmd += ['--'] # Terminate javacopts 123 124 # Use AtomicOutput so that output timestamps are not updated when outputs 125 # are not changed. 126 with action_helpers.atomic_output(options.jar_path) as output_jar, \ 127 action_helpers.atomic_output(options.generated_jar_path) as gensrc_jar: 128 cmd += ['--output', output_jar.name, '--gensrc_output', gensrc_jar.name] 129 process_javac_output_partial = functools.partial( 130 ProcessJavacOutput, target_name=options.target_name) 131 132 logging.debug('Command: %s', cmd) 133 start = time.time() 134 try: 135 build_utils.CheckOutput(cmd, 136 print_stdout=True, 137 stdout_filter=process_javac_output_partial, 138 stderr_filter=process_javac_output_partial, 139 fail_on_output=options.warnings_as_errors) 140 except build_utils.CalledProcessError as e: 141 # Do not output stacktrace as it takes up space on gerrit UI, forcing 142 # you to click though to find the actual compilation error. It's never 143 # interesting to see the Python stacktrace for a Java compilation error. 144 sys.stderr.write(e.output) 145 sys.exit(1) 146 end = time.time() - start 147 logging.info('Header compilation took %ss', end) 148 if options.kotlin_jar_path: 149 with zipfile.ZipFile(output_jar.name, 'a') as out_zip: 150 path_transform = lambda p: p if p.endswith('.class') else None 151 zip_helpers.merge_zips(out_zip, [options.kotlin_jar_path], 152 path_transform=path_transform) 153 154 if options.depfile: 155 # GN already knows of the java files, so avoid listing individual java files 156 # in the depfile. 157 depfile_deps = (options.classpath + options.processorpath + 158 options.java_srcjars) 159 action_helpers.write_depfile(options.depfile, options.jar_path, 160 depfile_deps) 161 162 163if __name__ == '__main__': 164 sys.exit(main(sys.argv)) 165