diff options
Diffstat (limited to 'src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java')
-rw-r--r-- | src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java new file mode 100644 index 0000000000..9b899e50ef --- /dev/null +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java @@ -0,0 +1,516 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.buildjar; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; +import com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * All the information needed to perform a single Java library build operation. + */ +public final class JavaLibraryBuildRequest { + private boolean compressJar; + + private final List<String> sourceFiles; + private final ImmutableList<String> sourceJars; + private final ImmutableList<String> messageFiles; + private final ImmutableList<String> resourceFiles; + private final ImmutableList<String> resourceJars; + /** Resource files that should be put in the root of the output jar. */ + private final ImmutableList<String> rootResourceFiles; + + private final String classPath; + + private final String processorPath; + private final List<String> processorNames; + + private final String classDir; + private final String tempDir; + + private final String outputJar; + + // Post processors + private final ImmutableList<AbstractPostProcessor> postProcessors; + + private final ImmutableList<String> javacOpts; + + /** + * Where to store source files generated by annotation processors. + */ + private final String sourceGenDir; + + /** + * The path to an output jar for source files generated by annotation processors. + */ + private final String generatedSourcesOutputJar; + + /** + * Repository for all dependency-related information. + */ + private final DependencyModule dependencyModule; + + /** + * List of plugins that are given to javac. + */ + private final ImmutableList<BlazeJavaCompilerPlugin> plugins; + + private JavaLibraryBuildRequest( + boolean compressJar, + List<String> sourceFiles, ImmutableList<String> sourceJars, + ImmutableList<String> messageFiles, ImmutableList<String> resourceFiles, + ImmutableList<String> resourceJars, ImmutableList<String> rootResourceFiles, + String classPath, String processorPath, List<String> processorNames, String classDir, + String tempDir, String outputJar, ImmutableList<AbstractPostProcessor> postProcessors, + ImmutableList<String> javacOpts, String sourceGenDir, String generatedSourcesOutputJar, + DependencyModule dependencyModule, ImmutableList<BlazeJavaCompilerPlugin> plugins) { + this.compressJar = compressJar; + this.sourceFiles = sourceFiles; + this.sourceJars = sourceJars; + this.messageFiles = messageFiles; + this.resourceFiles = resourceFiles; + this.resourceJars = resourceJars; + this.rootResourceFiles = rootResourceFiles; + this.classPath = classPath; + this.processorPath = processorPath; + this.processorNames = processorNames; + this.classDir = classDir; + this.tempDir = tempDir; + this.outputJar = outputJar; + this.postProcessors = postProcessors; + this.javacOpts = javacOpts; + this.sourceGenDir = sourceGenDir; + this.generatedSourcesOutputJar = generatedSourcesOutputJar; + this.dependencyModule = dependencyModule; + this.plugins = plugins; + } + + public boolean compressJar() { + return compressJar; + } + + public List<String> getSourceFiles() { + // TODO(bazel-team): This is being modified after parsing to add files from source jars. + return sourceFiles; + } + + public ImmutableList<String> getSourceJars() { + return sourceJars; + } + + public ImmutableList<String> getMessageFiles() { + return messageFiles; + } + + public ImmutableList<String> getResourceFiles() { + return resourceFiles; + } + + public ImmutableList<String> getResourceJars() { + return resourceJars; + } + + public ImmutableList<String> getRootResourceFiles() { + return rootResourceFiles; + } + + public String getClassPath() { + return classPath; + } + + public String getProcessorPath() { + return processorPath; + } + + public List<String> getProcessors() { + // TODO(bazel-team): This might be modified by a JavaLibraryBuilder to enable specific + // annotation processors. + return processorNames; + } + + public String getClassDir() { + return classDir; + } + + public String getTempDir() { + return tempDir; + } + + public String getOutputJar() { + return outputJar; + } + + public ImmutableList<String> getJavacOpts() { + return javacOpts; + } + + public String getSourceGenDir() { + return sourceGenDir; + } + + public String getGeneratedSourcesOutputJar() { + return generatedSourcesOutputJar; + } + + public ImmutableList<AbstractPostProcessor> getPostProcessors() { + return postProcessors; + } + + public ImmutableList<BlazeJavaCompilerPlugin> getPlugins() { + return plugins; + } + + public DependencyModule getDependencyModule() { + return dependencyModule; + } + + /** + * Parses the list of arguments into a {@link JavaLibraryBuildRequest}. The returned + * {@link JavaLibraryBuildRequest} object can be then used to configure the compilation itself. + * + * @throws IOException if the argument list contains file (with the @ prefix) and reading that + * file failed. + * @throws InvalidCommandLineException on any command line error + */ + public static JavaLibraryBuildRequest parse(List<String> args) throws IOException, + InvalidCommandLineException { + return new JavaLibraryBuildRequest.Builder(args).build(); + } + + /** + * Builds a {@link JavaLibraryBuildRequest}. + */ + public static final class Builder { + private boolean compressJar; + + private final ImmutableList.Builder<String> sourceFiles = ImmutableList.builder(); + private final ImmutableList.Builder<String> sourceJars = ImmutableList.builder(); + private final ImmutableList.Builder<String> messageFiles = ImmutableList.builder(); + private final ImmutableList.Builder<String> resourceFiles = ImmutableList.builder(); + private final ImmutableList.Builder<String> resourceJars = ImmutableList.builder(); + private final ImmutableList.Builder<String> rootResourceFiles = ImmutableList.builder(); + + private String classPath; + + private String processorPath = ""; + private final List<String> processorNames = new ArrayList<>(); + + // Since the default behavior of this tool with no arguments is + // "rm -fr <classDir>", let's not default to ".", shall we? + private String classDir = "classes"; + private String tempDir = "_tmp"; + + private String outputJar; + + private final ImmutableList.Builder<AbstractPostProcessor> postProcessors = + ImmutableList.builder(); + + private String ruleKind; + private String targetLabel; + + private ImmutableList.Builder<String> javacOpts = ImmutableList.builder(); + + private String sourceGenDir; + + private String generatedSourcesOutputJar; + + private final DependencyModule dependencyModule; + + private final ImmutableList.Builder<BlazeJavaCompilerPlugin> plugins = ImmutableList.builder(); + + /** + * Constructs a build from a list of command args. Sets the same JavacRunner + * for both compilation and annotation processing. + * + * @param args the list of command line args + * @throws InvalidCommandLineException on any command line error + */ + public Builder(List<String> args) throws InvalidCommandLineException, IOException { + dependencyModule = processCommandlineArgs(expandArguments(args)); + plugins.add(dependencyModule.getPlugin()); + } + + /** + * Constructs a build from a list of command args. Sets the same JavacRunner + * for both compilation and annotation processing. + * + * @param args the list of command line args + * @param extraPlugins extraneous plugins to use in addition to the strict dependency module. + * @throws InvalidCommandLineException on any command line error + */ + public Builder(List<String> args, List<BlazeJavaCompilerPlugin> extraPlugins) + throws InvalidCommandLineException, IOException { + this(args); + plugins.addAll(extraPlugins); + } + + public ImmutableList<String> getJavacOpts() { + return javacOpts.build(); + } + + public void setJavacOpts(List<String> javacOpts) { + this.javacOpts = ImmutableList.<String>builder().addAll(javacOpts); + } + + public JavaLibraryBuildRequest build() { + ArrayList<String> sourceFiles = new ArrayList<>(this.sourceFiles.build()); + ImmutableList<String> sourceJars = this.sourceJars.build(); + ImmutableList<String> messageFiles = this.messageFiles.build(); + ImmutableList<String> resourceFiles = this.resourceFiles.build(); + ImmutableList<String> resourceJars = this.resourceJars.build(); + ImmutableList<String> rootResourceFiles = this.rootResourceFiles.build(); + ImmutableList<AbstractPostProcessor> postProcessors = this.postProcessors.build(); + ImmutableList<String> javacOpts = this.javacOpts.build(); + ImmutableList<BlazeJavaCompilerPlugin> plugins = this.plugins.build(); + return new JavaLibraryBuildRequest(compressJar, sourceFiles, sourceJars, messageFiles, + resourceFiles, resourceJars, rootResourceFiles, classPath, processorPath, processorNames, + classDir, tempDir, outputJar, postProcessors, javacOpts, sourceGenDir, + generatedSourcesOutputJar, dependencyModule, plugins); + } + + /** + * Processes the command line arguments. + * + * @throws InvalidCommandLineException on an invalid option being passed. + */ + private DependencyModule processCommandlineArgs(Deque<String> argQueue) + throws InvalidCommandLineException { + DependencyModule.Builder builder = new DependencyModule.Builder(); + for (String arg = argQueue.pollFirst(); arg != null; arg = argQueue.pollFirst()) { + switch (arg) { + case "--javacopts": + // Collect additional arguments to javac. + // Assumes that javac options do not start with "--". + // otherwise we have to do something like adding a "--" + // terminator to the passed arguments. + collectFlagArguments(javacOpts, argQueue, "--"); + break; + case "--direct_dependency": { + String jar = getArgument(argQueue, arg); + String target = getArgument(argQueue, arg); + builder.addDirectMapping(jar, target); + break; + } + case "--indirect_dependency": { + String jar = getArgument(argQueue, arg); + String target = getArgument(argQueue, arg); + builder.addIndirectMapping(jar, target); + break; + } + case "--strict_java_deps": + builder.setStrictJavaDeps(getArgument(argQueue, arg)); + break; + case "--output_deps": + builder.setOutputDepsFile(getArgument(argQueue, arg)); + break; + case "--output_deps_proto": + builder.setOutputDepsProtoFile(getArgument(argQueue, arg)); + break; + case "--deps_artifacts": + ImmutableList.Builder<String> depsArtifacts = ImmutableList.builder(); + collectFlagArguments(depsArtifacts, argQueue, "--"); + builder.addDepsArtifacts(depsArtifacts.build()); + break; + case "--reduce_classpath": + builder.setReduceClasspath(); + break; + case "--sourcegendir": + sourceGenDir = getArgument(argQueue, arg); + break; + case "--generated_sources_output": + generatedSourcesOutputJar = getArgument(argQueue, arg); + break; + default: + processArg(arg, argQueue); + } + } + builder.setRuleKind(ruleKind); + builder.setTargetLabel(targetLabel); + return builder.build(); + } + + /** + * Pre-processes an argument list, expanding options &at;filename to read in + * the content of the file and add it to the list of arguments. + * + * @param args the List of arguments to pre-process. + * @return the List of pre-processed arguments. + * @throws IOException if one of the files containing options cannot be read. + */ + private static Deque<String> expandArguments(List<String> args) throws IOException { + Deque<String> expanded = new ArrayDeque<>(args.size()); + for (String arg : args) { + expandArgument(expanded, arg); + } + return expanded; + } + + /** + * Expands a single argument, expanding options &at;filename to read in the content of the file + * and add it to the list of processed arguments. The &at; itself can be escaped with &at;&at;. + * + * @param arg the argument to pre-process. + * @return the list of pre-processed arguments. + * @throws IOException if one of the files containing options cannot be read. + */ + private static void expandArgument(Deque<String> expanded, String arg) + throws IOException { + if (arg.startsWith("@") && !arg.startsWith("@@")) { + for (String line : Files.readAllLines(Paths.get(arg.substring(1)), UTF_8)) { + if (line.length() > 0) { + expandArgument(expanded, line); + } + } + } else { + expanded.add(arg); + } + } + + /** + * Collects the arguments for a command line flag until it finds a flag that starts with the + * terminatorPrefix. + * + * @param output where to put the collected flag arguments. + * @param args + * @param terminatorPrefix the terminator prefix to stop collecting of argument flags. + */ + private static void collectFlagArguments( + ImmutableList.Builder<String> output, Deque<String> args, String terminatorPrefix) { + for (String arg = args.pollFirst(); arg != null; arg = args.pollFirst()) { + if (arg.startsWith(terminatorPrefix)) { + args.addFirst(arg); + break; + } + output.add(arg); + } + } + + /** + * Collects the arguments for the --processors command line flag until it finds a flag that + * starts with the terminatorPrefix. + * + * @param output where to put the collected flag arguments. + * @param args + * @param terminatorPrefix the terminator prefix to stop collecting of argument flags. + */ + private static void collectProcessorArguments( + List<String> output, Deque<String> args, String terminatorPrefix) + throws InvalidCommandLineException { + for (String arg = args.pollFirst(); arg != null; arg = args.pollFirst()) { + if (arg.startsWith(terminatorPrefix)) { + args.addFirst(arg); + break; + } + if (arg.contains(",")) { + throw new InvalidCommandLineException("processor argument may not contain commas: " + + arg); + } + output.add(arg); + } + } + + private static String getArgument(Deque<String> args, String arg) + throws InvalidCommandLineException { + try { + return args.remove(); + } catch (NoSuchElementException e) { + throw new InvalidCommandLineException(arg + ": missing argument"); + } + } + + private void processArg(String arg, Deque<String> args) + throws InvalidCommandLineException { + switch (arg) { + case "--sources": + collectFlagArguments(sourceFiles, args, "-"); + break; + case "--source_jars": + collectFlagArguments(sourceJars, args, "-"); + break; + case "--messages": + collectFlagArguments(messageFiles, args, "-"); + break; + case "--resources": + collectFlagArguments(resourceFiles, args, "-"); + break; + case "--resource_jars": + collectFlagArguments(resourceJars, args, "-"); + break; + case "--classpath_resources": + collectFlagArguments(rootResourceFiles, args, "-"); + break; + case "--classpath": + classPath = getArgument(args, arg); + break; + case "--processorpath": + processorPath = getArgument(args, arg); + break; + case "--processors": + collectProcessorArguments(processorNames, args, "-"); + break; + case "--output": + outputJar = getArgument(args, arg); + break; + case "--classdir": + classDir = getArgument(args, arg); + break; + case "--tempdir": + tempDir = getArgument(args, arg); + break; + case "--gendir": + // TODO(bazel-team) - remove when Bazel no longer passes this flag to buildjar. + getArgument(args, arg); + break; + case "--post_processor": + addExternalPostProcessor(postProcessors, args, arg); + break; + case "--compress_jar": + compressJar = true; + break; + case "--rule_kind": + ruleKind = getArgument(args, arg); + break; + case "--target_label": + targetLabel = getArgument(args, arg); + break; + default: + throw new InvalidCommandLineException("unknown option : '" + arg + "'"); + } + } + + private void addExternalPostProcessor(ImmutableList.Builder<AbstractPostProcessor> output, + Deque<String> args, String arg) throws InvalidCommandLineException { + String processorName = getArgument(args, arg); + ImmutableList.Builder<String> arguments = ImmutableList.builder(); + collectFlagArguments(arguments, args, "--"); + // TODO(bazel-team): there is no check than the same post processor is not added twice. + // We should either forbid multiple add of the same post processor or use a processor factory + // to allow multiple add of the same post processor. Anyway, this binary is invoked by Blaze + // and not manually. + output.add(AbstractPostProcessor.create(processorName, arguments.build())); + } + } +} |