Main.java revision 12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b
112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden/*
212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Copyright (C) 2009 The Android Open Source Project
312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden *
412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Licensed under the Apache License, Version 2.0 (the "License");
512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * you may not use this file except in compliance with the License.
612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * You may obtain a copy of the License at
712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden *
812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden *      http://www.apache.org/licenses/LICENSE-2.0
912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden *
1012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * Unless required by applicable law or agreed to in writing, software
1112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * distributed under the License is distributed on an "AS IS" BASIS,
1212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * See the License for the specific language governing permissions and
1412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden * limitations under the License.
1512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden */
1612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
1712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenpackage com.android.dexdeps;
1812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
1912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.File;
2012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.FileNotFoundException;
2112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.IOException;
2212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.InputStream;
2312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.io.RandomAccessFile;
2412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipEntry;
2512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipException;
2612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipFile;
2712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenimport java.util.zip.ZipInputStream;
2812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
2912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFaddenpublic class Main {
3012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    private static final String CLASSES_DEX = "classes.dex";
3112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
3212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    private String mInputFileName;
3312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    private String mOutputFormat = "brief";
3412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
3512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    /**
3612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * Entry point.
3712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     */
3812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    public static void main(String[] args) {
3912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        Main main = new Main();
4012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        main.run(args);
4112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    }
4212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
4312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    /**
4412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * Start things up.
4512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     */
4612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    void run(String[] args) {
4712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        try {
4812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            parseArgs(args);
4912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            RandomAccessFile raf = openInputFile();
5012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            DexData dexData = new DexData(raf);
5112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            dexData.load();
5212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
5312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            Output.generate(dexData, mOutputFormat);
5412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        } catch (UsageException ue) {
5512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            usage();
5612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            System.exit(2);
5712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        } catch (IOException ioe) {
5812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            /* a message was already reported, just bail quietly */
5912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            System.exit(1);
6012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        } catch (DexDataException dde) {
6112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            /* a message was already reported, just bail quietly */
6212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            System.exit(1);
6312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
6412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    }
6512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
6612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    /**
6712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * Opens the input file, which could be a .dex or a .jar/.apk with a
6812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * classes.dex inside.  If the latter, we extract the contents to a
6912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * temporary file.
7012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     */
7112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    RandomAccessFile openInputFile() throws IOException {
7212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        RandomAccessFile raf;
7312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
7412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        raf = openInputFileAsZip();
7512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        if (raf == null) {
7612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            File inputFile = new File(mInputFileName);
7712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            raf = new RandomAccessFile(inputFile, "r");
7812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
7912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
8012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        return raf;
8112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    }
8212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
8312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    /**
8412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * Tries to open the input file as a Zip archive (jar/apk) with a
8512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * "classes.dex" inside.
8612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     *
8712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * @return a RandomAccessFile for classes.dex, or null if the input file
8812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     *         is not a zip archive
8912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * @throws IOException if the file isn't found, or it's a zip and
9012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     *         classes.dex isn't found inside
9112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     */
9212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    RandomAccessFile openInputFileAsZip() throws IOException {
9312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        ZipFile zipFile;
9412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
9512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        /*
9612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * Try it as a zip file.
9712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         */
9812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        try {
9912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            zipFile = new ZipFile(mInputFileName);
10012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        } catch (FileNotFoundException fnfe) {
10112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            /* not found, no point in retrying as non-zip */
10212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            System.err.println("Unable to open '" + mInputFileName + "': " +
10312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                fnfe.getMessage());
10412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            throw fnfe;
10512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        } catch (ZipException ze) {
10612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            /* not a zip */
10712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            return null;
10812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
10912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
11012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        /*
11112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * We know it's a zip; see if there's anything useful inside.  A
11212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * failure here results in some type of IOException (of which
11312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * ZipException is a subclass).
11412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         */
11512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        ZipEntry entry = zipFile.getEntry(CLASSES_DEX);
11612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        if (entry == null) {
11712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            System.err.println("Unable to find '" + CLASSES_DEX +
11812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                "' in '" + mInputFileName + "'");
11912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            zipFile.close();
12012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            throw new ZipException();
12112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
12212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
12312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        InputStream zis = zipFile.getInputStream(entry);
12412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
12512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        /*
12612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * Create a temp file to hold the DEX data, open it, and delete it
12712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * to ensure it doesn't hang around if we fail.
12812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         */
12912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        File tempFile = File.createTempFile("dexdeps", ".dex");
13012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        System.out.println("+++ using temp " + tempFile);
13112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
13212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        tempFile.delete();
13312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
13412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        /*
13512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         * Copy all data from input stream to output file.
13612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden         */
13712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        byte copyBuf[] = new byte[32768];
13812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        int actual;
13912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
14012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        while (true) {
14112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            actual = zis.read(copyBuf);
14212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            if (actual == -1)
14312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                break;
14412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
14512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            raf.write(copyBuf, 0, actual);
14612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
14712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
14812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        zis.close();
14912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        raf.seek(0);
15012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
15112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        return raf;
15212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    }
15312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
15412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
15512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    /**
15612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * Parses command-line arguments.
15712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     *
15812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * @throws UsageException if arguments are missing or poorly formed
15912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     */
16012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    void parseArgs(String[] args) {
16112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        int idx;
16212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
16312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        for (idx = 0; idx < args.length; idx++) {
16412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            String arg = args[idx];
16512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
16612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            if (arg.equals("--") || !arg.startsWith("--")) {
16712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                break;
16812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            } else if (arg.startsWith("--format=")) {
16912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                mOutputFormat = arg.substring(arg.indexOf('=') + 1);
17012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                if (!mOutputFormat.equals("brief") &&
17112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                    !mOutputFormat.equals("xml"))
17212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                {
17312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                    System.err.println("Unknown format '" + mOutputFormat +"'");
17412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                    throw new UsageException();
17512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                }
17612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                System.out.println("Using format " + mOutputFormat);
17712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            } else {
17812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                System.err.println("Unknown option '" + arg + "'");
17912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden                throw new UsageException();
18012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            }
18112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
18212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
18312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        // expecting one argument left
18412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        if (idx != args.length - 1) {
18512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden            throw new UsageException();
18612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        }
18712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
18812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        mInputFileName = args[idx];
18912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    }
19012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
19112d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    /**
19212d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     * Prints command-line usage info.
19312d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden     */
19412d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    void usage() {
19512d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        System.err.println("\nUsage: dexdeps [options] <file.{dex,apk,jar}>");
19612d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        System.err.println("Options:");
19712d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden        System.err.println("  --format={brief,xml}");
19812d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden    }
19912d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden}
20012d6d4c0ea192b6a924df0df1e3b14ce1ed5793bAndy McFadden
201