blob: b5219b99acb3da6506d54eb5f51326d6681c0bd0 [file] [log] [blame]
/*
* 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 File mLibClCore;
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");
mLibClCore = new File(mRsLib, "libclcore.bc");
} else {
mLibClCore = null;
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.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();
}
}