blob: 971edd0302c4a1873e06cb8f7f54f342bb4dab42 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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.android.build.gradle.tasks;
import static com.android.build.gradle.internal.scope.InternalArtifactType.APK_FOR_LOCAL_TEST;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_ASSETS;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_MANIFESTS;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_RES;
import static com.google.common.base.Preconditions.checkNotNull;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.api.artifact.BuildableArtifact;
import com.android.build.gradle.internal.api.artifact.BuildableArtifactUtil;
import com.android.build.gradle.internal.dsl.TestOptions;
import com.android.build.gradle.internal.scope.ApkData;
import com.android.build.gradle.internal.scope.BuildOutput;
import com.android.build.gradle.internal.scope.ExistingBuildElements;
import com.android.build.gradle.internal.scope.InternalArtifactType;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.AndroidVariantTask;
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction;
import com.android.build.gradle.options.BooleanOption;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.function.Supplier;
import org.gradle.api.file.Directory;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
/**
* Generates the {@code test_config.properties} file that is put on the classpath for running unit
* tests.
*
* <p>See DSL documentation in {@link TestOptions.UnitTestOptions#isIncludeAndroidResources()}
*/
public class GenerateTestConfig extends AndroidVariantTask {
BuildableArtifact resourcesDirectory;
BuildableArtifact assets;
Path sdkHome;
File generatedJavaResourcesDirectory;
ApkData mainApkInfo;
Provider<Directory> manifests;
BuildableArtifact compiledResourcesZip;
Supplier<String> packageForR;
@Input
public ApkData getMainApkInfo() {
return mainApkInfo;
}
@InputFiles
public Provider<Directory> getManifests() {
return manifests;
}
@TaskAction
public void generateTestConfig() throws IOException {
checkNotNull(assets);
checkNotNull(sdkHome);
BuildOutput output =
ExistingBuildElements.from(InternalArtifactType.MERGED_MANIFESTS, manifests)
.element(mainApkInfo);
generateTestConfigForOutput(
Iterables.getOnlyElement(assets).toPath().toAbsolutePath(),
resourcesDirectory == null
? null
: BuildableArtifactUtil.singleFile(resourcesDirectory)
.toPath()
.toAbsolutePath(),
sdkHome,
getPackageForR(),
checkNotNull(output, "Unable to find manifest output").getOutputFile().toPath(),
compiledResourcesZip,
generatedJavaResourcesDirectory.toPath().toAbsolutePath());
}
@VisibleForTesting
static void generateTestConfigForOutput(
@NonNull Path assetsDir,
@Nullable Path resDir,
@NonNull Path sdkHome,
@NonNull String packageForR,
@NonNull Path manifest,
@Nullable BuildableArtifact compiledResourcesZip,
@NonNull Path outputDir)
throws IOException {
Properties properties = new Properties();
properties.setProperty("android_sdk_home", sdkHome.toAbsolutePath().toString());
if (resDir != null) {
properties.setProperty("android_merged_resources", resDir.toAbsolutePath().toString());
}
properties.setProperty("android_merged_manifest", manifest.toAbsolutePath().toString());
properties.setProperty("android_merged_assets", assetsDir.toAbsolutePath().toString());
if (compiledResourcesZip != null) {
properties.setProperty("android_resource_apk",
apkFrom(compiledResourcesZip).getPath());
}
properties.setProperty("android_custom_package", packageForR);
Path output =
outputDir
.resolve("com")
.resolve("android")
.resolve("tools")
.resolve("test_config.properties");
Files.createDirectories(output.getParent());
try (Writer writer = Files.newBufferedWriter(output)) {
properties.store(writer, "# Generated by the Android Gradle Plugin");
}
}
@Input // No need for @InputDirectory, we only care about the path.
@Optional
public String getResourcesDirectory() {
if (resourcesDirectory == null) {
return null;
}
return BuildableArtifactUtil.singleFile(resourcesDirectory).getPath();
}
@Input // No need for @InputDirectory, we only care about the path.
public String getAssets() {
return Iterables.getOnlyElement(assets).getPath();
}
@Input // No need for @InputDirectory, we only care about the path.
public String getSdkHome() {
return sdkHome.toString();
}
@Optional
@Input
public String getCompiledResourcesZip() {
if (compiledResourcesZip == null) {
return null;
}
return apkFrom(compiledResourcesZip).getPath();
}
@NonNull
private static File apkFrom(BuildableArtifact compiledResourcesZip) {
return Iterables.getOnlyElement(compiledResourcesZip.getFiles());
}
@OutputDirectory
public File getOutputFile() {
return generatedJavaResourcesDirectory;
}
@Input
public String getPackageForR() {
return packageForR.get();
}
public static class CreationAction extends VariantTaskCreationAction<GenerateTestConfig> {
@NonNull private final VariantScope testedScope;
private File generatedJavaResourcesDirectory;
public CreationAction(@NonNull VariantScope scope) {
super(scope);
this.testedScope =
Preconditions.checkNotNull(
scope.getTestedVariantData(), "Not a unit test variant.")
.getScope();
}
@NonNull
@Override
public String getName() {
return getVariantScope().getTaskName("generate", "Config");
}
@NonNull
@Override
public Class<GenerateTestConfig> getType() {
return GenerateTestConfig.class;
}
@Override
public void preConfigure(@NonNull String taskName) {
super.preConfigure(taskName);
generatedJavaResourcesDirectory =
getVariantScope()
.getArtifacts()
.appendArtifact(
InternalArtifactType.UNIT_TEST_CONFIG_DIRECTORY,
taskName,
"out");
}
@Override
public void configure(@NonNull GenerateTestConfig task) {
super.configure(task);
VariantScope scope = getVariantScope();
// we don't actually consume the task, only the path, so make a manual dependency
// on the filecollections.
task.manifests = testedScope.getArtifacts().getFinalProduct(MERGED_MANIFESTS);
boolean enableBinaryResources =
scope.getGlobalScope().getProjectOptions().get(
BooleanOption.ENABLE_UNIT_TEST_BINARY_RESOURCES);
if (enableBinaryResources) {
task.compiledResourcesZip = scope
.getArtifacts().getFinalArtifactFiles(APK_FOR_LOCAL_TEST);
task.dependsOn(task.compiledResourcesZip);
} else {
task.resourcesDirectory = scope.getArtifacts().getFinalArtifactFiles(MERGED_RES);
task.dependsOn(task.resourcesDirectory);
}
task.assets = testedScope.getArtifacts().getFinalArtifactFiles(MERGED_ASSETS);
task.dependsOn(task.assets);
task.mainApkInfo = testedScope.getOutputScope().getMainSplit();
task.sdkHome =
Paths.get(scope.getGlobalScope().getAndroidBuilder().getTarget().getLocation());
task.generatedJavaResourcesDirectory = generatedJavaResourcesDirectory;
task.packageForR = testedScope.getVariantConfiguration()::getOriginalApplicationId;
}
}
}