Fix renderscript compilation.
Xavier Ducrohet [Wed, 30 Jan 2013 02:24:56 +0000 (18:24 -0800)]
After checking with the renderscript team, it seems incremental support
cannot work as the compiler must be called with all .rs files.

This is broken in the other build system.

Change-Id: I045e1aa22f9a961d36fd32f71bd5158f40b765c4

builder/src/main/java/com/android/builder/AndroidBuilder.java
builder/src/main/java/com/android/builder/internal/compiler/FileGatherer.java [copied from builder/src/main/java/com/android/builder/internal/compiler/LeafFolderProcessor.java with 67% similarity]
builder/src/main/java/com/android/builder/internal/compiler/LeafFolderGatherer.java [moved from builder/src/main/java/com/android/builder/internal/compiler/LeafFolderProcessor.java with 93% similarity]
builder/src/main/java/com/android/builder/internal/compiler/RenderscriptProcessor.java [deleted file]
gradle/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy

index 76e6163..c484675 100644 (file)
@@ -27,8 +27,8 @@ import com.android.builder.internal.SymbolLoader;
 import com.android.builder.internal.SymbolWriter;
 import com.android.builder.internal.TestManifestGenerator;
 import com.android.builder.internal.compiler.AidlProcessor;
-import com.android.builder.internal.compiler.LeafFolderProcessor;
-import com.android.builder.internal.compiler.RenderscriptProcessor;
+import com.android.builder.internal.compiler.FileGatherer;
+import com.android.builder.internal.compiler.LeafFolderGatherer;
 import com.android.builder.internal.compiler.SourceSearcher;
 import com.android.builder.internal.packaging.JavaResourceProcessor;
 import com.android.builder.internal.packaging.Packager;
@@ -731,6 +731,11 @@ public class AndroidBuilder {
     /**
      * Compiles all the renderscript files found in the given source folders.
      *
+     * Right now this is the only way to compile them as the renderscript compiler requires all
+     * renderscript files to be passed for all compilation.
+     *
+     * Therefore whenever a renderscript file or header changes, all must be recompiled.
+     *
      * @param sourceFolders all the source folders to find files to compile
      * @param importFolders all the import folders.
      * @param sourceOutputDir the output dir in which to generate the source code
@@ -738,8 +743,6 @@ public class AndroidBuilder {
      * @param targetApi the target api
      * @param debugBuild whether the build is debug
      * @param optimLevel the optimization level
-     * @param dependencyFileProcessor the dependencyFileProcessor to record the dependencies
-     *                                of the compilation.
      * @throws IOException
      * @throws InterruptedException
      */
@@ -749,8 +752,7 @@ public class AndroidBuilder {
                                             @NonNull File resOutputDir,
                                             int targetApi,
                                             boolean debugBuild,
-                                            int optimLevel,
-                                            @Nullable DependencyFileProcessor dependencyFileProcessor)
+                                            int optimLevel)
             throws IOException, InterruptedException, ExecutionException {
         checkState(mTarget != null, "Target not set.");
         checkNotNull(sourceFolders, "sourceFolders cannot be null.");
@@ -763,38 +765,72 @@ public class AndroidBuilder {
             throw new IllegalStateException(String.valueOf("llvm-rs-cc is missing"));
         }
 
-        List<File> fullImportList = Lists.newArrayListWithCapacity(importFolders.size() + 2);
-        fullImportList.addAll(importFolders);
+        // gather the files to compile
+        FileGatherer fileGatherer = new FileGatherer();
+        SourceSearcher searcher = new SourceSearcher(sourceFolders, "rs", "fs");
+        searcher.setUseExecutor(false);
+        searcher.search(fileGatherer);
+
+        List<File> renderscriptFiles = fileGatherer.getFiles();
+
+        if (renderscriptFiles.isEmpty()) {
+            return;
+        }
 
         @SuppressWarnings("deprecation")
         String rsPath = mTarget.getPath(IAndroidTarget.ANDROID_RS);
-        fullImportList.add(new File(rsPath));
 
         @SuppressWarnings("deprecation")
         String rsClangPath = mTarget.getPath(IAndroidTarget.ANDROID_RS_CLANG);
-        fullImportList.add(new File(rsClangPath));
 
         // the renderscript compiler doesn't expect the top res folder,
         // but the raw folder directly.
         File rawFolder = new File(resOutputDir, SdkConstants.FD_RES_RAW);
 
-        RenderscriptProcessor processor = new RenderscriptProcessor(
-                renderscript.getAbsolutePath(),
-                fullImportList,
-                sourceOutputDir.getAbsolutePath(),
-                rawFolder.getAbsolutePath(),
-                targetApi,
-                debugBuild,
-                optimLevel,
-                dependencyFileProcessor != null ?
-                        dependencyFileProcessor : sNoOpDependencyFileProcessor,
-                mCmdLineRunner);
 
-        SourceSearcher searcher = new SourceSearcher(sourceFolders, "rs", "fs");
-        searcher.setUseExecutor(false);
-        searcher.search(processor);
-    }
+        // compile all the files in a single pass
+        ArrayList<String> command = Lists.newArrayList();
+
+        command.add(renderscript.getAbsolutePath());
 
+        if (debugBuild) {
+            command.add("-g");
+        }
+
+        command.add("-O");
+        command.add(Integer.toString(optimLevel));
+
+        // add all import paths
+        command.add("-I");
+        command.add(rsPath);
+        command.add("-I");
+        command.add(rsClangPath);
+
+        for (File importPath : importFolders) {
+            if (importPath.isDirectory()) {
+                command.add("-I");
+                command.add(importPath.getAbsolutePath());
+            }
+        }
+
+        // source output
+        command.add("-p");
+        command.add(sourceOutputDir.getAbsolutePath());
+
+        // res output
+        command.add("-o");
+        command.add(rawFolder.getAbsolutePath());
+
+        command.add("-target-api");
+        command.add(Integer.toString(targetApi < 11 ? 11 : targetApi));
+
+        // input files
+        for (File sourceFile : renderscriptFiles) {
+            command.add(sourceFile.getAbsolutePath());
+        }
+
+        mCmdLineRunner.runCmdLine(command);
+    }
 
     /**
      * Computes and returns the leaf folders based on a given file extension.
@@ -815,7 +851,7 @@ public class AndroidBuilder {
             for (List<File> folders : importFolders) {
                 SourceSearcher searcher = new SourceSearcher(folders, extension);
                 searcher.setUseExecutor(false);
-                LeafFolderProcessor processor = new LeafFolderProcessor();
+                LeafFolderGatherer processor = new LeafFolderGatherer();
                 try {
                     searcher.search(processor);
                 } catch (InterruptedException e) {
@@ -837,71 +873,6 @@ public class AndroidBuilder {
     }
 
     /**
-     * Compiles the given renderscript file.
-     *
-     * @param renderscriptFile the renderscript file to compile
-     * @param importFolders all the import folders.
-     * @param sourceOutputDir the output dir in which to generate the source code
-     * @param resOutputDir the output dir in which to generate the bitcode file
-     * @param targetApi the target api
-     * @param debugBuild whether the build is debug
-     * @param optimLevel the optimization level
-     * @param dependencyFileProcessor the dependencyFileProcessor to record the dependencies
-     *                                of the compilation.
-     * @throws IOException
-     * @throws InterruptedException
-     */
-    public void compileRenderscriptFile(@NonNull File renderscriptFile,
-                                        @NonNull List<File> importFolders,
-                                        @NonNull File sourceOutputDir,
-                                        @NonNull File resOutputDir,
-                                        int targetApi,
-                                        boolean debugBuild,
-                                        int optimLevel,
-                                        @Nullable DependencyFileProcessor dependencyFileProcessor)
-            throws IOException, InterruptedException {
-        checkState(mTarget != null, "Target not set.");
-        checkNotNull(renderscriptFile, "renderscriptFile cannot be null.");
-        checkNotNull(importFolders, "importFolders cannot be null.");
-        checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.");
-        checkNotNull(resOutputDir, "resOutputDir cannot be null.");
-
-        File renderscript = mSdkParser.getRenderscriptCompiler();
-        if (renderscript == null || !renderscript.isFile()) {
-            throw new IllegalStateException(String.valueOf("llvm-rs-cc is missing"));
-        }
-
-        List<File> fullImportList = Lists.newArrayListWithCapacity(importFolders.size() + 2);
-        fullImportList.addAll(importFolders);
-
-        @SuppressWarnings("deprecation")
-        String rsPath = mTarget.getPath(IAndroidTarget.ANDROID_RS);
-        fullImportList.add(new File(rsPath));
-
-        @SuppressWarnings("deprecation")
-        String rsClangPath = mTarget.getPath(IAndroidTarget.ANDROID_RS_CLANG);
-        fullImportList.add(new File(rsClangPath));
-
-        // the renderscript compiler doesn't expect the top res folder,
-        // but the raw folder directly.
-        File rawFolder = new File(resOutputDir, SdkConstants.FD_RES_RAW);
-
-        RenderscriptProcessor processor = new RenderscriptProcessor(
-                renderscript.getAbsolutePath(),
-                fullImportList,
-                sourceOutputDir.getAbsolutePath(),
-                rawFolder.getAbsolutePath(),
-                targetApi,
-                debugBuild,
-                optimLevel,
-                dependencyFileProcessor != null ?
-                        dependencyFileProcessor : sNoOpDependencyFileProcessor,
-                mCmdLineRunner);
-
-        processor.processFile(renderscriptFile);
-    }
-
-    /**
      * Converts the bytecode to Dalvik format
      * @param classesLocation the location of the compiler output
      * @param libraries the list of libraries
 
 package com.android.builder.internal.compiler;
 
-import com.google.common.collect.Sets;
+import com.google.common.collect.Lists;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Set;
+import java.util.List;
 
 /**
- * Source Searcher processor, gathering a list of folders containing processed source files.
+ * Source Searcher processor, gathering a list of all the files found by the SourceSearcher.
  */
-public class LeafFolderProcessor implements SourceSearcher.SourceFileProcessor {
-
-    private final Set<File> mFolders = Sets.newHashSet();
+public class FileGatherer implements SourceSearcher.SourceFileProcessor {
+    private final List<File> mFiles = Lists.newArrayList();
 
     @Override
     public void processFile(File sourceFile) throws IOException, InterruptedException {
-        mFolders.add(sourceFile.getParentFile());
+        mFiles.add(sourceFile);
     }
 
-    public Set<File> getFolders() {
-        return mFolders;
+    public List<File> getFiles() {
+        return mFiles;
     }
 }
@@ -25,7 +25,7 @@ import java.util.Set;
 /**
  * Source Searcher processor, gathering a list of folders containing processed source files.
  */
-public class LeafFolderProcessor implements SourceSearcher.SourceFileProcessor {
+public class LeafFolderGatherer implements SourceSearcher.SourceFileProcessor {
 
     private final Set<File> mFolders = Sets.newHashSet();
 
diff --git a/builder/src/main/java/com/android/builder/internal/compiler/RenderscriptProcessor.java b/builder/src/main/java/com/android/builder/internal/compiler/RenderscriptProcessor.java
deleted file mode 100644 (file)
index f94ac00..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2013 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.builder.internal.compiler;
-
-import com.android.annotations.NonNull;
-import com.android.builder.compiling.DependencyFileProcessor;
-import com.android.builder.internal.CommandLineRunner;
-import com.google.common.collect.Lists;
-import com.google.common.io.Files;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A Source File processor for Renderscript files. This compiles each Renderscript file
- * found by the SourceSearcher.
- */
-public class RenderscriptProcessor  implements SourceSearcher.SourceFileProcessor {
-
-    @NonNull private final String mCompilerPath;
-    @NonNull private final List<File> mImportPaths;
-    @NonNull private final String mSourceOutputDir;
-    @NonNull private final String mResOutputDir;
-    private final int mTargetApi;
-    private final boolean mDebugBuild;
-    private final int mOptimLevel;
-    @NonNull private final DependencyFileProcessor mDependencyFileProcessor;
-    @NonNull private final CommandLineRunner mCmdLineRunner;
-
-    public RenderscriptProcessor(@NonNull String compilerPath,
-                                 @NonNull List<File> importPaths,
-                                 @NonNull String sourceOutputDir,
-                                 @NonNull String resOutputDir,
-                                 int targetApi,
-                                 boolean debugBuild,
-                                 int optimLevel,
-                                 @NonNull DependencyFileProcessor dependencyFileProcessor,
-                                 @NonNull CommandLineRunner cmdLineRunner) {
-        mCompilerPath = compilerPath;
-        mImportPaths = importPaths;
-        mSourceOutputDir = sourceOutputDir;
-        mResOutputDir = resOutputDir;
-        mTargetApi = targetApi < 11 ? 11 : targetApi;
-        mDebugBuild = debugBuild;
-        mOptimLevel = optimLevel;
-        mDependencyFileProcessor = dependencyFileProcessor;
-        mCmdLineRunner = cmdLineRunner;
-    }
-
-    @Override
-    public void processFile(File sourceFile) throws IOException, InterruptedException {
-        ArrayList<String> command = Lists.newArrayList();
-
-        command.add(mCompilerPath);
-
-        if (mDebugBuild) {
-            command.add("-g");
-        }
-
-        command.add("-O");
-        command.add(Integer.toString(mOptimLevel));
-
-        for (File importPath : mImportPaths) {
-            if (importPath.isDirectory()) {
-                command.add("-I");
-                command.add(importPath.getAbsolutePath());
-            }
-        }
-
-        command.add("-p");
-        command.add(mSourceOutputDir);
-
-        command.add("-o");
-        command.add(mResOutputDir);
-
-        command.add("-target-api");
-        command.add(Integer.toString(mTargetApi));
-
-        // dependency file
-        File tempFolder = Files.createTempDir();
-        command.add("-d");
-        command.add(tempFolder.getAbsolutePath());
-        command.add("-MD");
-
-        // input file
-        command.add(sourceFile.getAbsolutePath());
-
-        mCmdLineRunner.runCmdLine(command);
-
-        // send the dependency file to the processor.
-        // since it's a temp folder, there should be only one file in it, so use that.
-        File[] files = tempFolder.listFiles();
-        if (files != null && files.length > 0) {
-            File depFile = files[0];
-            if (mDependencyFileProcessor.processFile(depFile)) {
-                depFile.delete();
-            }
-        }
-    }
-}
index e01349b..853a0a9 100644 (file)
  */
 
 package com.android.build.gradle.tasks
-import com.android.build.gradle.internal.tasks.DependencyBasedCompileTask
-import com.android.builder.compiling.DependencyFileProcessor
+import com.android.build.gradle.internal.tasks.BaseTask
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
 /**
  * Task to compile Renderscript files. Supports incremental update.
  */
-public class RenderscriptCompile extends DependencyBasedCompileTask {
+public class RenderscriptCompile extends BaseTask {
 
     // ----- PUBLIC TASK API -----
 
     @OutputDirectory
+    File sourceOutputDir
+
+    @OutputDirectory
     File resOutputDir
 
     // ----- PRIVATE TASK API -----
@@ -47,54 +50,27 @@ public class RenderscriptCompile extends DependencyBasedCompileTask {
     @Input
     boolean debugBuild
 
-    @Override
-    protected boolean isIncremental() {
-        return true
-    }
-
-    @Override
-    protected boolean supportsParallelization() {
-        return false
-    }
-
-    @Override
-    protected Collection<File> getOutputForIncrementalBuild() {
-        return Collections.singletonList(getSourceOutputDir())
-    }
+    @TaskAction
+    void taskAction() {
+        // this is full run (always), clean the previous outputs
+        File sourceDestDir = getSourceOutputDir()
+        emptyFolder(sourceDestDir)
 
-    @Override
-    protected void compileAllFiles(DependencyFileProcessor dependencyFileProcessor) {
+        File resDestDir = getResOutputDir()
+        emptyFolder(resDestDir)
 
+        // get the import folders. If the .rsh files are not directly under the import folders,
+        // we need to get the leaf folders, as this is what llvm-rs-cc expects.
         List<File> importFolders = getBuilder().getLeafFolders("rsh",
                 getImportDirs(), getSourceDirs())
 
         getBuilder().compileAllRenderscriptFiles(
                 getSourceDirs(),
                 importFolders,
-                getSourceOutputDir(),
-                getResOutputDir(),
-                getTargetApi(),
-                getDebugBuild(),
-                getOptimLevel(),
-                dependencyFileProcessor)
-    }
-
-    @Override
-    protected Object incrementalSetup() {
-        return getBuilder().getLeafFolders("rsh", getImportDirs(), getSourceDirs())
-    }
-
-    @Override
-    protected void compileSingleFile(File file,Object data,
-                                     DependencyFileProcessor dependencyFileProcessor) {
-        getBuilder().compileRenderscriptFile(
-                file,
-                (List<File>) data,
-                getSourceOutputDir(),
-                getResOutputDir(),
+                sourceDestDir,
+                resDestDir,
                 getTargetApi(),
                 getDebugBuild(),
-                getOptimLevel(),
-                dependencyFileProcessor)
+                getOptimLevel())
     }
 }
index aad0b5a..05a4a8c 100755 (executable)
@@ -129,8 +129,11 @@ public abstract class BaseTest extends TestCase {
                     .useGradleVersion(gradleVersion)
                     .forProjectDirectory(project)
                     .connect()
-
-            connection.newBuild().forTasks(tasks).run()
+            try {
+                connection.newBuild().forTasks(tasks).withArguments("-i").run()
+            } finally {
+                connection.close()
+            }
         } finally {
             localProp.delete()
         }