/*
* Copyright 2012-present Facebook, Inc.
*
* 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.facebook.buck.jvm.java;
import static com.facebook.buck.jvm.java.AbstractJavacOptions.SpoolMode;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.log.Logger;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.RuleKeyObjectSink;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.Tool;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import java.nio.file.Path;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class JavacToJarStepFactory extends BaseCompileToJarStepFactory {
private static final Logger LOG = Logger.get(JavacToJarStepFactory.class);
private final Javac javac;
private JavacOptions javacOptions;
private final JavacOptionsAmender amender;
public JavacToJarStepFactory(
Javac javac, JavacOptions javacOptions, JavacOptionsAmender amender) {
this.javac = javac;
this.javacOptions = javacOptions;
this.amender = amender;
}
public void setCompileAbi() {
javacOptions =
javacOptions
.withCompilationMode(Javac.CompilationMode.ABI)
.withAnnotationProcessingParams(
abiProcessorsOnly(javacOptions.getAnnotationProcessingParams()));
}
private AnnotationProcessingParams abiProcessorsOnly(
AnnotationProcessingParams annotationProcessingParams) {
Preconditions.checkArgument(annotationProcessingParams.getLegacyProcessors().isEmpty());
return annotationProcessingParams.withModernProcessors(
annotationProcessingParams
.getModernProcessors()
.stream()
.filter(processor -> !processor.getDoesNotAffectAbi())
.collect(Collectors.toList()));
}
@Override
public void createCompileStep(
BuildContext context,
ImmutableSortedSet<Path> sourceFilePaths,
BuildTarget invokingRule,
SourcePathResolver resolver,
SourcePathRuleFinder ruleFinder,
ProjectFilesystem filesystem,
ImmutableSortedSet<Path> declaredClasspathEntries,
Path outputDirectory,
Optional<Path> workingDirectory,
Path pathToSrcsList,
ClassUsageFileWriter usedClassesFileWriter,
ImmutableList.Builder<Step> steps,
BuildableContext buildableContext) {
final JavacOptions buildTimeOptions = amender.amend(javacOptions, context);
if (!javacOptions.getAnnotationProcessingParams().isEmpty()) {
// Javac requires that the root directory for generated sources already exist.
addAnnotationGenFolderStep(buildTimeOptions, filesystem, steps, buildableContext);
}
steps.add(
new JavacStep(
outputDirectory,
usedClassesFileWriter,
workingDirectory,
sourceFilePaths,
pathToSrcsList,
declaredClasspathEntries,
javac,
buildTimeOptions,
invokingRule,
resolver,
filesystem,
new ClasspathChecker(),
Optional.empty()));
}
@Override
Optional<String> getBootClasspath(BuildContext context) {
JavacOptions buildTimeOptions = amender.amend(javacOptions, context);
return buildTimeOptions.getBootclasspath();
}
@Override
protected Tool getCompiler() {
return javac;
}
@Override
public Iterable<BuildRule> getExtraDeps(SourcePathRuleFinder ruleFinder) {
// If any dep of an annotation processor changes, we need to recompile, so we add those as
// extra deps
return Iterables.concat(
super.getExtraDeps(ruleFinder),
ruleFinder.filterBuildRuleInputs(javacOptions.getAnnotationProcessingParams().getInputs()));
}
@Override
public void createCompileToJarStep(
BuildContext context,
ImmutableSortedSet<Path> sourceFilePaths,
BuildTarget invokingRule,
SourcePathResolver resolver,
SourcePathRuleFinder ruleFinder,
ProjectFilesystem filesystem,
ImmutableSortedSet<Path> declaredClasspathEntries,
Path outputDirectory,
Optional<Path> workingDirectory,
Path pathToSrcsList,
ImmutableList<String> postprocessClassesCommands,
ImmutableSortedSet<Path> entriesToJar,
Optional<String> mainClass,
Optional<Path> manifestFile,
Path outputJar,
ClassUsageFileWriter usedClassesFileWriter,
/* output params */
ImmutableList.Builder<Step> steps,
BuildableContext buildableContext,
ImmutableSet<Pattern> classesToRemoveFromJar) {
String spoolMode = javacOptions.getSpoolMode().name();
// In order to use direct spooling to the Jar:
// (1) It must be enabled through a .buckconfig.
// (2) The target must have 0 postprocessing steps.
// (3) Tha compile API must be JSR 199.
boolean isSpoolingToJarEnabled =
postprocessClassesCommands.isEmpty()
&& javacOptions.getSpoolMode() == AbstractJavacOptions.SpoolMode.DIRECT_TO_JAR
&& javac instanceof Jsr199Javac;
LOG.info(
"Target: %s SpoolMode: %s Expected SpoolMode: %s Postprocessing steps: %s",
invokingRule.getBaseName(),
(isSpoolingToJarEnabled) ? (SpoolMode.DIRECT_TO_JAR) : (SpoolMode.INTERMEDIATE_TO_DISK),
spoolMode,
postprocessClassesCommands.toString());
if (isSpoolingToJarEnabled) {
final JavacOptions buildTimeOptions = amender.amend(javacOptions, context);
// Javac requires that the root directory for generated sources already exists.
addAnnotationGenFolderStep(buildTimeOptions, filesystem, steps, buildableContext);
steps.add(
new JavacDirectToJarStep(
sourceFilePaths,
invokingRule,
resolver,
filesystem,
declaredClasspathEntries,
javac,
buildTimeOptions,
outputDirectory,
workingDirectory,
pathToSrcsList,
entriesToJar,
mainClass,
manifestFile,
outputJar,
usedClassesFileWriter));
} else {
super.createCompileToJarStep(
context,
sourceFilePaths,
invokingRule,
resolver,
ruleFinder,
filesystem,
declaredClasspathEntries,
outputDirectory,
workingDirectory,
pathToSrcsList,
postprocessClassesCommands,
entriesToJar,
mainClass,
manifestFile,
outputJar,
usedClassesFileWriter,
steps,
buildableContext,
javacOptions.getClassesToRemoveFromJar());
}
}
private static void addAnnotationGenFolderStep(
JavacOptions buildTimeOptions,
ProjectFilesystem filesystem,
ImmutableList.Builder<Step> steps,
BuildableContext buildableContext) {
Optional<Path> annotationGenFolder = buildTimeOptions.getGeneratedSourceFolderName();
if (annotationGenFolder.isPresent()) {
steps.addAll(MakeCleanDirectoryStep.of(filesystem, annotationGenFolder.get()));
buildableContext.recordArtifact(annotationGenFolder.get());
}
}
@Override
public void appendToRuleKey(RuleKeyObjectSink sink) {
sink.setReflectively("javac", javac);
sink.setReflectively("javacOptions", javacOptions);
sink.setReflectively("amender", amender);
}
@VisibleForTesting
public JavacOptions getJavacOptions() {
return javacOptions;
}
}