| /* |
| * 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.sdklib.build; |
| |
| 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.sdklib.BuildToolInfo; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * 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<File> mInputs; |
| |
| @NonNull |
| private final List<File> mImportFolders; |
| |
| @NonNull |
| private final File mBuildFolder; |
| |
| @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 mSupportMode; |
| |
| private final File mRsLib; |
| private final Map<String, File> mLibClCore = Maps.newHashMap(); |
| |
| public interface CommandLineLauncher { |
| void launch( |
| @NonNull File executable, |
| @NonNull List<String> arguments, |
| @NonNull Map<String, String> envVariableMap) |
| throws IOException, InterruptedException; |
| } |
| |
| public RenderScriptProcessor( |
| @NonNull List<File> inputs, |
| @NonNull List<File> importFolders, |
| @NonNull File buildFolder, |
| @NonNull File sourceOutputDir, |
| @NonNull File resOutputDir, |
| @NonNull File objOutputDir, |
| @NonNull File libOutputDir, |
| @NonNull BuildToolInfo buildToolInfo, |
| int targetApi, |
| boolean debugBuild, |
| int optimLevel, |
| boolean supportMode) { |
| mInputs = inputs; |
| mImportFolders = importFolders; |
| mBuildFolder = buildFolder; |
| mSourceOutputDir = sourceOutputDir; |
| mResOutputDir = resOutputDir; |
| mObjOutputDir = objOutputDir; |
| mLibOutputDir = libOutputDir; |
| mBuildToolInfo = buildToolInfo; |
| mTargetApi = targetApi; |
| mDebugBuild = debugBuild; |
| mOptimLevel = optimLevel; |
| mSupportMode = supportMode; |
| |
| 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 void cleanOldOutput(@Nullable Collection<File> oldOutputs) { |
| if (oldOutputs != null) { |
| // the old output collections contains the bc and .java files that could be |
| // in a folder shared with other output files, so it's useful to delete |
| // those only. |
| |
| for (File file : oldOutputs) { |
| file.delete(); |
| } |
| } |
| |
| // however .o and .so from support mode are in their own folder so we delete the |
| // content of those folders directly. |
| deleteFolder(mObjOutputDir); |
| deleteFolder(mLibOutputDir); |
| } |
| |
| 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 CommandLineLauncher launcher) |
| throws IOException, InterruptedException { |
| |
| // get the env var |
| Map<String, String> 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(launcher, env); |
| |
| if (mSupportMode) { |
| createSupportFiles(launcher, env); |
| } |
| } |
| |
| private void doMainCompilation(@NonNull CommandLineLauncher launcher, |
| @NonNull Map<String, String> env) |
| throws IOException, InterruptedException { |
| if (mInputs.isEmpty()) { |
| return; |
| } |
| |
| 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 |
| ArrayList<String> command = Lists.newArrayListWithExpectedSize(25); |
| |
| // Due to a device side bug, let's not enable this at this time. |
| // if (mDebugBuild) { |
| // command.add("-g"); |
| // } |
| |
| command.add("-O"); |
| command.add(Integer.toString(mOptimLevel)); |
| |
| // add all import paths |
| command.add("-I"); |
| command.add(rsPath); |
| command.add("-I"); |
| command.add(rsClangPath); |
| |
| for (File importPath : mImportFolders) { |
| if (importPath.isDirectory()) { |
| command.add("-I"); |
| command.add(importPath.getAbsolutePath()); |
| } |
| } |
| |
| command.add("-d"); |
| command.add(new File(mBuildFolder, RS_DEPS).getAbsolutePath()); |
| command.add("-MD"); |
| |
| if (mSupportMode) { |
| command.add("-rs-package-name=android.support.v8.renderscript"); |
| } |
| |
| // source output |
| command.add("-p"); |
| command.add(mSourceOutputDir.getAbsolutePath()); |
| |
| // res output |
| command.add("-o"); |
| command.add(rawFolder.getAbsolutePath()); |
| |
| command.add("-target-api"); |
| int targetApi = mTargetApi < 11 ? 11 : mTargetApi; |
| targetApi = (mSupportMode && targetApi < 18) ? 18 : targetApi; |
| command.add(Integer.toString(targetApi)); |
| |
| // input files |
| for (File sourceFile : mInputs) { |
| command.add(sourceFile.getAbsolutePath()); |
| } |
| |
| launcher.launch(new File(renderscript), command, env); |
| } |
| |
| private void createSupportFiles(@NonNull CommandLineLauncher launcher, |
| @NonNull Map<String, String> env) throws IOException, InterruptedException { |
| // 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); |
| |
| for (File bcFile : fileGatherer.getFiles()) { |
| String name = bcFile.getName(); |
| String objName = name.replaceAll("\\.bc", ".o"); |
| String soName = "librs." + name.replaceAll("\\.bc", ".so"); |
| |
| for (Abi abi : ABIS) { |
| File objFile = createSupportObjFile(bcFile, abi, objName, launcher, env); |
| createSupportLibFile(objFile, abi, soName, launcher, env); |
| } |
| } |
| } |
| |
| private File createSupportObjFile( |
| @NonNull File bcFile, |
| @NonNull Abi abi, |
| @NonNull String objName, |
| @NonNull CommandLineLauncher launcher, |
| @NonNull Map<String, String> env) throws IOException, InterruptedException { |
| |
| |
| // make sure the dest folder exist |
| File abiFolder = new File(mObjOutputDir, abi.mDevice); |
| if (!abiFolder.isDirectory() && !abiFolder.mkdirs()) { |
| throw new IOException("Unable to create dir " + abiFolder.getAbsolutePath()); |
| } |
| |
| File exe = new File(mBuildToolInfo.getPath(BuildToolInfo.PathId.BCC_COMPAT)); |
| |
| List<String> args = Lists.newArrayListWithExpectedSize(10); |
| |
| args.add("-O" + Integer.toString(mOptimLevel)); |
| |
| File outFile = new File(abiFolder, objName); |
| args.add("-o"); |
| args.add(outFile.getAbsolutePath()); |
| |
| args.add("-fPIC"); |
| args.add("-shared"); |
| |
| args.add("-rt-path"); |
| args.add(mLibClCore.get(abi.mDevice).getAbsolutePath()); |
| |
| args.add("-mtriple"); |
| args.add(abi.mToolchain); |
| |
| args.add(bcFile.getAbsolutePath()); |
| |
| launcher.launch(exe, args, env); |
| |
| return outFile; |
| } |
| |
| private void createSupportLibFile( |
| @NonNull File objFile, |
| @NonNull Abi abi, |
| @NonNull String soName, |
| @NonNull CommandLineLauncher launcher, |
| @NonNull Map<String, String> env) throws IOException, InterruptedException { |
| |
| // make sure the dest folder exist |
| File abiFolder = new File(mLibOutputDir, abi.mDevice); |
| if (!abiFolder.isDirectory() && !abiFolder.mkdirs()) { |
| throw new IOException("Unable to create dir " + abiFolder.getAbsolutePath()); |
| } |
| |
| 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); |
| |
| List<String> args = Lists.newArrayListWithExpectedSize(25); |
| |
| args.add("--eh-frame-hdr"); |
| Collections.addAll(args, abi.mLinkerArgs); |
| args.add("-shared"); |
| args.add("-Bsymbolic"); |
| args.add("-z"); |
| args.add("noexecstack"); |
| args.add("-z"); |
| args.add("relro"); |
| args.add("-z"); |
| args.add("now"); |
| |
| File outFile = new File(abiFolder, soName); |
| args.add("-o"); |
| args.add(outFile.getAbsolutePath()); |
| |
| args.add("-L" + intermediatesAbiFolder.getAbsolutePath()); |
| args.add("-L" + packagedAbiFolder.getAbsolutePath()); |
| |
| args.add("-soname"); |
| args.add(soName); |
| |
| args.add(objFile.getAbsolutePath()); |
| args.add(new File(intermediatesAbiFolder, "libcompiler_rt.a").getAbsolutePath()); |
| |
| args.add("-lRSSupport"); |
| args.add("-lm"); |
| args.add("-lc"); |
| |
| File exe = new File(mBuildToolInfo.getPath(abi.mLinker)); |
| |
| launcher.launch(exe, args, env); |
| } |
| |
| protected static void deleteFolder(File folder) { |
| File[] files = folder.listFiles(); |
| if (files != null && files.length > 0) { |
| for (File file : files) { |
| if (file.isDirectory()) { |
| deleteFolder(file); |
| } else { |
| file.delete(); |
| } |
| } |
| } |
| |
| folder.delete(); |
| } |
| } |