All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.builder.internal.compiler.RenderScriptProcessor Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * 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 static com.android.SdkConstants.EXT_BC;
import static com.android.SdkConstants.FN_RENDERSCRIPT_V8_JAR;

import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.internal.LoggedErrorException;
import com.android.ide.common.internal.WaitableExecutor;
import com.android.ide.common.process.ProcessException;
import com.android.ide.common.process.ProcessExecutor;
import com.android.ide.common.process.ProcessInfoBuilder;
import com.android.ide.common.process.ProcessOutputHandler;
import com.android.ide.common.process.ProcessResult;
import com.android.sdklib.BuildToolInfo;
import com.google.common.collect.Maps;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;

/**
 * Compiles Renderscript files.
 */
public class RenderScriptProcessor {

    // ABI list, as pairs of (android-ABI, toolchain-ABI)
    private static final class Abi {

        @NonNull
        private final String mDevice;
        @NonNull
        private final String mToolchain;
        @NonNull
        private final BuildToolInfo.PathId mLinker;
        @NonNull
        private final String[] mLinkerArgs;

        Abi(@NonNull String device,
            @NonNull String toolchain,
            @NonNull BuildToolInfo.PathId linker,
            @NonNull String... linkerArgs) {

            mDevice = device;
            mToolchain = toolchain;
            mLinker = linker;
            mLinkerArgs = linkerArgs;
        }
    }

    private static final Abi[] ABIS = {
            new Abi("armeabi-v7a", "armv7-none-linux-gnueabi", BuildToolInfo.PathId.LD_ARM,
                    "-dynamic-linker", "/system/bin/linker", "-X", "-m", "armelf_linux_eabi"),
            new Abi("mips", "mipsel-unknown-linux", BuildToolInfo.PathId.LD_MIPS, "-EL"),
            new Abi("x86", "i686-unknown-linux", BuildToolInfo.PathId.LD_X86, "-m", "elf_i386") };

    public static final String RS_DEPS = "rsDeps";

    @NonNull
    private final List mSourceFolders;

    @NonNull
    private final List mImportFolders;

    @NonNull
    private final File mSourceOutputDir;

    @NonNull
    private final File mResOutputDir;

    @NonNull
    private final File mObjOutputDir;

    @NonNull
    private final File mLibOutputDir;

    @NonNull
    private final BuildToolInfo mBuildToolInfo;

    private final int mTargetApi;

    private final boolean mDebugBuild;

    private final int mOptimLevel;

    private final boolean mNdkMode;

    private final boolean mSupportMode;
    private final Set mAbiFilters;

    private final File mRsLib;
    private final Map mLibClCore = Maps.newHashMap();

    public RenderScriptProcessor(
            @NonNull List sourceFolders,
            @NonNull List importFolders,
            @NonNull File sourceOutputDir,
            @NonNull File resOutputDir,
            @NonNull File objOutputDir,
            @NonNull File libOutputDir,
            @NonNull BuildToolInfo buildToolInfo,
            int targetApi,
            boolean debugBuild,
            int optimLevel,
            boolean ndkMode,
            boolean supportMode,
            @Nullable Set abiFilters) {
        mSourceFolders = sourceFolders;
        mImportFolders = importFolders;
        mSourceOutputDir = sourceOutputDir;
        mResOutputDir = resOutputDir;
        mObjOutputDir = objOutputDir;
        mLibOutputDir = libOutputDir;
        mBuildToolInfo = buildToolInfo;
        mTargetApi = targetApi;
        mDebugBuild = debugBuild;
        mOptimLevel = optimLevel;
        mNdkMode = ndkMode;
        mSupportMode = supportMode;
        mAbiFilters = abiFilters;

        if (supportMode) {
            File rs = new File(mBuildToolInfo.getLocation(), "renderscript");
            mRsLib = new File(rs, "lib");
            File bcFolder = new File(mRsLib, "bc");
            for (Abi abi : ABIS) {
                mLibClCore.put(abi.mDevice,
                        new File(bcFolder, abi.mDevice + File.separatorChar + "libclcore.bc"));
            }
        } else {
            mRsLib = null;
        }
    }

    public static File getSupportJar(String buildToolsFolder) {
        return new File(buildToolsFolder, "renderscript/lib/" + FN_RENDERSCRIPT_V8_JAR);
    }

    public static File getSupportNativeLibFolder(String buildToolsFolder) {
        File rs = new File(buildToolsFolder, "renderscript");
        File lib = new File(rs, "lib");
        return new File(lib, "packaged");
    }

    public void build(
            @NonNull ProcessExecutor processExecutor,
            @NonNull ProcessOutputHandler processOutputHandler)
            throws InterruptedException, ProcessException, LoggedErrorException, IOException {

        // gather the files to compile
        FileGatherer fileGatherer = new FileGatherer();
        SourceSearcher searcher = new SourceSearcher(mSourceFolders, "rs", "fs");
        searcher.setUseExecutor(false);
        searcher.search(fileGatherer);

        List renderscriptFiles = fileGatherer.getFiles();

        if (renderscriptFiles.isEmpty()) {
            return;
        }

        // get the env var
        Map env = Maps.newHashMap();
        if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
            env.put("DYLD_LIBRARY_PATH", mBuildToolInfo.getLocation().getAbsolutePath());
        } else if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX) {
            env.put("LD_LIBRARY_PATH", mBuildToolInfo.getLocation().getAbsolutePath());
        }

        doMainCompilation(renderscriptFiles, processExecutor, processOutputHandler, env);

        if (mSupportMode) {
            createSupportFiles(processExecutor, processOutputHandler, env);
        }
    }

    private void doMainCompilation(
            @NonNull List inputFiles,
            @NonNull ProcessExecutor processExecutor,
            @NonNull ProcessOutputHandler processOutputHandler,
            @NonNull Map env)
            throws ProcessException {
        ProcessInfoBuilder builder = new ProcessInfoBuilder();

        String renderscript = mBuildToolInfo.getPath(BuildToolInfo.PathId.LLVM_RS_CC);
        if (renderscript == null || !new File(renderscript).isFile()) {
            throw new IllegalStateException(BuildToolInfo.PathId.LLVM_RS_CC + " is missing");
        }

        String rsPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.ANDROID_RS);
        String rsClangPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.ANDROID_RS_CLANG);

        // the renderscript compiler doesn't expect the top res folder,
        // but the raw folder directly.
        File rawFolder = new File(mResOutputDir, SdkConstants.FD_RES_RAW);

        // compile all the files in a single pass
        builder.setExecutable(renderscript);
        builder.addEnvironments(env);

        // Due to a device side bug, let's not enable this at this time.
//        if (mDebugBuild) {
//            command.add("-g");
//        }

        builder.addArgs("-O");
        builder.addArgs(Integer.toString(mOptimLevel));

        // add all import paths
        builder.addArgs("-I");
        builder.addArgs(rsPath);
        builder.addArgs("-I");
        builder.addArgs(rsClangPath);

        for (File importPath : mImportFolders) {
            if (importPath.isDirectory()) {
                builder.addArgs("-I");
                builder.addArgs(importPath.getAbsolutePath());
            }
        }

        if (mSupportMode) {
            builder.addArgs("-rs-package-name=android.support.v8.renderscript");
        }

        // source output
        builder.addArgs("-p");
        builder.addArgs(mSourceOutputDir.getAbsolutePath());

        if (mNdkMode) {
            builder.addArgs("-reflect-c++");
        }

        // res output
        builder.addArgs("-o");
        builder.addArgs(rawFolder.getAbsolutePath());

        builder.addArgs("-target-api");
        int targetApi = mTargetApi < 11 ? 11 : mTargetApi;
        targetApi = (mSupportMode && targetApi < 18) ? 18 : targetApi;
        builder.addArgs(Integer.toString(targetApi));

        // input files
        for (File sourceFile : inputFiles) {
            builder.addArgs(sourceFile.getAbsolutePath());
        }

        ProcessResult result = processExecutor.execute(
                builder.createProcess(), processOutputHandler);
        result.rethrowFailure().assertNormalExitValue();
    }

    private void createSupportFiles(
            @NonNull final ProcessExecutor processExecutor,
            @NonNull final ProcessOutputHandler processOutputHandler,
            @NonNull final Map env)
            throws IOException, InterruptedException, LoggedErrorException, ProcessException {
        // get the generated BC files.
        File rawFolder = new File(mResOutputDir, SdkConstants.FD_RES_RAW);

        SourceSearcher searcher = new SourceSearcher(
                Collections.singletonList(rawFolder), EXT_BC);
        FileGatherer fileGatherer = new FileGatherer();
        searcher.search(fileGatherer);

        WaitableExecutor mExecutor  = new WaitableExecutor();

        for (final File bcFile : fileGatherer.getFiles()) {
            String name = bcFile.getName();
            final String objName = name.replaceAll("\\.bc", ".o");
            final String soName = "librs." + name.replaceAll("\\.bc", ".so");

            for (final Abi abi : ABIS) {
                if (mAbiFilters != null && !mAbiFilters.contains(abi.mDevice)) {
                    continue;
                }

                // make sure the dest folders exist
                final File objAbiFolder = new File(mObjOutputDir, abi.mDevice);
                if (!objAbiFolder.isDirectory() && !objAbiFolder.mkdirs()) {
                    throw new IOException("Unable to create dir " + objAbiFolder.getAbsolutePath());
                }

                final File libAbiFolder = new File(mLibOutputDir, abi.mDevice);
                if (!libAbiFolder.isDirectory() && !libAbiFolder.mkdirs()) {
                    throw new IOException("Unable to create dir " + libAbiFolder.getAbsolutePath());
                }

                mExecutor.execute(new Callable() {
                    @Override
                    public Void call() throws Exception {
                        File objFile = createSupportObjFile(
                                bcFile,
                                abi,
                                objName,
                                objAbiFolder,
                                processExecutor,
                                processOutputHandler,
                                env);
                        createSupportLibFile(
                                objFile,
                                abi,
                                soName,
                                libAbiFolder,
                                processExecutor,
                                processOutputHandler,
                                env);
                        return null;
                    }
                });
            }
        }

        mExecutor.waitForTasksWithQuickFail(true /*cancelRemaining*/);
    }

    private File createSupportObjFile(
            @NonNull File bcFile,
            @NonNull Abi abi,
            @NonNull String objName,
            @NonNull File objAbiFolder,
            @NonNull ProcessExecutor processExecutor,
            @NonNull ProcessOutputHandler processOutputHandler,
            @NonNull Map env) throws ProcessException {

        ProcessInfoBuilder builder = new ProcessInfoBuilder();
        builder.setExecutable(mBuildToolInfo.getPath(BuildToolInfo.PathId.BCC_COMPAT));
        builder.addEnvironments(env);

        builder.addArgs("-O" + Integer.toString(mOptimLevel));

        File outFile = new File(objAbiFolder, objName);
        builder.addArgs("-o", outFile.getAbsolutePath());

        builder.addArgs("-fPIC");
        builder.addArgs("-shared");

        builder.addArgs("-rt-path", mLibClCore.get(abi.mDevice).getAbsolutePath());

        builder.addArgs("-mtriple", abi.mToolchain);

        builder.addArgs(bcFile.getAbsolutePath());

        processExecutor.execute(
                builder.createProcess(), processOutputHandler)
                .rethrowFailure().assertNormalExitValue();

        return outFile;
    }

    private void createSupportLibFile(
            @NonNull File objFile,
            @NonNull Abi abi,
            @NonNull String soName,
            @NonNull File libAbiFolder,
            @NonNull ProcessExecutor processExecutor,
            @NonNull ProcessOutputHandler processOutputHandler,
            @NonNull Map env) throws ProcessException {

        File intermediatesFolder = new File(mRsLib, "intermediates");
        File intermediatesAbiFolder = new File(intermediatesFolder, abi.mDevice);
        File packagedFolder = new File(mRsLib, "packaged");
        File packagedAbiFolder = new File(packagedFolder, abi.mDevice);

        ProcessInfoBuilder builder = new ProcessInfoBuilder();
        builder.setExecutable(mBuildToolInfo.getPath(abi.mLinker));
        builder.addEnvironments(env);

        builder.addArgs("--eh-frame-hdr")
                .addArgs(abi.mLinkerArgs)
                .addArgs("-shared", "-Bsymbolic", "-z", "noexecstack", "-z", "relro", "-z", "now");

        File outFile = new File(libAbiFolder, soName);
        builder.addArgs("-o", outFile.getAbsolutePath());

        builder.addArgs(
                "-L" + intermediatesAbiFolder.getAbsolutePath(),
                "-L" + packagedAbiFolder.getAbsolutePath(),
                "-soname",
                soName,
                objFile.getAbsolutePath(),
                new File(intermediatesAbiFolder, "libcompiler_rt.a").getAbsolutePath(),
                "-lRSSupport",
                "-lm",
                "-lc");

        processExecutor.execute(
                builder.createProcess(), processOutputHandler)
                .rethrowFailure().assertNormalExitValue();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy