| /* |
| * 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.SdkConstants |
| import com.android.annotations.NonNull |
| import com.android.annotations.Nullable |
| import com.android.annotations.concurrency.GuardedBy |
| import com.android.build.gradle.api.ApkVariant |
| import com.android.build.gradle.internal.TaskManager |
| 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.build.gradle.internal.variant.ApkVariantData |
| import com.android.build.gradle.internal.variant.BaseVariantData |
| import com.android.build.gradle.internal.variant.BaseVariantOutputData |
| 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.builder.model.AndroidProject |
| import com.android.ide.common.internal.WaitableExecutor |
| import com.android.ide.common.res2.FileStatus |
| 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.util.PatternSet |
| |
| import java.util.concurrent.Callable |
| |
| import static com.android.builder.model.AndroidProject.FD_GENERATED |
| import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES |
| |
| /** |
| * Task to compile aidl files. Supports incremental update. |
| */ |
| public class AidlCompile extends IncrementalTask { |
| |
| private static final String DEPENDENCY_STORE = "dependency.store" |
| |
| // ----- PUBLIC TASK API ----- |
| |
| @OutputDirectory |
| File sourceOutputDir |
| |
| @OutputDirectory @Optional |
| File aidlParcelableDir |
| |
| // ----- PRIVATE TASK API ----- |
| @Input |
| String getBuildToolsVersion() { |
| getBuildTools().getRevision() |
| } |
| |
| List<File> sourceDirs |
| |
| @InputFiles |
| List<File> importDirs |
| |
| final PatternSet patternSet = new PatternSet().include("**/*.aidl") |
| |
| @InputFiles |
| FileTree getSourceFiles() { |
| FileTree src = null |
| Set<File> sources = getSourceDirs() |
| if (!sources.isEmpty()) { |
| src = getProject().files(new ArrayList<Object>(sources)).getAsFileTree().matching(patternSet) |
| } |
| return src == null ? getProject().files().getAsFileTree() : src |
| } |
| |
| private static class DepFileProcessor implements DependencyFileProcessor { |
| |
| @GuardedBy("this") |
| List<DependencyData> dependencyDataList = Lists.newArrayList() |
| |
| List<DependencyData> getDependencyDataList() { |
| return dependencyDataList |
| } |
| |
| @Override |
| DependencyData processFile(@NonNull File dependencyFile) { |
| DependencyData data = DependencyData.parseDependencyFile(dependencyFile) |
| if (data != null) { |
| synchronized (this) { |
| dependencyDataList.add(data) |
| } |
| } |
| |
| return data; |
| } |
| } |
| |
| 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) { |
| getBuilder().compileAllAidlFiles( |
| getSourceDirs(), |
| getSourceOutputDir(), |
| getAidlParcelableDir(), |
| getImportDirs(), |
| dependencyFileProcessor) |
| } |
| |
| /** |
| * Returns the import folders. |
| */ |
| @NonNull |
| private List<File> getImportFolders() { |
| List<File> 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<File> importFolders, |
| @NonNull DependencyFileProcessor dependencyFileProcessor) { |
| getBuilder().compileAidlFile( |
| sourceFolder, |
| file, |
| getSourceOutputDir(), |
| getAidlParcelableDir(), |
| importFolders, |
| dependencyFileProcessor) |
| } |
| |
| @Override |
| protected void doFullTaskAction() { |
| // this is full run, clean the previous output |
| File destinationDir = getSourceOutputDir() |
| emptyFolder(destinationDir) |
| |
| File parcelableDir = getAidlParcelableDir() |
| if (parcelableDir != null) { |
| emptyFolder(parcelableDir) |
| } |
| |
| DepFileProcessor processor = new DepFileProcessor() |
| |
| compileAllFiles(processor) |
| |
| List<DependencyData> dataList = processor.getDependencyDataList() |
| |
| DependencyDataStore store = new DependencyDataStore() |
| store.addData(dataList) |
| |
| store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE)) |
| } |
| |
| @Override |
| protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) { |
| File incrementalData = new File(getIncrementalFolder(), DEPENDENCY_STORE) |
| DependencyDataStore store = new DependencyDataStore() |
| Multimap<String, DependencyData> inputMap |
| try { |
| inputMap = store.loadFrom(incrementalData) |
| } catch (Exception ignored) { |
| incrementalData.delete() |
| project.logger.info( |
| "Failed to read dependency store: full task run!") |
| doFullTaskAction() |
| return |
| } |
| |
| final List<File> importFolders = getImportFolders() |
| final DepFileProcessor processor = new DepFileProcessor() |
| |
| // use an executor to parallelize the compilation of multiple files. |
| WaitableExecutor<Void> executor = new WaitableExecutor<Void>() |
| |
| Map<String,DependencyData> mainFileMap = store.getMainFileMap() |
| |
| for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) { |
| FileStatus status = entry.getValue() |
| |
| switch (status) { |
| case FileStatus.NEW: |
| executor.execute(new Callable<Void>() { |
| @Override |
| Void call() throws Exception { |
| File file = entry.getKey() |
| compileSingleFile(getSourceFolder(file), file, importFolders, processor) |
| } |
| }) |
| break |
| case FileStatus.CHANGED: |
| List<DependencyData> impactedData = inputMap.get(entry.getKey().absolutePath) |
| if (impactedData != null) { |
| final int count = impactedData.size(); |
| for (int i = 0; i < count; i++) { |
| final DependencyData data = impactedData.get(i); |
| |
| executor.execute(new Callable<Void>() { |
| @Override |
| Void call() throws Exception { |
| File file = new File(data.getMainFile()); |
| compileSingleFile(getSourceFolder(file), file, |
| importFolders, processor) |
| } |
| }) |
| } |
| } |
| break |
| case FileStatus.REMOVED: |
| final DependencyData data2 = mainFileMap.get(entry.getKey().absolutePath) |
| if (data2 != null) { |
| executor.execute(new Callable<Void>() { |
| @Override |
| Void call() throws Exception { |
| cleanUpOutputFrom(data2) |
| } |
| }) |
| store.remove(data2) |
| } |
| break |
| } |
| } |
| |
| try { |
| executor.waitForTasksWithQuickFail(true /*cancelRemaining*/) |
| } catch (Throwable t) { |
| incrementalData.delete() |
| throw t |
| } |
| |
| // get all the update data for the recompiled objects |
| store.updateAll(processor.getDependencyDataList()) |
| |
| store.saveTo(incrementalData) |
| } |
| |
| private File getSourceFolder(@NonNull File file) { |
| File parentDir = file |
| while ((parentDir = parentDir.getParentFile()) != null) { |
| for (File folder : getSourceDirs()) { |
| if (parentDir.equals(folder)) { |
| return folder; |
| } |
| } |
| } |
| |
| assert false |
| } |
| |
| private static void cleanUpOutputFrom(@NonNull DependencyData dependencyData) { |
| for (String output : dependencyData.getOutputFiles()) { |
| new File(output).delete() |
| } |
| for (String output : dependencyData.getSecondaryOutputFiles()) { |
| new File(output).delete() |
| } |
| } |
| |
| |
| public static class ConfigAction implements TaskConfigAction<AidlCompile> { |
| |
| @NonNull |
| VariantScope scope |
| |
| ConfigAction(@NonNull VariantScope scope) { |
| this.scope = scope |
| } |
| |
| @Override |
| @NonNull |
| String getName() { |
| return scope.getTaskName("compile", "Aidl") |
| } |
| |
| @Override |
| @NonNull |
| Class<AidlCompile> getType() { |
| return AidlCompile |
| } |
| |
| @Override |
| void execute(AidlCompile compileTask) { |
| VariantConfiguration variantConfiguration = scope.variantConfiguration |
| |
| scope.variantData.aidlCompileTask = compileTask |
| |
| compileTask.androidBuilder = scope.globalScope.androidBuilder |
| compileTask.incrementalFolder = |
| new File( |
| "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/incremental/aidl/${variantConfiguration.dirName}") |
| |
| ConventionMappingHelper.map(compileTask, "sourceDirs") { variantConfiguration.aidlSourceList } |
| ConventionMappingHelper.map(compileTask, "importDirs") { variantConfiguration.aidlImports } |
| |
| ConventionMappingHelper.map(compileTask, "sourceOutputDir") { |
| //new File(scope.globalScope.generatedDir, "source/aidl/${variantConfiguration.dirName}") |
| scope.getAidlSourceOutputDir() |
| } |
| |
| if (variantConfiguration.type == VariantType.LIBRARY) { |
| compileTask.aidlParcelableDir = new File( |
| "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/$TaskManager.DIR_BUNDLES/${variantConfiguration.dirName}/$SdkConstants.FD_AIDL") |
| } |
| |
| } |
| } |
| |
| } |