Main.java revision 753f32ab1db6de174a1de68210f9f30a1e327612
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            if (ioe.getMessage() != null)
59                System.err.println("Failed: " + ioe);
60            System.exit(1);
61        } catch (DexDataException dde) {
62            /* a message was already reported, just bail quietly */
63            System.exit(1);
64        }
65    }
66
67    /**
68     * Opens the input file, which could be a .dex or a .jar/.apk with a
69     * classes.dex inside.  If the latter, we extract the contents to a
70     * temporary file.
71     */
72    RandomAccessFile openInputFile() throws IOException {
73        RandomAccessFile raf;
74
75        raf = openInputFileAsZip();
76        if (raf == null) {
77            File inputFile = new File(mInputFileName);
78            raf = new RandomAccessFile(inputFile, "r");
79        }
80
81        return raf;
82    }
83
84    /**
85     * Tries to open the input file as a Zip archive (jar/apk) with a
86     * "classes.dex" inside.
87     *
88     * @return a RandomAccessFile for classes.dex, or null if the input file
89     *         is not a zip archive
90     * @throws IOException if the file isn't found, or it's a zip and
91     *         classes.dex isn't found inside
92     */
93    RandomAccessFile openInputFileAsZip() throws IOException {
94        ZipFile zipFile;
95
96        /*
97         * Try it as a zip file.
98         */
99        try {
100            zipFile = new ZipFile(mInputFileName);
101        } catch (FileNotFoundException fnfe) {
102            /* not found, no point in retrying as non-zip */
103            System.err.println("Unable to open '" + mInputFileName + "': " +
104                fnfe.getMessage());
105            throw fnfe;
106        } catch (ZipException ze) {
107            /* not a zip */
108            return null;
109        }
110
111        /*
112         * We know it's a zip; see if there's anything useful inside.  A
113         * failure here results in some type of IOException (of which
114         * ZipException is a subclass).
115         */
116        ZipEntry entry = zipFile.getEntry(CLASSES_DEX);
117        if (entry == null) {
118            System.err.println("Unable to find '" + CLASSES_DEX +
119                "' in '" + mInputFileName + "'");
120            zipFile.close();
121            throw new ZipException();
122        }
123
124        InputStream zis = zipFile.getInputStream(entry);
125
126        /*
127         * Create a temp file to hold the DEX data, open it, and delete it
128         * to ensure it doesn't hang around if we fail.
129         */
130        File tempFile = File.createTempFile("dexdeps", ".dex");
131        //System.out.println("+++ using temp " + tempFile);
132        RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
133        tempFile.delete();
134
135        /*
136         * Copy all data from input stream to output file.
137         */
138        byte copyBuf[] = new byte[32768];
139        int actual;
140
141        while (true) {
142            actual = zis.read(copyBuf);
143            if (actual == -1)
144                break;
145
146            raf.write(copyBuf, 0, actual);
147        }
148
149        zis.close();
150        raf.seek(0);
151
152        return raf;
153    }
154
155
156    /**
157     * Parses command-line arguments.
158     *
159     * @throws UsageException if arguments are missing or poorly formed
160     */
161    void parseArgs(String[] args) {
162        int idx;
163
164        for (idx = 0; idx < args.length; idx++) {
165            String arg = args[idx];
166
167            if (arg.equals("--") || !arg.startsWith("--")) {
168                break;
169            } else if (arg.startsWith("--format=")) {
170                mOutputFormat = arg.substring(arg.indexOf('=') + 1);
171                if (!mOutputFormat.equals("brief") &&
172                    !mOutputFormat.equals("xml"))
173                {
174                    System.err.println("Unknown format '" + mOutputFormat +"'");
175                    throw new UsageException();
176                }
177                //System.out.println("+++ using format " + mOutputFormat);
178            } else {
179                System.err.println("Unknown option '" + arg + "'");
180                throw new UsageException();
181            }
182        }
183
184        // expecting one argument left
185        if (idx != args.length - 1) {
186            throw new UsageException();
187        }
188
189        mInputFileName = args[idx];
190    }
191
192    /**
193     * Prints command-line usage info.
194     */
195    void usage() {
196        System.err.println("\nUsage: dexdeps [options] <file.{dex,apk,jar}>");
197        System.err.println("Options:");
198        System.err.println("  --format={brief,xml}");
199    }
200}
201
202