/* * 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.rules.BuildableProperties.Kind.PACKAGING; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.rules.AbstractBuildRule; import com.facebook.buck.rules.AddToRuleKey; import com.facebook.buck.rules.BinaryBuildRule; import com.facebook.buck.rules.BuildContext; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildableContext; import com.facebook.buck.rules.BuildableProperties; import com.facebook.buck.rules.CommandTool; import com.facebook.buck.rules.ExplicitBuildTargetSourcePath; import com.facebook.buck.rules.PathSourcePath; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.Tool; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.step.Step; import com.facebook.buck.step.fs.MakeCleanDirectoryStep; import com.facebook.buck.step.fs.MkdirStep; import com.facebook.buck.step.fs.SymlinkFileStep; 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 java.nio.file.Path; import java.nio.file.Paths; import java.util.regex.Pattern; import javax.annotation.Nullable; @BuildsAnnotationProcessor public class JavaBinary extends AbstractBuildRule implements BinaryBuildRule, HasClasspathEntries { private static final BuildableProperties OUTPUT_TYPE = new BuildableProperties(PACKAGING); @AddToRuleKey private final JavaRuntimeLauncher javaRuntimeLauncher; @AddToRuleKey @Nullable private final String mainClass; @AddToRuleKey @Nullable private final SourcePath manifestFile; private final boolean mergeManifests; @Nullable @AddToRuleKey private final SourcePath metaInfDirectory; @AddToRuleKey private final ImmutableSet<Pattern> blacklist; private final ImmutableSet<JavaLibrary> transitiveClasspathDeps; private final ImmutableSet<SourcePath> transitiveClasspaths; private final boolean cache; public JavaBinary( BuildRuleParams params, JavaRuntimeLauncher javaRuntimeLauncher, @Nullable String mainClass, @Nullable SourcePath manifestFile, boolean mergeManifests, @Nullable Path metaInfDirectory, ImmutableSet<Pattern> blacklist, ImmutableSet<JavaLibrary> transitiveClasspathDeps, ImmutableSet<SourcePath> transitiveClasspaths, boolean cache) { super(params); this.javaRuntimeLauncher = javaRuntimeLauncher; this.mainClass = mainClass; this.manifestFile = manifestFile; this.mergeManifests = mergeManifests; this.metaInfDirectory = metaInfDirectory != null ? new PathSourcePath(getProjectFilesystem(), metaInfDirectory) : null; this.blacklist = blacklist; this.transitiveClasspathDeps = transitiveClasspathDeps; this.transitiveClasspaths = transitiveClasspaths; this.cache = cache; } @Override public BuildableProperties getProperties() { return OUTPUT_TYPE; } @Override public ImmutableList<Step> getBuildSteps( BuildContext context, BuildableContext buildableContext) { ImmutableList.Builder<Step> commands = ImmutableList.builder(); Path outputDirectory = getOutputDirectory(); Step mkdir = MkdirStep.of(getProjectFilesystem(), outputDirectory); commands.add(mkdir); ImmutableSortedSet<Path> includePaths; if (metaInfDirectory != null) { Path stagingRoot = outputDirectory.resolve("meta_inf_staging"); Path stagingTarget = stagingRoot.resolve("META-INF"); commands.addAll(MakeCleanDirectoryStep.of(getProjectFilesystem(), stagingRoot)); commands.add( SymlinkFileStep.builder() .setFilesystem(getProjectFilesystem()) .setExistingFile(context.getSourcePathResolver().getAbsolutePath(metaInfDirectory)) .setDesiredLink(stagingTarget) .build()); includePaths = ImmutableSortedSet.<Path>naturalOrder() .add(stagingRoot) .addAll( context.getSourcePathResolver().getAllAbsolutePaths(getTransitiveClasspaths())) .build(); } else { includePaths = context.getSourcePathResolver().getAllAbsolutePaths(getTransitiveClasspaths()); } Path outputFile = context.getSourcePathResolver().getRelativePath(getSourcePathToOutput()); Path manifestPath = manifestFile == null ? null : context.getSourcePathResolver().getAbsolutePath(manifestFile); Step jar = new JarDirectoryStep( getProjectFilesystem(), outputFile, includePaths, mainClass, manifestPath, mergeManifests, blacklist); commands.add(jar); buildableContext.recordArtifact(outputFile); return commands.build(); } @Override public ImmutableSet<SourcePath> getTransitiveClasspaths() { return transitiveClasspaths; } @Override public ImmutableSet<JavaLibrary> getTransitiveClasspathDeps() { return transitiveClasspathDeps; } @Override public ImmutableSet<SourcePath> getImmediateClasspaths() { return ImmutableSet.of(); } @Override public ImmutableSet<SourcePath> getOutputClasspaths() { // A binary has no exported deps or classpath contributions of its own return ImmutableSet.of(); } private Path getOutputDirectory() { return BuildTargets.getGenPath(getProjectFilesystem(), getBuildTarget(), "%s").getParent(); } @Override public SourcePath getSourcePathToOutput() { return new ExplicitBuildTargetSourcePath( getBuildTarget(), Paths.get( String.format( "%s/%s.jar", getOutputDirectory(), getBuildTarget().getShortNameAndFlavorPostfix()))); } @Override public Tool getExecutableCommand() { Preconditions.checkState( mainClass != null, "Must specify a main class for %s in order to to run it.", getBuildTarget()); return new CommandTool.Builder() .addArg(javaRuntimeLauncher.getCommand()) .addArg("-jar") .addArg(SourcePathArg.of(getSourcePathToOutput())) .build(); } @Override public boolean isCacheable() { return cache; } }