/* * Copyright (C) 2015 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.internal; import static com.android.builder.model.AndroidProject.FD_OUTPUTS; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.build.gradle.AndroidConfig; import com.android.build.gradle.TestAndroidConfig; import com.android.build.gradle.internal.scope.AndroidTask; import com.android.build.gradle.internal.scope.VariantScope; import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask; import com.android.build.gradle.internal.test.TestApplicationTestData; import com.android.build.gradle.internal.variant.BaseVariantData; import com.android.build.gradle.internal.variant.BaseVariantOutputData; import com.android.build.gradle.internal.variant.LibraryVariantData; import com.android.build.gradle.tasks.TestModuleProGuardTask; import com.android.builder.core.AndroidBuilder; import com.android.builder.model.AndroidProject; import com.android.builder.testing.ConnectedDeviceProvider; import com.android.builder.testing.TestData; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; import java.io.File; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import proguard.ParseException; /** * TaskManager for standalone test application that lives in a separate module from the tested * application. */ public class TestApplicationTaskManager extends ApplicationTaskManager { public TestApplicationTaskManager(Project project, AndroidBuilder androidBuilder, AndroidConfig extension, SdkHandler sdkHandler, DependencyManager dependencyManager, ToolingModelBuilderRegistry toolingRegistry) { super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry); } @Override public void createTasksForVariantData(@NonNull TaskFactory tasks, @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) { super.createTasksForVariantData(tasks, variantData); // create a new configuration with the target application coordinates. final Configuration testTarget = project.getConfigurations().create("testTarget"); DependencyHandler dependencyHandler = project.getDependencies(); TestAndroidConfig testExtension = (TestAndroidConfig) extension; dependencyHandler.add("testTarget", dependencyHandler.project( ImmutableMap.of( "path", testExtension.getTargetProjectPath(), "configuration", testExtension.getTargetVariant()))); // and create the configuration for the project's metadata. final Configuration testTargetMetadata = project.getConfigurations().create("testTargetMetadata"); dependencyHandler.add("testTargetMetadata", dependencyHandler.project( ImmutableMap.of( "path", testExtension.getTargetProjectPath(), "configuration", testExtension.getTargetVariant() + "-metadata" ))); TestData testData = new TestApplicationTestData( variantData, testTarget, testTargetMetadata, androidBuilder); // create the test connected check task. AndroidTask<DeviceProviderInstrumentTestTask> testConnectedCheck = getAndroidTasks().create( tasks, new DeviceProviderInstrumentTestTask.ConfigAction( variantData.getScope(), new ConnectedDeviceProvider( sdkHandler.getSdkInfo().getAdb(), new LoggerWrapper(getLogger())), testData)); // make the test application connectedCheck depends on the configuration added above so // we can retrieve its artifacts testConnectedCheck.dependsOn(tasks, testTarget, testTargetMetadata, variantData.assembleVariantTask); // make the main ConnectedCheck task depends on this test connectedCheck Task connectedCheck = tasks.named(CONNECTED_CHECK); if (connectedCheck != null) { connectedCheck.dependsOn(testConnectedCheck.getName()); } } @Override @Nullable public File maybeCreateProguardTasks( @NonNull final TaskFactory tasks, @NonNull final VariantScope scope, @NonNull final PostCompilationData pcData) { DependencyHandler dependencyHandler = project.getDependencies(); TestAndroidConfig testExtension = (TestAndroidConfig) extension; Configuration testTargetMapping = project.getConfigurations().create("testTargetMapping"); dependencyHandler.add("testTargetMapping", dependencyHandler.project( ImmutableMap.of( "path", testExtension.getTargetProjectPath(), "configuration", testExtension.getTargetVariant() + "-mapping" ))); if (testTargetMapping.getFiles().isEmpty() || scope.getVariantConfiguration().getProvidedOnlyJars().isEmpty()) { return null; } BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData(); final TestModuleProGuardTask proguardTask = project.getTasks().create( scope.getTaskName("proguard"), TestModuleProGuardTask.class); variantData.obfuscationTask = proguardTask; proguardTask.setLogger(getLogger()); // and create the configuration for the project's mapping file. // Input the mapping from the tested app so that we can deal with obfuscated code. proguardTask.setMappingConfiguration(testTargetMapping); proguardTask.setVariantConfiguration(scope.getVariantConfiguration()); // --- Proguard Config --- // Don't remove any code in tested app. proguardTask.dontshrink(); proguardTask.dontoptimize(); // We can't call dontobfuscate, since that would make ProGuard ignore the mapping file. try { proguardTask.keep("class * {*;}"); proguardTask.keep("interface * {*;}"); proguardTask.keep("enum * {*;}"); } catch (ParseException e) { throw new RuntimeException(e); } // libraryJars: the runtime jars. Do this in doFirst since the boot classpath isn't // available until the SDK is loaded in the prebuild task proguardTask.doFirst(new Action<Task>() { @Override public void execute(Task task) { for (String runtimeJar : androidBuilder.getBootClasspathAsStrings()) { try { proguardTask.libraryjars(runtimeJar); } catch (ParseException e) { throw new RuntimeException(e); } } } }); try { // injar: the compilation output proguardTask.injars(pcData.getInputDirCallable()); if (pcData.getJavaResourcesInputDirCallable() != null) { proguardTask.injars(pcData.getJavaResourcesInputDirCallable()); } // injar: the packaged dependencies LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1); map.put("filter", "!META-INF/MANIFEST.MF"); proguardTask.injars(map, new Callable<Set<File>>() { @Override public Set<File> call() throws Exception { return scope.getVariantConfiguration().getPackagedJars(); } }); proguardTask.libraryjars(new Callable<List<File>>() { @Override public List<File> call() throws Exception { return scope.getVariantConfiguration().getProvidedOnlyJars(); } }); // All -dontwarn rules for test dependencies should go in here: proguardTask.configuration( variantData.getVariantConfiguration().getTestProguardFiles()); // --- Out files --- proguardTask.outjars(scope.getProguardOutputFile()); final File proguardOut = project.file( new File(project.getBuildDir(), FD_OUTPUTS + File.separatorChar + "mapping" + File.separatorChar + variantData.getVariantConfiguration().getDirName())); proguardTask.dump(new File(proguardOut, "dump.txt")); proguardTask.printseeds(new File(proguardOut, "seeds.txt")); proguardTask.printusage(new File(proguardOut, "usage.txt")); proguardTask.printmapping(new File(proguardOut, "mapping.txt")); // proguard doesn't verify that the seed/mapping/usage folders exist and will fail // if they don't so create them. proguardTask.doFirst(new Action<Task>() { @Override public void execute(Task task) { if (!proguardOut.mkdirs()) { throw new RuntimeException( "Cannot create proguard output folder " + proguardOut); } } }); // update dependency. optionalDependsOn(proguardTask, pcData.getClassGeneratingTasks()); optionalDependsOn(proguardTask, pcData.getLibraryGeneratingTasks()); pcData.setLibraryGeneratingTasks(ImmutableList.of(proguardTask)); pcData.setClassGeneratingTasks(ImmutableList.of(proguardTask)); // Update the inputs return scope.getProguardOutputFile(); } catch (Exception e) { throw new RuntimeException(e); } } }