com.android.build.gradle.tasks.AidlCompile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-core Show documentation
Show all versions of gradle-core Show documentation
Core library to build Android Gradle plugin.
/*
* Copyright (C) 2012 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 com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.concurrency.GuardedBy;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.IncrementalTask;
import com.android.builder.compiling.DependencyFileProcessor;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.core.VariantType;
import com.android.builder.internal.incremental.DependencyData;
import com.android.builder.internal.incremental.DependencyDataStore;
import com.android.ide.common.internal.LoggedErrorException;
import com.android.ide.common.internal.WaitableExecutor;
import com.android.ide.common.process.LoggedProcessOutputHandler;
import com.android.ide.common.process.ProcessException;
import com.android.ide.common.process.ProcessOutputHandler;
import com.android.ide.common.res2.FileStatus;
import com.android.utils.FileUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.gradle.api.file.FileTree;
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.ParallelizableTask;
import org.gradle.api.tasks.util.PatternSet;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Task to compile aidl files. Supports incremental update.
*/
@ParallelizableTask
public class AidlCompile extends IncrementalTask {
private static final String DEPENDENCY_STORE = "dependency.store";
private static final PatternSet PATTERN_SET = new PatternSet().include("**/*.aidl");
// ----- PUBLIC TASK API -----
private File sourceOutputDir;
@Nullable
private File packagedDir;
@Nullable
private Collection packageWhitelist;
// ----- PRIVATE TASK API -----
@Input
String getBuildToolsVersion() {
return getBuildTools().getRevision().toString();
}
private List sourceDirs;
private List importDirs;
@InputFiles
FileTree getSourceFiles() {
FileTree src = null;
List sources = getSourceDirs();
if (!sources.isEmpty()) {
src = getProject().files(sources).getAsFileTree().matching(PATTERN_SET);
}
return src == null ? getProject().files().getAsFileTree() : src;
}
private static class DepFileProcessor implements DependencyFileProcessor {
@GuardedBy("this")
List dependencyDataList = Lists.newArrayList();
List getDependencyDataList() {
return dependencyDataList;
}
@Override
public DependencyData processFile(@NonNull File dependencyFile) throws IOException {
DependencyData data = DependencyData.parseDependencyFile(dependencyFile);
if (data != null) {
synchronized (this) {
dependencyDataList.add(data);
}
}
return data;
}
}
@Override
protected boolean isIncremental() {
// TODO fix once dep file parsing is resolved.
return false;
}
/**
* Action methods to compile all the files.
*
* The method receives a {@link DependencyFileProcessor} to be used by the
* {@link com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during
* the compilation.
*
* @param dependencyFileProcessor a DependencyFileProcessor
*/
private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor)
throws InterruptedException, ProcessException, LoggedErrorException, IOException {
getBuilder().compileAllAidlFiles(
getSourceDirs(),
getSourceOutputDir(),
getPackagedDir(),
getPackageWhitelist(),
getImportDirs(),
dependencyFileProcessor,
new LoggedProcessOutputHandler(getILogger()));
}
/**
* Returns the import folders.
*/
@NonNull
private List getImportFolders() {
List fullImportDir = Lists.newArrayList();
fullImportDir.addAll(getImportDirs());
fullImportDir.addAll(getSourceDirs());
return fullImportDir;
}
/**
* Compiles a single file.
* @param sourceFolder the file to compile.
* @param file the file to compile.
* @param importFolders the import folders.
* @param dependencyFileProcessor a DependencyFileProcessor
*/
private void compileSingleFile(
@NonNull File sourceFolder,
@NonNull File file,
@Nullable List importFolders,
@NonNull DependencyFileProcessor dependencyFileProcessor,
@NonNull ProcessOutputHandler processOutputHandler)
throws InterruptedException, ProcessException, LoggedErrorException, IOException {
getBuilder().compileAidlFile(
sourceFolder,
file,
getSourceOutputDir(),
getPackagedDir(),
getPackageWhitelist(),
Preconditions.checkNotNull(importFolders),
dependencyFileProcessor,
processOutputHandler);
}
@Override
protected void doFullTaskAction() throws IOException {
// this is full run, clean the previous output
File destinationDir = getSourceOutputDir();
File parcelableDir = getPackagedDir();
FileUtils.emptyFolder(destinationDir);
if (parcelableDir != null) {
FileUtils.emptyFolder(parcelableDir);
}
DepFileProcessor processor = new DepFileProcessor();
try {
compileAllFiles(processor);
} catch (Exception e) {
throw new RuntimeException(e);
}
List dataList = processor.getDependencyDataList();
DependencyDataStore store = new DependencyDataStore();
store.addData(dataList);
try {
store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void doIncrementalTaskAction(Map changedInputs) throws IOException {
File incrementalData = new File(getIncrementalFolder(), DEPENDENCY_STORE);
DependencyDataStore store = new DependencyDataStore();
Multimap inputMap;
try {
inputMap = store.loadFrom(incrementalData);
} catch (Exception ignored) {
FileUtils.delete(incrementalData);
getProject().getLogger().info(
"Failed to read dependency store: full task run!");
doFullTaskAction();
return;
}
final List importFolders = getImportFolders();
final DepFileProcessor processor = new DepFileProcessor();
final ProcessOutputHandler processOutputHandler =
new LoggedProcessOutputHandler(getILogger());
// use an executor to parallelize the compilation of multiple files.
WaitableExecutor executor = new WaitableExecutor();
Map mainFileMap = store.getMainFileMap();
for (final Map.Entry entry : changedInputs.entrySet()) {
FileStatus status = entry.getValue();
switch (status) {
case NEW:
executor.execute(new Callable() {
@Override
public Void call() throws Exception {
File file = entry.getKey();
compileSingleFile(getSourceFolder(file), file, importFolders,
processor, processOutputHandler);
return null;
}
});
break;
case CHANGED:
Collection impactedData =
inputMap.get(entry.getKey().getAbsolutePath());
if (impactedData != null) {
for (final DependencyData data: impactedData) {
executor.execute(new Callable() {
@Override
public Void call() throws Exception {
File file = new File(data.getMainFile());
compileSingleFile(getSourceFolder(file), file,
importFolders, processor, processOutputHandler);
return null;
}
});
}
}
break;
case REMOVED:
final DependencyData data2 = mainFileMap.get(entry.getKey().getAbsolutePath());
if (data2 != null) {
executor.execute(new Callable() {
@Override
public Void call() throws Exception {
cleanUpOutputFrom(data2);
return null;
}
});
store.remove(data2);
}
break;
}
}
try {
executor.waitForTasksWithQuickFail(true /*cancelRemaining*/);
} catch (Throwable t) {
FileUtils.delete(incrementalData);
throw new RuntimeException(t);
}
// get all the update data for the recompiled objects
store.updateAll(processor.getDependencyDataList());
try {
store.saveTo(incrementalData);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private File getSourceFolder(@NonNull File file) {
File parentDir = file;
while ((parentDir = parentDir.getParentFile()) != null) {
for (File folder : getSourceDirs()) {
if (parentDir.equals(folder)) {
return folder;
}
}
}
throw new IllegalArgumentException(String.format("File '%s' is not in a source dir", file));
}
private static void cleanUpOutputFrom(@NonNull DependencyData dependencyData)
throws IOException {
for (String output : dependencyData.getOutputFiles()) {
FileUtils.delete(new File(output));
}
for (String output : dependencyData.getSecondaryOutputFiles()) {
FileUtils.delete(new File(output));
}
}
@OutputDirectory
public File getSourceOutputDir() {
return sourceOutputDir;
}
public void setSourceOutputDir(File sourceOutputDir) {
this.sourceOutputDir = sourceOutputDir;
}
@OutputDirectory @Optional @Nullable
public File getPackagedDir() {
return packagedDir;
}
public void setPackagedDir(@Nullable File packagedDir) {
this.packagedDir = packagedDir;
}
@Input @Optional @Nullable
public Collection getPackageWhitelist() {
return packageWhitelist;
}
public void setPackageWhitelist(@Nullable Collection packageWhitelist) {
this.packageWhitelist = packageWhitelist;
}
public List getSourceDirs() {
return sourceDirs;
}
public void setSourceDirs(List sourceDirs) {
this.sourceDirs = sourceDirs;
}
@InputFiles
public List getImportDirs() {
return importDirs;
}
public void setImportDirs(List importDirs) {
this.importDirs = importDirs;
}
public static class ConfigAction implements TaskConfigAction {
@NonNull
VariantScope scope;
public ConfigAction(@NonNull VariantScope scope) {
this.scope = scope;
}
@Override
@NonNull
public String getName() {
return scope.getTaskName("compile", "Aidl");
}
@Override
@NonNull
public Class getType() {
return AidlCompile.class;
}
@Override
public void execute(AidlCompile compileTask) {
final VariantConfiguration,?,?> variantConfiguration = scope.getVariantConfiguration();
scope.getVariantData().aidlCompileTask = compileTask;
compileTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
compileTask.setVariantName(scope.getVariantConfiguration().getFullName());
compileTask.setIncrementalFolder(scope.getIncrementalDir(getName()));
ConventionMappingHelper.map(compileTask, "sourceDirs", new Callable>() {
@Override
public List call() throws Exception {
return variantConfiguration.getAidlSourceList();
}
});
ConventionMappingHelper.map(compileTask, "importDirs", new Callable>() {
@Override
public List call() throws Exception {
return variantConfiguration.getAidlImports();
}
});
compileTask.setSourceOutputDir(scope.getAidlSourceOutputDir());
if (variantConfiguration.getType() == VariantType.LIBRARY) {
compileTask.setPackagedDir(scope.getPackagedAidlDir());
compileTask.setPackageWhitelist(
scope.getGlobalScope().getExtension().getAidlPackageWhiteList());
}
}
}
}