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