Main.java revision 12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 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 17package com.android.dexdeps; 18 19import java.io.File; 20import java.io.FileNotFoundException; 21import java.io.IOException; 22import java.io.InputStream; 23import java.io.RandomAccessFile; 24import java.util.zip.ZipEntry; 25import java.util.zip.ZipException; 26import java.util.zip.ZipFile; 27import java.util.zip.ZipInputStream; 28 29public class Main { 30 private static final String CLASSES_DEX = "classes.dex"; 31 32 private String mInputFileName; 33 private String mOutputFormat = "brief"; 34 35 /** 36 * Entry point. 37 */ 38 public static void main(String[] args) { 39 Main main = new Main(); 40 main.run(args); 41 } 42 43 /** 44 * Start things up. 45 */ 46 void run(String[] args) { 47 try { 48 parseArgs(args); 49 RandomAccessFile raf = openInputFile(); 50 DexData dexData = new DexData(raf); 51 dexData.load(); 52 53 Output.generate(dexData, mOutputFormat); 54 } catch (UsageException ue) { 55 usage(); 56 System.exit(2); 57 } catch (IOException ioe) { 58 /* a message was already reported, just bail quietly */ 59 System.exit(1); 60 } catch (DexDataException dde) { 61 /* a message was already reported, just bail quietly */ 62 System.exit(1); 63 } 64 } 65 66 /** 67 * Opens the input file, which could be a .dex or a .jar/.apk with a 68 * classes.dex inside. If the latter, we extract the contents to a 69 * temporary file. 70 */ 71 RandomAccessFile openInputFile() throws IOException { 72 RandomAccessFile raf; 73 74 raf = openInputFileAsZip(); 75 if (raf == null) { 76 File inputFile = new File(mInputFileName); 77 raf = new RandomAccessFile(inputFile, "r"); 78 } 79 80 return raf; 81 } 82 83 /** 84 * Tries to open the input file as a Zip archive (jar/apk) with a 85 * "classes.dex" inside. 86 * 87 * @return a RandomAccessFile for classes.dex, or null if the input file 88 * is not a zip archive 89 * @throws IOException if the file isn't found, or it's a zip and 90 * classes.dex isn't found inside 91 */ 92 RandomAccessFile openInputFileAsZip() throws IOException { 93 ZipFile zipFile; 94 95 /* 96 * Try it as a zip file. 97 */ 98 try { 99 zipFile = new ZipFile(mInputFileName); 100 } catch (FileNotFoundException fnfe) { 101 /* not found, no point in retrying as non-zip */ 102 System.err.println("Unable to open '" + mInputFileName + "': " + 103 fnfe.getMessage()); 104 throw fnfe; 105 } catch (ZipException ze) { 106 /* not a zip */ 107 return null; 108 } 109 110 /* 111 * We know it's a zip; see if there's anything useful inside. A 112 * failure here results in some type of IOException (of which 113 * ZipException is a subclass). 114 */ 115 ZipEntry entry = zipFile.getEntry(CLASSES_DEX); 116 if (entry == null) { 117 System.err.println("Unable to find '" + CLASSES_DEX + 118 "' in '" + mInputFileName + "'"); 119 zipFile.close(); 120 throw new ZipException(); 121 } 122 123 InputStream zis = zipFile.getInputStream(entry); 124 125 /* 126 * Create a temp file to hold the DEX data, open it, and delete it 127 * to ensure it doesn't hang around if we fail. 128 */ 129 File tempFile = File.createTempFile("dexdeps", ".dex"); 130 System.out.println("+++ using temp " + tempFile); 131 RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); 132 tempFile.delete(); 133 134 /* 135 * Copy all data from input stream to output file. 136 */ 137 byte copyBuf[] = new byte[32768]; 138 int actual; 139 140 while (true) { 141 actual = zis.read(copyBuf); 142 if (actual == -1) 143 break; 144 145 raf.write(copyBuf, 0, actual); 146 } 147 148 zis.close(); 149 raf.seek(0); 150 151 return raf; 152 } 153 154 155 /** 156 * Parses command-line arguments. 157 * 158 * @throws UsageException if arguments are missing or poorly formed 159 */ 160 void parseArgs(String[] args) { 161 int idx; 162 163 for (idx = 0; idx < args.length; idx++) { 164 String arg = args[idx]; 165 166 if (arg.equals("--") || !arg.startsWith("--")) { 167 break; 168 } else if (arg.startsWith("--format=")) { 169 mOutputFormat = arg.substring(arg.indexOf('=') + 1); 170 if (!mOutputFormat.equals("brief") && 171 !mOutputFormat.equals("xml")) 172 { 173 System.err.println("Unknown format '" + mOutputFormat +"'"); 174 throw new UsageException(); 175 } 176 System.out.println("Using format " + mOutputFormat); 177 } else { 178 System.err.println("Unknown option '" + arg + "'"); 179 throw new UsageException(); 180 } 181 } 182 183 // expecting one argument left 184 if (idx != args.length - 1) { 185 throw new UsageException(); 186 } 187 188 mInputFileName = args[idx]; 189 } 190 191 /** 192 * Prints command-line usage info. 193 */ 194 void usage() { 195 System.err.println("\nUsage: dexdeps [options] <file.{dex,apk,jar}>"); 196 System.err.println("Options:"); 197 System.err.println(" --format={brief,xml}"); 198 } 199} 200 201