/*
* 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.LIBRARY;
import com.facebook.buck.android.AndroidPackageable;
import com.facebook.buck.android.AndroidPackageableCollector;
import com.facebook.buck.event.ConsoleEvent;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildRuleWithResolver;
import com.facebook.buck.rules.AddToRuleKey;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildOutputInitializer;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.BuildableProperties;
import com.facebook.buck.rules.ExplicitBuildTargetSourcePath;
import com.facebook.buck.rules.ExportDependencies;
import com.facebook.buck.rules.InitializableFromDisk;
import com.facebook.buck.rules.OnDiskBuildInfo;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.keys.SupportsInputBasedRuleKey;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.CopyStep;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.step.fs.MkdirStep;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.hash.HashCode;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
@BuildsAnnotationProcessor
public class PrebuiltJar extends AbstractBuildRuleWithResolver
implements AndroidPackageable,
ExportDependencies,
HasClasspathEntries,
InitializableFromDisk<JavaLibrary.Data>,
JavaLibrary,
SupportsInputBasedRuleKey {
private static final BuildableProperties OUTPUT_TYPE = new BuildableProperties(LIBRARY);
@AddToRuleKey private final SourcePath binaryJar;
private final JarContentsSupplier binaryJarContentsSupplier;
private final Path copiedBinaryJar;
@AddToRuleKey private final Optional<SourcePath> sourceJar;
@SuppressWarnings("PMD.UnusedPrivateField")
@AddToRuleKey
private final Optional<SourcePath> gwtJar;
@AddToRuleKey private final Optional<String> javadocUrl;
@AddToRuleKey private final Optional<String> mavenCoords;
@AddToRuleKey private final boolean provided;
private final Supplier<ImmutableSet<SourcePath>> transitiveClasspathsSupplier;
private final Supplier<ImmutableSet<JavaLibrary>> transitiveClasspathDepsSupplier;
private final BuildOutputInitializer<Data> buildOutputInitializer;
public PrebuiltJar(
BuildRuleParams params,
SourcePathResolver resolver,
SourcePath binaryJar,
Optional<SourcePath> sourceJar,
Optional<SourcePath> gwtJar,
Optional<String> javadocUrl,
Optional<String> mavenCoords,
final boolean provided) {
super(params, resolver);
this.binaryJar = binaryJar;
this.sourceJar = sourceJar;
this.gwtJar = gwtJar;
this.javadocUrl = javadocUrl;
this.mavenCoords = mavenCoords;
this.provided = provided;
transitiveClasspathsSupplier =
Suppliers.memoize(
() ->
JavaLibraryClasspathProvider.getClasspathsFromLibraries(
getTransitiveClasspathDeps()));
this.transitiveClasspathDepsSupplier =
Suppliers.memoize(
() -> {
if (provided) {
return JavaLibraryClasspathProvider.getClasspathDeps(
PrebuiltJar.this.getDeclaredDeps());
}
return ImmutableSet.<JavaLibrary>builder()
.add(PrebuiltJar.this)
.addAll(
JavaLibraryClasspathProvider.getClasspathDeps(
PrebuiltJar.this.getDeclaredDeps()))
.build();
});
Path fileName = resolver.getRelativePath(binaryJar).getFileName();
String fileNameWithJarExtension =
String.format("%s.jar", MorePaths.getNameWithoutExtension(fileName));
copiedBinaryJar =
BuildTargets.getGenPath(
getProjectFilesystem(), getBuildTarget(), "__%s__/" + fileNameWithJarExtension);
this.binaryJarContentsSupplier = new JarContentsSupplier(resolver, getSourcePathToOutput());
buildOutputInitializer = new BuildOutputInitializer<>(params.getBuildTarget(), this);
}
@Override
public BuildableProperties getProperties() {
return OUTPUT_TYPE;
}
public Optional<SourcePath> getSourceJar() {
return sourceJar;
}
public Optional<String> getJavadocUrl() {
return javadocUrl;
}
@Override
public ImmutableSortedMap<String, HashCode> getClassNamesToHashes() {
return buildOutputInitializer.getBuildOutput().getClassNamesToHashes();
}
@Override
public JavaLibrary.Data initializeFromDisk(OnDiskBuildInfo onDiskBuildInfo) throws IOException {
// Warm up the jar contents. We just wrote the thing, so it should be in the filesystem cache
binaryJarContentsSupplier.load();
return JavaLibraryRules.initializeFromDisk(
getBuildTarget(), getProjectFilesystem(), onDiskBuildInfo);
}
@Override
public BuildOutputInitializer<Data> getBuildOutputInitializer() {
return buildOutputInitializer;
}
@Override
public Set<BuildRule> getDepsForTransitiveClasspathEntries() {
return getBuildDeps();
}
@Override
public ImmutableSet<SourcePath> getTransitiveClasspaths() {
return transitiveClasspathsSupplier.get();
}
@Override
public ImmutableSet<JavaLibrary> getTransitiveClasspathDeps() {
return transitiveClasspathDepsSupplier.get();
}
@Override
public ImmutableSet<SourcePath> getImmediateClasspaths() {
if (!provided) {
return ImmutableSet.of(getSourcePathToOutput());
} else {
return ImmutableSet.of();
}
}
@Override
public ImmutableSet<SourcePath> getOutputClasspaths() {
return ImmutableSet.of(getSourcePathToOutput());
}
@Override
public ImmutableSortedSet<SourcePath> getJavaSrcs() {
return ImmutableSortedSet.of();
}
@Override
public ImmutableSortedSet<SourcePath> getSources() {
return ImmutableSortedSet.of();
}
@Override
public ImmutableSortedSet<SourcePath> getResources() {
return ImmutableSortedSet.of();
}
@Override
public ImmutableSortedSet<BuildRule> getExportedDeps() {
return getDeclaredDeps();
}
@Override
public Optional<Path> getGeneratedSourcePath() {
return Optional.empty();
}
@Override
public ImmutableList<Step> getBuildSteps(
BuildContext context, final BuildableContext buildableContext) {
ImmutableList.Builder<Step> steps = ImmutableList.builder();
SourcePathResolver resolver = context.getSourcePathResolver();
// Create a copy of the JAR in case it was generated by another rule.
Path resolvedBinaryJar = resolver.getAbsolutePath(binaryJar);
Path resolvedCopiedBinaryJar = getProjectFilesystem().resolve(copiedBinaryJar);
Preconditions.checkState(
!resolvedBinaryJar.equals(resolvedCopiedBinaryJar),
"%s: source (%s) can't be equal to destination (%s) when copying prebuilt JAR.",
getBuildTarget().getFullyQualifiedName(),
resolvedBinaryJar,
copiedBinaryJar);
if (resolver.getFilesystem(binaryJar).isDirectory(resolvedBinaryJar)) {
steps.addAll(MakeCleanDirectoryStep.of(getProjectFilesystem(), copiedBinaryJar));
steps.add(
CopyStep.forDirectory(
getProjectFilesystem(),
resolvedBinaryJar,
copiedBinaryJar,
CopyStep.DirectoryMode.CONTENTS_ONLY));
} else {
if (!MorePaths.getFileExtension(copiedBinaryJar.getFileName())
.equals(MorePaths.getFileExtension(resolvedBinaryJar))) {
context
.getEventBus()
.post(
ConsoleEvent.warning(
"Assuming %s is a JAR and renaming to %s in %s. "
+ "Change the extension of the binary_jar to '.jar' to remove this warning.",
resolvedBinaryJar.getFileName(),
copiedBinaryJar.getFileName(),
getBuildTarget().getFullyQualifiedName()));
}
steps.add(MkdirStep.of(getProjectFilesystem(), copiedBinaryJar.getParent()));
steps.add(CopyStep.forFile(getProjectFilesystem(), resolvedBinaryJar, copiedBinaryJar));
}
buildableContext.recordArtifact(copiedBinaryJar);
JavaLibraryRules.addAccumulateClassNamesStep(
this, buildableContext, context.getSourcePathResolver(), steps);
return steps.build();
}
@Override
public Iterable<AndroidPackageable> getRequiredPackageables() {
return AndroidPackageableCollector.getPackageableRules(getDeclaredDeps());
}
@Override
public void addToCollector(AndroidPackageableCollector collector) {
if (!provided) {
collector.addClasspathEntry(this, getSourcePathToOutput());
collector.addPathToThirdPartyJar(getBuildTarget(), getSourcePathToOutput());
}
}
@Override
public SourcePath getSourcePathToOutput() {
return new ExplicitBuildTargetSourcePath(getBuildTarget(), copiedBinaryJar);
}
@Override
public ImmutableSortedSet<SourcePath> getJarContents() {
return binaryJarContentsSupplier.get();
}
@Override
public Optional<String> getMavenCoords() {
return mavenCoords;
}
}