Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
introduce ability to support android_aar (part III, main components)
Browse files Browse the repository at this point in the history
Summary:
This diff call different buildRules to generate the main aar components
1. new BuildRule `AssembleDirectories` to assemble all resource and assets into `res/` and `assets`
2. AndroidResource which depends on #1 to generate `R.txt`
3. AndroidLibrary to generate `classes.jar`
Note that if `srcs` and `res` are missing, there is no JAR is generated, will handle this in another diff.

Test Plan: new unittest to test `AssembleDirectories` and then `arc unit`
  • Loading branch information
Brian Cheung authored and sdwilsh committed Feb 8, 2015
1 parent 55941c9 commit 35b2847
Show file tree
Hide file tree
Showing 16 changed files with 450 additions and 61 deletions.
37 changes: 35 additions & 2 deletions src/com/facebook/buck/android/AndroidAar.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-present Facebook, Inc.
* 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
Expand Down Expand Up @@ -40,16 +40,28 @@ public class AndroidAar extends AbstractBuildRule {
private final Path pathToOutputFile;
private final Path temp;
private final AndroidManifest manifest;
private final AndroidResource androidResource;
private final AndroidLibrary androidLibrary;
private final AssembleDirectories assembleResourceDirectories;
private final AssembleDirectories assembleAssetsDirectories;

public AndroidAar(
BuildRuleParams params,
SourcePathResolver resolver,
AndroidManifest manifest) {
AndroidManifest manifest,
AndroidResource androidResource,
AndroidLibrary androidLibrary,
AssembleDirectories assembleResourceDirectories,
AssembleDirectories assembleAssetsDirectories) {
super(params, resolver);
BuildTarget buildTarget = params.getBuildTarget();
this.pathToOutputFile = BuildTargets.getGenPath(buildTarget, "%s.aar");
this.temp = BuildTargets.getBinPath(buildTarget, "__temp__%s");
this.manifest = manifest;
this.androidResource = androidResource;
this.androidLibrary = androidLibrary;
this.assembleAssetsDirectories = assembleAssetsDirectories;
this.assembleResourceDirectories = assembleResourceDirectories;
}

@Override
Expand Down Expand Up @@ -80,6 +92,27 @@ public ImmutableList<Step> getBuildSteps(
manifest.getPathToOutputFile(),
temp.resolve("AndroidManifest.xml")));

// put R.txt into tmp folder
commands.add(CopyStep.forFile(androidResource.getPathToOutputFile(), temp.resolve("R.txt")));

// put res/ and assets/ into tmp folder
commands.add(CopyStep.forDirectory(
assembleResourceDirectories.getPathToOutputFile(),
temp.resolve("res"),
CopyStep.DirectoryMode.CONTENTS_ONLY));
commands.add(CopyStep.forDirectory(
assembleAssetsDirectories.getPathToOutputFile(),
temp.resolve("assets"),
CopyStep.DirectoryMode.CONTENTS_ONLY));

// put .jar into tmp folder
Path jar = androidLibrary.getPathToOutputFile();
if (jar != null) {
commands.add(CopyStep.forFile(jar, temp.resolve("classes.jar")));
}
// TODO(user) if there is neither src nor resource, there is no JAR,
// in such case we need to generate an empty JAR, and copy the dependence to /libs folder

// do the zipping
commands.add(
new ZipStep(
Expand Down
130 changes: 119 additions & 11 deletions src/com/facebook/buck/android/AndroidAarDescription.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-present Facebook, Inc.
* 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
Expand All @@ -24,15 +24,20 @@
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.BuildTargetSourcePath;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.ImmutableBuildRuleType;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;

import java.nio.file.Path;

/**
* Description for a {@link BuildRule} that generates an {@code .aar} file.
* <p>
Expand All @@ -49,12 +54,23 @@ public class AndroidAarDescription implements Description<AndroidAarDescription.

private static final Flavor AAR_ANDROID_MANIFEST_FLAVOR =
ImmutableFlavor.of("aar_android_manifest");
private static final Flavor AAR_ASSEMBLE_RESOURCE_FLAVOR =
ImmutableFlavor.of("aar_assemble_resource");
private static final Flavor AAR_ASSEMBLE_ASSETS_FLAVOR =
ImmutableFlavor.of("aar_assemble_assets");
private static final Flavor AAR_ANDROID_RESOURCE_FLAVOR =
ImmutableFlavor.of("aar_android_resource");
private static final Flavor AAR_ANDROID_LIBRARY_FLAVOR =
ImmutableFlavor.of("aar_android_library");

private final AndroidManifestDescription androidManifestDescription;
private final AndroidLibraryDescription androidLibraryDescription;

public AndroidAarDescription(
AndroidManifestDescription androidManifestDescription) {
AndroidManifestDescription androidManifestDescription,
AndroidLibraryDescription androidLibraryDescription) {
this.androidManifestDescription = androidManifestDescription;
this.androidLibraryDescription = androidLibraryDescription;
}

@Override
Expand All @@ -75,8 +91,9 @@ public <A extends Arg> BuildRule createBuildRule(

BuildTarget originalBuildTarget = originalBuildRuleParams.getBuildTarget();
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
ImmutableList.Builder<BuildRule> depRules = ImmutableList.builder();

// android_manifest
/* android_manifest */
AndroidManifestDescription.Arg androidManifestArgs =
androidManifestDescription.createUnpopulatedConstructorArg();
androidManifestArgs.skeleton = args.manifestSkeleton;
Expand All @@ -92,25 +109,116 @@ public <A extends Arg> BuildRule createBuildRule(
androidManifestParams,
resolver,
androidManifestArgs);
depRules.add(resolver.addToIndex(manifest));

/* assemble dirs */
AndroidPackageableCollector collector =
new AndroidPackageableCollector(
originalBuildRuleParams.getBuildTarget(),
ImmutableSet.<BuildTarget>of(),
ImmutableSet.<BuildTarget>of());
collector.addPackageables(AndroidPackageableCollector.getPackageableRules(
originalBuildRuleParams.getDeps()));
ImmutableAndroidPackageableCollection packageableCollection = collector.build();

ImmutableSortedSet<BuildRule> androidResourceDeclaredDeps =
AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getDeclaredDeps());
ImmutableSortedSet<BuildRule> androidResourceExtraDeps =
AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getExtraDeps());

BuildRuleParams assembleAssetsParams = originalBuildRuleParams.copyWithChanges(
originalBuildRuleParams.getBuildRuleType(),
BuildTargets.createFlavoredBuildTarget(originalBuildTarget, AAR_ASSEMBLE_ASSETS_FLAVOR),
Suppliers.ofInstance(androidResourceDeclaredDeps),
Suppliers.ofInstance(androidResourceExtraDeps));
ImmutableCollection<SourcePath> assetsDirectories = getSourcePathForDirectories(
assembleAssetsParams.getBuildTarget(),
packageableCollection.getAssetsDirectories());
AssembleDirectories assembleAssetsDirectories = new AssembleDirectories(
assembleAssetsParams,
pathResolver,
assetsDirectories);
depRules.add(resolver.addToIndex(assembleAssetsDirectories));

BuildRuleParams assembleResourceParams = originalBuildRuleParams.copyWithChanges(
originalBuildRuleParams.getBuildRuleType(),
BuildTargets.createFlavoredBuildTarget(originalBuildTarget, AAR_ASSEMBLE_RESOURCE_FLAVOR),
Suppliers.ofInstance(androidResourceDeclaredDeps),
Suppliers.ofInstance(androidResourceExtraDeps));
ImmutableCollection<SourcePath> resDirectories = getSourcePathForDirectories(
assembleResourceParams.getBuildTarget(),
packageableCollection.getResourceDetails().getResourceDirectories());
AssembleDirectories assembleResourceDirectories = new AssembleDirectories(
assembleResourceParams,
pathResolver,
resDirectories);
depRules.add(resolver.addToIndex(assembleResourceDirectories));

/* android_resource */
BuildRuleParams androidResourceParams = originalBuildRuleParams.copyWithChanges(
AndroidLibraryDescription.TYPE,
BuildTargets.createFlavoredBuildTarget(originalBuildTarget, AAR_ANDROID_RESOURCE_FLAVOR),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of(
manifest,
assembleAssetsDirectories,
assembleResourceDirectories)),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));

AndroidResource androidResource = new AndroidResource(
androidResourceParams,
pathResolver,
/* deps */ ImmutableSortedSet.copyOf(depRules.build()),
assembleResourceDirectories.getPathToOutputFile(),
/* resSrcs */ ImmutableSortedSet.<Path>of(),
/* rDotJavaPackage */ null,
assembleAssetsDirectories.getPathToOutputFile(),
/* assetsSrcs */ ImmutableSortedSet.<Path>of(),
new BuildTargetSourcePath(manifest.getBuildTarget()),
/* hasWhitelistedStrings */ false);
depRules.add(resolver.addToIndex(androidResource));

/* android_library */
BuildRuleParams androidLibraryParams = originalBuildRuleParams.copyWithChanges(
AndroidLibraryDescription.TYPE,
BuildTargets.createFlavoredBuildTarget(originalBuildTarget, AAR_ANDROID_LIBRARY_FLAVOR),
Suppliers.ofInstance(originalBuildRuleParams.getDeclaredDeps()),
Suppliers.ofInstance(originalBuildRuleParams.getExtraDeps()));

resolver.addToIndex(manifest);
BuildRule androidLibrary = androidLibraryDescription.createBuildRule(
androidLibraryParams,
resolver,
args);
depRules.add(resolver.addToIndex(androidLibrary));

// android_aar
/* android_aar */
BuildRuleParams androidAarParams = originalBuildRuleParams.copyWithChanges(
TYPE,
originalBuildTarget,
/* declaredDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of(manifest)),
/* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));
Suppliers.ofInstance(ImmutableSortedSet.copyOf(depRules.build())),
Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));

return new AndroidAar(
androidAarParams,
pathResolver,
manifest);
manifest,
androidResource,
(AndroidLibrary) androidLibrary,
assembleResourceDirectories,
assembleAssetsDirectories);
}

private ImmutableList<SourcePath> getSourcePathForDirectories(
BuildTarget buildTarget,
ImmutableCollection<Path> directories) {
ImmutableList.Builder<SourcePath> builder = ImmutableList.builder();
for (Path directory : directories) {
builder.add(new BuildTargetSourcePath(buildTarget, directory));
}
return builder.build();
}

@SuppressFieldNotInitialized
public static class Arg {
public static class Arg extends AndroidLibraryDescription.Arg {
public SourcePath manifestSkeleton;
public Optional<ImmutableSortedSet<BuildTarget>> deps;
}
}
44 changes: 2 additions & 42 deletions src/com/facebook/buck/android/AndroidResourceDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.facebook.buck.util.HumanReadableException;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.base.Suppliers;
Expand All @@ -48,8 +47,6 @@
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import javax.annotation.Nullable;

public class AndroidResourceDescription implements Description<AndroidResourceDescription.Arg> {

public static final BuildRuleType TYPE = ImmutableBuildRuleType.of("android_resource");
Expand All @@ -67,43 +64,6 @@ public Arg createUnpopulatedConstructorArg() {
return new Arg();
}

/**
* Filters out the set of {@code android_resource()} dependencies from {@code deps}. As a special
* case, if an {@code android_prebuilt_aar()} appears in the deps, the {@code android_resource()}
* that corresponds to the AAR will also be included in the output.
* <p>
* Note that if we allowed developers to depend on a flavored build target (in this case, the
* {@link AndroidPrebuiltAarGraphEnhancer#AAR_ANDROID_RESOURCE_FLAVOR} flavor), then we could
* require them to depend on the flavored dep explicitly in their build files. Then we could
* eliminate this special case, though it would be more burdensome for developers to have to
* keep track of when they could depend on an ordinary build rule vs. a flavored one.
*/
private static ImmutableSortedSet<BuildRule> androidResOnly(ImmutableSortedSet<BuildRule> deps) {
return FluentIterable
.from(deps)
.transform(new Function<BuildRule, BuildRule>() {
@Override
@Nullable
public BuildRule apply(BuildRule buildRule) {
if (buildRule instanceof AndroidResource) {
return buildRule;
} else if (buildRule instanceof AndroidLibrary &&
((AndroidLibrary) buildRule).isPrebuiltAar()) {
// An AndroidLibrary that is created via graph enhancement from an
// android_prebuilt_aar() should always have exactly one dependency that is an
// AndroidResource.
return Iterables.getOnlyElement(
FluentIterable.from(buildRule.getDeps())
.filter(Predicates.instanceOf(AndroidResource.class))
.toList());
}
return null;
}
})
.filter(Predicates.notNull())
.toSortedSet(deps.comparator());
}

@Override
public <A extends Arg> BuildRule createBuildRule(
BuildRuleParams params,
Expand All @@ -130,8 +90,8 @@ public <A extends Arg> BuildRule createBuildRule(
// the only deps which should control whether we need to re-run the aapt_package
// step.
params.copyWithDeps(
Suppliers.ofInstance(androidResOnly(params.getDeclaredDeps())),
Suppliers.ofInstance(androidResOnly(params.getExtraDeps()))),
Suppliers.ofInstance(AndroidResourceHelper.androidResOnly(params.getDeclaredDeps())),
Suppliers.ofInstance(AndroidResourceHelper.androidResOnly(params.getExtraDeps()))),
new SourcePathResolver(resolver),
resolver.getAllRules(args.deps.get()),
args.res.orNull(),
Expand Down
67 changes: 67 additions & 0 deletions src/com/facebook/buck/android/AndroidResourceHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.rules.BuildRule;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;

import javax.annotation.Nullable;

public class AndroidResourceHelper {

private AndroidResourceHelper() {}
/**
* Filters out the set of {@code android_resource()} dependencies from {@code deps}. As a special
* case, if an {@code android_prebuilt_aar()} appears in the deps, the {@code android_resource()}
* that corresponds to the AAR will also be included in the output.
* <p>
* Note that if we allowed developers to depend on a flavored build target (in this case, the
* {@link AndroidPrebuiltAarGraphEnhancer#AAR_ANDROID_RESOURCE_FLAVOR} flavor), then we could
* require them to depend on the flavored dep explicitly in their build files. Then we could
* eliminate this special case, though it would be more burdensome for developers to have to
* keep track of when they could depend on an ordinary build rule vs. a flavored one.
*/
public static ImmutableSortedSet<BuildRule> androidResOnly(ImmutableSortedSet<BuildRule> deps) {
return FluentIterable
.from(deps)
.transform(new Function<BuildRule, BuildRule>() {
@Override
@Nullable
public BuildRule apply(BuildRule buildRule) {
if (buildRule instanceof AndroidResource) {
return buildRule;
} else if (buildRule instanceof AndroidLibrary &&
((AndroidLibrary) buildRule).isPrebuiltAar()) {
// An AndroidLibrary that is created via graph enhancement from an
// android_prebuilt_aar() should always have exactly one dependency that is an
// AndroidResource.
return Iterables.getOnlyElement(
FluentIterable.from(buildRule.getDeps())
.filter(Predicates.instanceOf(AndroidResource.class))
.toList());
}
return null;
}
})
.filter(Predicates.notNull())
.toSortedSet(deps.comparator());
}
}

0 comments on commit 35b2847

Please sign in to comment.