blob: 9441fecf1a54a38fbbae46cd5ad89830f297dbeb [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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 org.jetbrains.android.compiler.tools;
import com.intellij.openapi.util.io.FileUtilRt;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace", "SSBasedInspection"})
public class AndroidDxRunner {
@NonNls private final static String DEX_MAIN = "com.android.dx.command.dexer.Main";
@NonNls private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole";
@NonNls private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments";
@NonNls private final static String MAIN_RUN = "run";
private static Method myMethod;
private static Constructor<?> myConstructor;
private static Field myOutNameField;
private static Field myVerboseField;
private static Field myForceJumboField;
private static Field myCoreLibraryField;
private static Field myJarOutputField;
private static Field myFileNamesField;
private static Field myStrictNameCheckField;
private static Field myOptimizeField;
private static Field myConsoleOut;
private static Field myConsoleErr;
private AndroidDxRunner() { }
private static void loadDex(String dxPath) {
try {
File f = new File(dxPath);
if (!f.isFile()) {
System.err.println("File not found: " + dxPath);
return;
}
URL url = f.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[]{url}, AndroidDxRunner.class.getClassLoader());
Class<?> mainClass = loader.loadClass(DEX_MAIN);
Class<?> consoleClass = loader.loadClass(DEX_CONSOLE);
Class<?> argClass = loader.loadClass(DEX_ARGS);
myMethod = mainClass.getMethod(MAIN_RUN, argClass);
myConstructor = argClass.getConstructor();
myOutNameField = argClass.getField("outName");
myJarOutputField = argClass.getField("jarOutput");
myFileNamesField = argClass.getField("fileNames");
myVerboseField = argClass.getField("verbose");
myStrictNameCheckField = argClass.getField("strictNameCheck");
myForceJumboField = getFieldIfPossible(argClass, "forceJumbo");
myCoreLibraryField = getFieldIfPossible(argClass, "coreLibrary");
myOptimizeField = getFieldIfPossible(argClass, "optimize");
myConsoleOut = consoleClass.getField("out");
myConsoleErr = consoleClass.getField("err");
}
catch (SecurityException e) {
reportError("Unable to find API for dex.jar", e);
}
catch (NoSuchMethodException e) {
reportError("Unable to find method for dex.jar", e);
}
catch (NoSuchFieldException e) {
reportError("Unable to find field for dex.jar", e);
}
catch (MalformedURLException e) {
reportError("Failed to load dx.jar", e);
}
catch (ClassNotFoundException e) {
reportError("Failed to load dx.jar", e);
}
}
@Nullable
private static Field getFieldIfPossible(Class<?> argClass, String name) {
try {
return argClass.getField(name);
}
catch (NoSuchFieldException e) {
return null;
}
}
private static int runDex(String dxPath,
String outFilePath,
String[] fileNames,
boolean optimize,
boolean forceJumbo,
boolean coreLibrary) {
loadDex(dxPath);
try {
myConsoleErr.set(null, System.err);
myConsoleOut.set(null, System.out);
Object args = myConstructor.newInstance();
myOutNameField.set(args, outFilePath);
myFileNamesField.set(args, fileNames);
myJarOutputField.set(args, FileUtilRt.extensionEquals(new File(outFilePath).getName(), "jar"));
myVerboseField.set(args, false);
myStrictNameCheckField.set(args, false);
if (myOptimizeField != null) {
myOptimizeField.set(args, optimize);
}
else {
reportWarning("Cannot find 'optimize' field. The option won't be passed to DEX");
}
if (myForceJumboField != null) {
myForceJumboField.set(args, forceJumbo);
}
else {
reportWarning("Cannot find 'forceJumbo' field. The option won't be passed to DEX");
}
if (myCoreLibraryField != null) {
myCoreLibraryField.set(args, coreLibrary);
}
else {
reportWarning("Cannot find 'coreLibrary' field. The option won't be passed to DEX");
}
Object res = myMethod.invoke(null, args);
if (res instanceof Integer) {
return ((Integer)res).intValue();
}
}
catch (IllegalAccessException e) {
reportError("Unable to execute DX", e);
}
catch (InstantiationException e) {
reportError("Unable to execute DX", e);
}
catch (InvocationTargetException e) {
Throwable targetException = e.getTargetException();
reportError("Unable to execute DX", targetException != null ? targetException : e);
}
return -1;
}
private static void reportError(String message, Throwable t) {
System.err.println(message);
t.printStackTrace();
}
private static void reportWarning(String message) {
System.err.println("warning: " + message);
}
private static void collectFiles(File root, Collection<String> result, Set<String> visited, Set<String> qNames) throws IOException {
collectFiles(root.getParentFile(), root, result, visited, qNames);
}
private static void collectFiles(File root, File file, Collection<String> result, Set<String> visited, Set<String> qNames)
throws IOException {
String path = file.getCanonicalPath();
if (!visited.add(path)) {
return;
}
if (file.isDirectory()) {
final File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
collectFiles(root, child, result, visited, qNames);
}
}
}
else {
if (FileUtilRt.extensionEquals(file.getName(), "class")) {
final String qName = getQualifiedName(root, file);
if (qName != null && !qNames.add(qName)) {
reportWarning(FileUtilRt.toSystemDependentName(file.getPath()) + " won't be added. Class " +
qName + " already exists in classpath");
return;
}
}
result.add(path);
}
}
@Nullable
private static String getQualifiedName(File root, File classFile) {
String relativePath = FileUtilRt.getRelativePath(root, classFile);
if (relativePath == null) {
return null;
}
return FileUtilRt.getNameWithoutExtension(FileUtilRt.toSystemIndependentName(relativePath)).replace('/', '.');
}
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Error: dx path must be passed as first argument");
}
String dxPath = args[0];
if (args.length == 1) {
System.err.println("Error: out file path must be passed as second argument");
}
String outFilePath = args[1];
if (args.length == 2) {
System.err.println("Error: no files");
}
Set<String> files = new HashSet<String>();
HashSet<String> visited = new HashSet<String>();
HashSet<String> qNames = new HashSet<String>();
boolean optimize = true;
boolean forceJumbo = false;
boolean coreLibrary = false;
int i = 2;
while (i < args.length && args[i].startsWith("--")) {
if ("--optimize".equals(args[i])) {
i++;
if (i < args.length) {
optimize = Boolean.parseBoolean(args[i]);
}
}
else if ("--forceJumbo".equals(args[i])) {
i++;
if (i < args.length) {
forceJumbo = Boolean.parseBoolean(args[i]);
}
}
else if ("--coreLibrary".equals(args[i])) {
coreLibrary = true;
}
i++;
}
while (i < args.length) {
String arg = args[i];
if ("--exclude".equals(arg)) {
break;
}
File file = new File(arg);
if (file.exists()) {
try {
collectFiles(file, files, visited, qNames);
}
catch (IOException e) {
reportError("I/O error", e);
}
}
i++;
}
String[] excludedFiles = new String[args.length - i - 1];
System.arraycopy(args, i + 1, excludedFiles, 0, excludedFiles.length);
files.removeAll(Arrays.asList(excludedFiles));
String[] filesArray = files.toArray(new String[files.size()]);
//System.out.println("file names: " + concat(filesArray));
runDex(dxPath, outFilePath, filesArray, optimize, forceJumbo, coreLibrary);
}
private static String concat(String[] ar) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < ar.length; i++) {
builder.append('"').append(ar[i]).append('"');
if (i < ar.length - 1) {
builder.append(", ");
}
}
return builder.toString();
}
}