blob: 89fff53d7633fc0c646cff45a7b6d427a264914b [file] [log] [blame]
/*
* Copyright (C) 2014 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.jack.test.toolchain;
import com.google.common.base.Joiner;
import com.google.common.io.Files;
import com.android.jack.library.JackLibrary;
import com.android.jack.test.TestsProperties;
import com.android.jack.test.util.ExecFileException;
import com.android.jack.test.util.ExecuteFile;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.annotation.Nonnull;
/**
* This {@link AndroidToolchain} uses Jill as an external tool to convert legacy library format.
*/
public abstract class ExternalJillBasedToolchain
extends JackCliToolchain implements JillBasedToolchain {
@Nonnull
private static final String META_DIR = "meta";
@Nonnull
private static final String RSC_DIR = "rsc";
@Nonnull
private File refCompilerPrebuilt;
@Nonnull
private File jarjarPrebuilt;
@Nonnull
private File proguardPrebuilt;
ExternalJillBasedToolchain(@Nonnull File jackPrebuilt,
@Nonnull File refCompilerPrebuilt, @Nonnull File jarjarPrebuilt,
@Nonnull File proguardPrebuilt) {
super(jackPrebuilt);
this.refCompilerPrebuilt = refCompilerPrebuilt;
this.jarjarPrebuilt = jarjarPrebuilt;
this.proguardPrebuilt = proguardPrebuilt;
}
@Override
public void srcToExe(@Nonnull File out, boolean zipFile, @Nonnull File... sources)
throws Exception {
try {
File jarFile = srcToJar(sources);
libToExe(jarFile, out, zipFile);
} catch (IOException e) {
throw new RuntimeException("Legacy toolchain exited with an error", e);
}
}
@Override
public void srcToLib(@Nonnull File out, boolean zipFiles, @Nonnull File... sources)
throws Exception {
try {
File classesDir;
if (zipFiles) {
classesDir = AbstractTestTools.createTempDir();
} else {
classesDir = out;
}
if (staticLibs.size() > 0) {
for (File staticLib : staticLibs) {
if (staticLib.isDirectory()) {
AbstractTestTools.copyDirectory(staticLib, classesDir);
} else {
assert staticLib.isFile();
AbstractTestTools.unzip(staticLib, classesDir, isVerbose);
}
}
}
if (sources.length > 0) {
compileWithExternalRefCompiler(sources,
getClasspathAsString() + File.pathSeparatorChar
+ classesDir.getPath(), classesDir);
}
File resDestDir;
if (resImport.size() > 0) {
resDestDir = new File(classesDir, RSC_DIR);
if (!resDestDir.exists() && !resDestDir.mkdir()) {
throw new AssertionError("Could not create resource dir");
}
for (File res : resImport) {
AbstractTestTools.copyDirectory(res, resDestDir);
}
}
File metaDestDir;
if (metaImport.size() > 0) {
metaDestDir = new File(classesDir, META_DIR);
if (!metaDestDir.exists() && !metaDestDir.mkdir()) {
throw new AssertionError("Could not create meta dir");
}
for (File meta : metaImport) {
AbstractTestTools.copyDirectory(meta, metaDestDir);
}
}
File tmpJarsDir = AbstractTestTools.createTempDir();
File jarFile = new File(tmpJarsDir, "legacyLib.jar");
File jarFileJarjar = new File(tmpJarsDir, "legacyLibJarjar.jar");
File jarFileProguard = new File(tmpJarsDir, "legacyLibProguard.jar");
AbstractTestTools.createjar(jarFile, classesDir, isVerbose);
if (jarjarRules.size() > 0) {
if (jarjarRules.size() > 1) {
throw new AssertionError("Not yet supported");
}
processWithJarJar(jarjarRules.get(0), jarFile, jarFileJarjar);
} else {
jarFileJarjar = jarFile;
}
if (proguardFlags.size() > 0) {
processWithProguard(getClasspathAsString(), proguardFlags, jarFileJarjar,
jarFileProguard);
} else {
jarFileProguard = jarFileJarjar;
}
if (zipFiles) {
Files.copy(jarFileProguard, out);
} else {
AbstractTestTools.unzip(jarFileProguard, out, isVerbose);
}
} catch (IOException e) {
throw new RuntimeException("Legacy toolchain exited with an error", e);
}
}
@Override
public void libToLib(@Nonnull File[] in, @Nonnull File out, boolean zipFiles) throws Exception {
List<String> commandLine = new ArrayList<String>();
libToCommon(commandLine, convertClasspahtWithJillAsString(), in);
if (zipFiles) {
commandLine.add("--output-jack");
} else {
commandLine.add("--output-jack-dir");
}
commandLine.add(out.getAbsolutePath());
ExecuteFile exec = new ExecuteFile(commandLine.toArray(new String[commandLine.size()]));
exec.inheritEnvironment();
exec.setErr(errRedirectStream);
exec.setOut(outRedirectStream);
exec.setVerbose(isVerbose);
if (exec.run() != 0) {
throw new RuntimeException("Jack compiler exited with an error");
}
}
@Nonnull
protected String convertClasspahtWithJillAsString() throws Exception {
File[] result = new File[classpath.size()];
for (int i = 0; i < classpath.size(); i++) {
result[i] = AbstractTestTools.createTempFile(classpath.get(i).getName(), ".jack");
executeJill(classpath.get(i), result[i]);
}
return AbstractTestTools.getClasspathAsString(result);
}
@Override
@Nonnull
public File[] getDefaultBootClasspath() {
return new File[] {
new File(TestsProperties.getJackRootDir(), "jack-tests/prebuilts/core-stubs-mini.jar"),
new File(TestsProperties.getJackRootDir(), "jack-tests/libs/junit4.jar")
};
}
@Override
@Nonnull
public String getLibraryExtension() {
return ".jar";
}
@Override
@Nonnull
public String getLibraryElementsExtension() {
return ".class";
}
@Nonnull
protected File srcToJar(@Nonnull File... sources) throws Exception {
File jarFile = AbstractTestTools.createTempFile("legacyLib", ".jar");
File classesDir = AbstractTestTools.createTempDir();
List<String> staticLibsAsCp = new ArrayList<String>(staticLibs.size());
for (File staticLib : staticLibs) {
staticLibsAsCp.add(staticLib.getAbsolutePath());
}
String staticLibsAsCpAsString = Joiner.on(File.pathSeparatorChar).join(staticLibsAsCp);
if (sources.length > 0) {
compileWithExternalRefCompiler(sources,
getClasspathAsString() + File.pathSeparatorChar + staticLibsAsCpAsString, classesDir);
}
AbstractTestTools.createjar(jarFile, classesDir, isVerbose);
return jarFile;
}
protected void compileWithExternalRefCompiler(@Nonnull File[] sources,
@Nonnull String classpath, @Nonnull File out) {
List<String> commandLine = new ArrayList<String>();
commandLine.add(refCompilerPrebuilt.getPath());
if (isVerbose) {
commandLine.add("-verbose");
}
addSourceLevel(sourceLevel, commandLine);
if (annotationProcessorClasses != null) {
commandLine.add("-processor");
commandLine.add(Joiner.on(',').join(annotationProcessorClasses));
}
if (classpath != null) {
commandLine.add("-classpath");
commandLine.add(classpath);
}
AbstractTestTools.addFile(commandLine, false, sources);
if (withDebugInfos) {
commandLine.add("-g");
}
commandLine.add("-d");
commandLine.add(out.getAbsolutePath());
ExecuteFile execFile = new ExecuteFile(commandLine.toArray(new String[commandLine.size()]));
execFile.inheritEnvironment();
execFile.setErr(errRedirectStream);
execFile.setOut(outRedirectStream);
execFile.setVerbose(isVerbose);
try {
if (execFile.run() != 0) {
throw new RuntimeException("Reference compiler exited with an error");
}
} catch (ExecFileException e) {
throw new RuntimeException("An error occurred while running reference compiler", e);
}
}
protected void processWithJarJar(@Nonnull File jarjarRules,
@Nonnull File inJar, @Nonnull File outJar) {
boolean assertEnable = false;
assert true == (assertEnable = true);
String[] commandLine = new String[] {"java", (assertEnable ? "-ea" : "-da"),
"-Dverbose=" + String.valueOf(isVerbose), "-jar", jarjarPrebuilt.getAbsolutePath(),
"process", jarjarRules.getAbsolutePath(), inJar.getAbsolutePath(),
outJar.getAbsolutePath()};
ExecuteFile execFile = new ExecuteFile(commandLine);
execFile.inheritEnvironment();
execFile.setOut(outRedirectStream);
execFile.setErr(errRedirectStream);
execFile.setVerbose(isVerbose);
try {
if (execFile.run() != 0) {
throw new RuntimeException("JarJar exited with an error");
}
} catch (ExecFileException e) {
throw new RuntimeException("An error occurred while running Jarjar", e);
}
}
protected void processWithProguard(@Nonnull String bootclasspathStr,
@Nonnull List<File> proguardFlags, @Nonnull File inJar, @Nonnull File outJar) {
boolean assertEnable = false;
assert true == (assertEnable = true);
List<String> commandLine = new ArrayList<String>();
commandLine.add("java");
commandLine.add(assertEnable ? "-ea" : "-da");
commandLine.add("-jar");
commandLine.add(proguardPrebuilt.getAbsolutePath());
commandLine.add("-injar");
commandLine.add(inJar.getAbsolutePath());
commandLine.add("-outjars");
commandLine.add(outJar.getAbsolutePath());
commandLine.add("-libraryjars");
commandLine.add(bootclasspathStr);
if (isVerbose) {
commandLine.add("-verbose");
}
for (File flags : proguardFlags) {
commandLine.add("-include");
commandLine.add(flags.getAbsolutePath());
}
ExecuteFile execFile = new ExecuteFile(commandLine.toArray(new String[commandLine.size()]));
execFile.inheritEnvironment();
execFile.setOut(outRedirectStream);
execFile.setErr(errRedirectStream);
execFile.setVerbose(isVerbose);
try {
if (execFile.run() != 0) {
throw new RuntimeException("Proguard exited with an error");
}
} catch (ExecFileException e) {
throw new RuntimeException("An error occurred while running Proguard", e);
}
}
protected static void addSourceLevel(
@Nonnull SourceLevel level, @Nonnull List<String> commandLine) {
commandLine.add("-source");
switch (level) {
case JAVA_6:
commandLine.add("1.6");
break;
case JAVA_7:
commandLine.add("1.7");
break;
case JAVA_8:
commandLine.add("1.8");
break;
default:
throw new AssertionError("Unkown level: '" + level.toString() + "'");
}
}
@Override
protected void libToImportStaticLibs(@Nonnull List<String> commandLine, @Nonnull File[] in)
throws Exception {
for (int i = 0; i < in.length; i++) {
File tmpDir = AbstractTestTools.createTempDir();
File jilledLib = new File(tmpDir, "jilledLib_" + i + ".jack");
executeJillWithResources(in[i], jilledLib, /* zipFiles = */ true);
commandLine.add("--import");
commandLine.add(jilledLib.getAbsolutePath());
}
for (int i = 0; i < staticLibs.size(); i++) {
File jilledLib = AbstractTestTools.createTempFile("jilledLib", ".jack");
executeJillWithResources(staticLibs.get(i), jilledLib, /* zipFiles = */ true);
commandLine.add("--import");
commandLine.add(jilledLib.getAbsolutePath());
}
}
private void executeJillWithResources(@Nonnull File in, @Nonnull File out, boolean zipFiles)
throws Exception {
File tmpOut = AbstractTestTools.createTempFile("out", ".jack");
executeJill(in, tmpOut);
File rscDir;
if (in.isDirectory()) {
rscDir = new File(in, RSC_DIR);
} else {
// Assume it's a library archive
File tmpUnzippedLib = AbstractTestTools.createTempDir();
AbstractTestTools.unzip(in, tmpUnzippedLib, isVerbose);
rscDir = new File(tmpUnzippedLib, RSC_DIR);
}
if (rscDir.exists()) {
File tmpUnzippedOutLib = AbstractTestTools.createTempDir();
AbstractTestTools.unzip(tmpOut, tmpUnzippedOutLib, isVerbose);
File destRscDir = new File(tmpUnzippedOutLib, RSC_DIR);
if (!destRscDir.mkdir()) {
throw new AssertionError("Could not create directory: '" + destRscDir.getPath() + "'");
}
AbstractTestTools.copyDirectory(rscDir, destRscDir);
File jackProperties;
jackProperties = new File(tmpUnzippedOutLib, JackLibrary.LIBRARY_PROPERTIES);
Properties prop = new Properties();
FileReader fr = new FileReader(jackProperties);
try {
prop.load(fr);
} finally {
fr.close();
}
prop.setProperty(RSC_DIR, "true");
FileWriter fw = new FileWriter(jackProperties);
try {
prop.store(fw, "Edited by legacy-jill toolchain");
} finally {
fw.close();
}
AbstractTestTools.zip(tmpUnzippedOutLib, out, isVerbose);
} else {
Files.copy(tmpOut, out);
}
}
@Override
public void libToExe(@Nonnull File[] in, @Nonnull File out, boolean zipFile) throws Exception {
List<String> commandLine = new ArrayList<String>();
libToCommon(commandLine, convertClasspahtWithJillAsString(), in);
if (zipFile) {
commandLine.add("--output-dex-zip");
} else {
commandLine.add("--output-dex");
}
commandLine.add(out.getAbsolutePath());
ExecuteFile exec = new ExecuteFile(commandLine.toArray(new String[commandLine.size()]));
exec.inheritEnvironment();
exec.setErr(errRedirectStream);
exec.setOut(outRedirectStream);
exec.setVerbose(isVerbose);
if (exec.run() != 0) {
throw new RuntimeException("Jack compiler exited with an error");
}
}
protected abstract void executeJill(@Nonnull File in, @Nonnull File out) throws Exception;
}