/*
* 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.android;
import com.facebook.buck.android.aapt.MergeAndroidResourceSources;
import com.facebook.buck.cxx.CxxBuckConfig;
import com.facebook.buck.jvm.java.JavaBuckConfig;
import com.facebook.buck.jvm.java.JavaLibrary;
import com.facebook.buck.jvm.java.JavacFactory;
import com.facebook.buck.jvm.java.JavacOptions;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildRules;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.coercer.BuildConfigFields;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.immutables.BuckStyleImmutable;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.Optional;
import java.util.stream.Collectors;
import org.immutables.value.Value;
/**
* Description for a {@link BuildRule} that generates an {@code .aar} file.
*
* <p>This represents an Android Library Project packaged as an {@code .aar} bundle as specified by:
* <a> http://tools.android.com/tech-docs/new-build-system/aar-format</a>.
*
* <p>Note that the {@code aar} may be specified as a {@link SourcePath}, so it could be either a
* binary {@code .aar} file checked into version control, or a zip file that conforms to the {@code
* .aar} specification that is generated by another build rule.
*/
public class AndroidAarDescription implements Description<AndroidAarDescriptionArg> {
private static final Flavor AAR_ANDROID_MANIFEST_FLAVOR =
InternalFlavor.of("aar_android_manifest");
private static final Flavor AAR_ASSEMBLE_RESOURCE_FLAVOR =
InternalFlavor.of("aar_assemble_resource");
private static final Flavor AAR_ASSEMBLE_ASSETS_FLAVOR = InternalFlavor.of("aar_assemble_assets");
private static final Flavor AAR_ANDROID_RESOURCE_FLAVOR =
InternalFlavor.of("aar_android_resource");
private final AndroidManifestDescription androidManifestDescription;
private final CxxBuckConfig cxxBuckConfig;
private final JavaBuckConfig javaBuckConfig;
private final JavacOptions javacOptions;
private final ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms;
public AndroidAarDescription(
AndroidManifestDescription androidManifestDescription,
CxxBuckConfig cxxBuckConfig,
JavaBuckConfig javaBuckConfig,
JavacOptions javacOptions,
ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms) {
this.androidManifestDescription = androidManifestDescription;
this.cxxBuckConfig = cxxBuckConfig;
this.javaBuckConfig = javaBuckConfig;
this.javacOptions = javacOptions;
this.nativePlatforms = nativePlatforms;
}
@Override
public Class<AndroidAarDescriptionArg> getConstructorArgType() {
return AndroidAarDescriptionArg.class;
}
@Override
public BuildRule createBuildRule(
TargetGraph targetGraph,
BuildRuleParams originalBuildRuleParams,
BuildRuleResolver resolver,
CellPathResolver cellRoots,
AndroidAarDescriptionArg args)
throws NoSuchBuildTargetException {
originalBuildRuleParams.getBuildTarget().checkUnflavored();
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
ImmutableList.Builder<BuildRule> aarExtraDepsBuilder =
ImmutableList.<BuildRule>builder().addAll(originalBuildRuleParams.getExtraDeps().get());
/* android_manifest */
BuildRuleParams androidManifestParams =
originalBuildRuleParams.withAppendedFlavor(AAR_ANDROID_MANIFEST_FLAVOR);
AndroidManifestDescriptionArg androidManifestArgs =
AndroidManifestDescriptionArg.builder()
.setName(androidManifestParams.getBuildTarget().getShortName())
.setSkeleton(args.getManifestSkeleton())
.setDeps(args.getDeps())
.build();
AndroidManifest manifest =
androidManifestDescription.createBuildRule(
targetGraph, androidManifestParams, resolver, cellRoots, androidManifestArgs);
aarExtraDepsBuilder.add(resolver.addToIndex(manifest));
final APKModuleGraph apkModuleGraph =
new APKModuleGraph(targetGraph, originalBuildRuleParams.getBuildTarget(), Optional.empty());
/* assemble dirs */
AndroidPackageableCollector collector =
new AndroidPackageableCollector(
originalBuildRuleParams.getBuildTarget(),
/* buildTargetsToExcludeFromDex */ ImmutableSet.of(),
/* resourcesToExclude */ ImmutableSet.of(),
apkModuleGraph);
collector.addPackageables(
AndroidPackageableCollector.getPackageableRules(originalBuildRuleParams.getBuildDeps()));
AndroidPackageableCollection packageableCollection = collector.build();
ImmutableSortedSet<BuildRule> androidResourceDeclaredDeps =
AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getDeclaredDeps().get());
ImmutableSortedSet<BuildRule> androidResourceExtraDeps =
AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getExtraDeps().get());
BuildRuleParams assembleAssetsParams =
originalBuildRuleParams
.withAppendedFlavor(AAR_ASSEMBLE_ASSETS_FLAVOR)
.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(androidResourceDeclaredDeps),
Suppliers.ofInstance(androidResourceExtraDeps));
ImmutableCollection<SourcePath> assetsDirectories =
packageableCollection.getAssetsDirectories();
AssembleDirectories assembleAssetsDirectories =
new AssembleDirectories(assembleAssetsParams, assetsDirectories);
aarExtraDepsBuilder.add(resolver.addToIndex(assembleAssetsDirectories));
BuildRuleParams assembleResourceParams =
originalBuildRuleParams
.withAppendedFlavor(AAR_ASSEMBLE_RESOURCE_FLAVOR)
.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(androidResourceDeclaredDeps),
Suppliers.ofInstance(androidResourceExtraDeps));
ImmutableCollection<SourcePath> resDirectories =
packageableCollection.getResourceDetails().getResourceDirectories();
MergeAndroidResourceSources assembleResourceDirectories =
new MergeAndroidResourceSources(assembleResourceParams, resDirectories);
aarExtraDepsBuilder.add(resolver.addToIndex(assembleResourceDirectories));
/* android_resource */
BuildRuleParams androidResourceParams =
originalBuildRuleParams
.withAppendedFlavor(AAR_ANDROID_RESOURCE_FLAVOR)
.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(
ImmutableSortedSet.of(
manifest, assembleAssetsDirectories, assembleResourceDirectories)),
Suppliers.ofInstance(ImmutableSortedSet.of()));
AndroidResource androidResource =
new AndroidResource(
androidResourceParams,
ruleFinder,
/* deps */ ImmutableSortedSet.<BuildRule>naturalOrder()
.add(assembleAssetsDirectories)
.add(assembleResourceDirectories)
.addAll(originalBuildRuleParams.getDeclaredDeps().get())
.build(),
assembleResourceDirectories.getSourcePathToOutput(),
/* resSrcs */ ImmutableSortedMap.of(),
/* rDotJavaPackage */ null,
assembleAssetsDirectories.getSourcePathToOutput(),
/* assetsSrcs */ ImmutableSortedMap.of(),
manifest.getSourcePathToOutput(),
/* hasWhitelistedStrings */ false);
aarExtraDepsBuilder.add(resolver.addToIndex(androidResource));
ImmutableSortedSet.Builder<SourcePath> classpathToIncludeInAar =
ImmutableSortedSet.naturalOrder();
classpathToIncludeInAar.addAll(packageableCollection.getClasspathEntriesToDex());
aarExtraDepsBuilder.addAll(
BuildRules.toBuildRulesFor(
originalBuildRuleParams.getBuildTarget(),
resolver,
packageableCollection.getJavaLibrariesToDex()));
if (!args.getBuildConfigValues().getNameToField().isEmpty()
&& !args.getIncludeBuildConfigClass()) {
throw new HumanReadableException(
"Rule %s has build_config_values set but does not set "
+ "include_build_config_class to True. Either indicate you want to include the "
+ "BuildConfig class in the final .aar or do not specify build config values.",
originalBuildRuleParams.getBuildTarget());
}
if (args.getIncludeBuildConfigClass()) {
ImmutableSortedSet<JavaLibrary> buildConfigRules =
AndroidBinaryGraphEnhancer.addBuildConfigDeps(
originalBuildRuleParams,
AndroidBinary.PackageType.RELEASE,
EnumSet.noneOf(AndroidBinary.ExopackageMode.class),
args.getBuildConfigValues(),
Optional.empty(),
resolver,
JavacFactory.create(ruleFinder, javaBuckConfig, args),
javacOptions,
packageableCollection);
resolver.addAllToIndex(buildConfigRules);
aarExtraDepsBuilder.addAll(buildConfigRules);
classpathToIncludeInAar.addAll(
buildConfigRules
.stream()
.map(BuildRule::getSourcePathToOutput)
.collect(Collectors.toList()));
}
/* native_libraries */
AndroidNativeLibsPackageableGraphEnhancer packageableGraphEnhancer =
new AndroidNativeLibsPackageableGraphEnhancer(
resolver,
originalBuildRuleParams,
nativePlatforms,
ImmutableSet.of(),
cxxBuckConfig,
/* nativeLibraryMergeMap */ Optional.empty(),
/* nativeLibraryMergeGlue */ Optional.empty(),
Optional.empty(),
AndroidBinary.RelinkerMode.DISABLED,
apkModuleGraph);
Optional<ImmutableMap<APKModule, CopyNativeLibraries>> nativeLibrariesOptional =
packageableGraphEnhancer.enhance(packageableCollection).getCopyNativeLibraries();
if (nativeLibrariesOptional.isPresent()
&& nativeLibrariesOptional.get().containsKey(apkModuleGraph.getRootAPKModule())) {
aarExtraDepsBuilder.add(
resolver.addToIndex(
nativeLibrariesOptional.get().get(apkModuleGraph.getRootAPKModule())));
}
Optional<Path> assembledNativeLibsDir =
nativeLibrariesOptional.map(
input -> {
// there will be only one value for the root module
CopyNativeLibraries copyNativeLibraries =
input.get(apkModuleGraph.getRootAPKModule());
if (copyNativeLibraries == null) {
throw new HumanReadableException(
"Native libraries are present but not in the root application module.");
}
return copyNativeLibraries.getPathToNativeLibsDir();
});
BuildRuleParams androidAarParams =
originalBuildRuleParams.copyReplacingExtraDeps(
Suppliers.ofInstance(ImmutableSortedSet.copyOf(aarExtraDepsBuilder.build())));
return new AndroidAar(
androidAarParams,
manifest,
androidResource,
assembleResourceDirectories.getSourcePathToOutput(),
assembleAssetsDirectories.getSourcePathToOutput(),
assembledNativeLibsDir,
ImmutableSet.copyOf(packageableCollection.getNativeLibAssetsDirectories().values()),
classpathToIncludeInAar.build());
}
@BuckStyleImmutable
@Value.Immutable
interface AbstractAndroidAarDescriptionArg extends AndroidLibraryDescription.CoreArg {
SourcePath getManifestSkeleton();
@Value.Default
default BuildConfigFields getBuildConfigValues() {
return BuildConfigFields.empty();
}
@Value.Default
default Boolean getIncludeBuildConfigClass() {
return false;
}
}
}