• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.build;
18 
19 import com.android.SdkConstants;
20 import com.android.ide.eclipse.adt.AdtPlugin;
21 
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Status;
25 
26 import java.io.File;
27 import java.io.PrintStream;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.Field;
30 import java.lang.reflect.Method;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.net.URLClassLoader;
34 import java.util.Collection;
35 
36 /**
37  * Wrapper to access dx.jar through reflection.
38  * <p/>Since there is no proper api to call the method in the dex library, this wrapper is going
39  * to access it through reflection.
40  */
41 public final class DexWrapper {
42 
43     private final static String DEX_MAIN = "com.android.dx.command.dexer.Main"; //$NON-NLS-1$
44     private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole"; //$NON-NLS-1$
45     private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments"; //$NON-NLS-1$
46 
47     private final static String MAIN_RUN = "run"; //$NON-NLS-1$
48 
49     private Method mRunMethod;
50 
51     private Constructor<?> mArgConstructor;
52     private Field mArgOutName;
53     private Field mArgVerbose;
54     private Field mArgJarOutput;
55     private Field mArgFileNames;
56     private Field mArgForceJumbo;
57 
58     private Field mConsoleOut;
59     private Field mConsoleErr;
60 
61     /**
62      * Loads the dex library from a file path.
63      *
64      * The loaded library can be used via
65      * {@link DexWrapper#run(String, String[], boolean, PrintStream, PrintStream)}.
66      *
67      * @param osFilepath the location of the dx.jar file.
68      * @return an IStatus indicating the result of the load.
69      */
loadDex(String osFilepath)70     public synchronized IStatus loadDex(String osFilepath) {
71         try {
72             File f = new File(osFilepath);
73             if (f.isFile() == false) {
74                 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format(
75                         Messages.DexWrapper_s_does_not_exists, osFilepath));
76             }
77             URL url = f.toURI().toURL();
78 
79             URLClassLoader loader = new URLClassLoader(new URL[] { url },
80                     DexWrapper.class.getClassLoader());
81 
82             // get the classes.
83             Class<?> mainClass = loader.loadClass(DEX_MAIN);
84             Class<?> consoleClass = loader.loadClass(DEX_CONSOLE);
85             Class<?> argClass = loader.loadClass(DEX_ARGS);
86 
87             try {
88                 // now get the fields/methods we need
89                 mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);
90 
91                 mArgConstructor = argClass.getConstructor();
92                 mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
93                 mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
94                 mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
95                 mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
96                 mArgForceJumbo = argClass.getField("forceJumbo"); //$NON-NLS-1$
97 
98                 mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
99                 mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$
100 
101             } catch (SecurityException e) {
102                 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e);
103             } catch (NoSuchMethodException e) {
104                 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e);
105             } catch (NoSuchFieldException e) {
106                 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e);
107             }
108 
109             return Status.OK_STATUS;
110         } catch (MalformedURLException e) {
111             // really this should not happen.
112             return createErrorStatus(
113                     String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
114         } catch (ClassNotFoundException e) {
115             return createErrorStatus(
116                     String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
117         }
118     }
119 
120     /**
121      * Removes any reference to the dex library.
122      * <p/>
123      * {@link #loadDex(String)} must be called on the wrapper
124      * before {@link #run(String, String[], boolean, PrintStream, PrintStream)} can
125      * be used again.
126      */
unload()127     public synchronized void unload() {
128         mRunMethod = null;
129         mArgConstructor = null;
130         mArgOutName = null;
131         mArgJarOutput = null;
132         mArgFileNames = null;
133         mArgVerbose = null;
134         mConsoleOut = null;
135         mConsoleErr = null;
136         System.gc();
137     }
138 
139     /**
140      * Runs the dex command.
141      * The wrapper must have been initialized via {@link #loadDex(String)} first.
142      *
143      * @param osOutFilePath the OS path to the outputfile (classes.dex
144      * @param osFilenames list of input source files (.class and .jar files)
145      * @param forceJumbo force jumbo mode.
146      * @param verbose verbose mode.
147      * @param outStream the stdout console
148      * @param errStream the stderr console
149      * @return the integer return code of com.android.dx.command.dexer.Main.run()
150      * @throws CoreException
151      */
run(String osOutFilePath, Collection<String> osFilenames, boolean forceJumbo, boolean verbose, PrintStream outStream, PrintStream errStream)152     public synchronized int run(String osOutFilePath, Collection<String> osFilenames,
153             boolean forceJumbo, boolean verbose,
154             PrintStream outStream, PrintStream errStream) throws CoreException {
155 
156         assert mRunMethod != null;
157         assert mArgConstructor != null;
158         assert mArgOutName != null;
159         assert mArgJarOutput != null;
160         assert mArgFileNames != null;
161         assert mArgForceJumbo != null;
162         assert mArgVerbose != null;
163         assert mConsoleOut != null;
164         assert mConsoleErr != null;
165 
166         if (mRunMethod == null) {
167             throw new CoreException(createErrorStatus(
168                     String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s,
169                             "wrapper was not properly loaded first"),
170                     null /*exception*/));
171         }
172 
173         try {
174             // set the stream
175             mConsoleErr.set(null /* obj: static field */, errStream);
176             mConsoleOut.set(null /* obj: static field */, outStream);
177 
178             // create the Arguments object.
179             Object args = mArgConstructor.newInstance();
180             mArgOutName.set(args, osOutFilePath);
181             mArgFileNames.set(args, osFilenames.toArray(new String[osFilenames.size()]));
182             mArgJarOutput.set(args, osOutFilePath.endsWith(SdkConstants.DOT_JAR));
183             mArgForceJumbo.set(args, forceJumbo);
184             mArgVerbose.set(args, verbose);
185 
186             // call the run method
187             Object res = mRunMethod.invoke(null /* obj: static method */, args);
188 
189             if (res instanceof Integer) {
190                 return ((Integer)res).intValue();
191             }
192 
193             return -1;
194         } catch (Exception e) {
195             Throwable t = e;
196             while (t.getCause() != null) {
197                 t = t.getCause();
198             }
199 
200             String msg = t.getMessage();
201             if (msg == null) {
202                 msg = String.format("%s. Check the Eclipse log for stack trace.",
203                         t.getClass().getName());
204             }
205 
206             throw new CoreException(createErrorStatus(
207                     String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, msg), t));
208         }
209     }
210 
createErrorStatus(String message, Throwable e)211     private static IStatus createErrorStatus(String message, Throwable e) {
212         AdtPlugin.log(e, message);
213         AdtPlugin.printErrorToConsole(Messages.DexWrapper_Dex_Loader, message);
214 
215         return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message, e);
216     }
217 }
218