/* * Copyright 2015-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 com.facebook.buck.io.ProjectFilesystem; 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.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.Tool; import com.facebook.buck.shell.BashStep; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.Step; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; /** Provides a base implementation for post compile steps. */ public abstract class BaseCompileToJarStepFactory implements CompileToJarStepFactory { public static final Function<BuildContext, Iterable<Path>> EMPTY_EXTRA_CLASSPATH = input -> ImmutableList.of(); @Override public Iterable<BuildRule> getDeclaredDeps(SourcePathRuleFinder ruleFinder) { return ImmutableList.of(); } @Override public Iterable<BuildRule> getExtraDeps(SourcePathRuleFinder ruleFinder) { return getCompiler().getDeps(ruleFinder); } protected abstract Tool getCompiler(); @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) { createCompileStep( context, sourceFilePaths, invokingRule, resolver, ruleFinder, filesystem, declaredClasspathEntries, outputDirectory, workingDirectory, pathToSrcsList, usedClassesFileWriter, steps, buildableContext); steps.addAll( Lists.newCopyOnWriteArrayList( addPostprocessClassesCommands( filesystem, postprocessClassesCommands, outputDirectory, declaredClasspathEntries, getBootClasspath(context)))); steps.add( new JarDirectoryStep( filesystem, outputJar, ImmutableSortedSet.of(outputDirectory), mainClass.orElse(null), manifestFile.orElse(null), true, classesToRemoveFromJar)); } /** * This can be used make the bootclasspath if available, to the postprocess classes commands. * * @return the bootclasspath. */ @SuppressWarnings("unused") Optional<String> getBootClasspath(BuildContext context) { return Optional.empty(); } /** * Adds a BashStep for each postprocessClasses command that runs the command followed by the * outputDirectory of javac outputs. * * <p>The expectation is that the command will inspect and update the directory by modifying, * adding, and deleting the .class files in the directory. * * <p>The outputDirectory should be a valid java root. I.e., if outputDirectory is * buck-out/bin/java/abc/lib__abc__classes/, then a contained class abc.AbcModule should be at * buck-out/bin/java/abc/lib__abc__classes/abc/AbcModule.class * * @param filesystem the project filesystem. * @param postprocessClassesCommands the list of commands to post-process .class files. * @param outputDirectory the directory that will contain all the javac output. * @param declaredClasspathEntries the list of classpath entries. * @param bootClasspath the compilation boot classpath. */ @VisibleForTesting static ImmutableList<Step> addPostprocessClassesCommands( ProjectFilesystem filesystem, List<String> postprocessClassesCommands, Path outputDirectory, ImmutableSortedSet<Path> declaredClasspathEntries, Optional<String> bootClasspath) { if (postprocessClassesCommands.isEmpty()) { return ImmutableList.of(); } ImmutableList.Builder<Step> commands = new ImmutableList.Builder<Step>(); ImmutableMap.Builder<String, String> envVarBuilder = ImmutableMap.builder(); envVarBuilder.put( "COMPILATION_CLASSPATH", Joiner.on(':').join(Iterables.transform(declaredClasspathEntries, filesystem::resolve))); if (bootClasspath.isPresent()) { envVarBuilder.put("COMPILATION_BOOTCLASSPATH", bootClasspath.get()); } ImmutableMap<String, String> envVars = envVarBuilder.build(); for (final String postprocessClassesCommand : postprocessClassesCommands) { BashStep bashStep = new BashStep( filesystem.getRootPath(), postprocessClassesCommand + " " + outputDirectory) { @Override public ImmutableMap<String, String> getEnvironmentVariables(ExecutionContext context) { return envVars; } }; commands.add(bashStep); } return commands.build(); } }