blob: 78fcfd4488d40da84562d1320af7e81588307a53 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.actions;
import com.android.SdkConstants;
import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.BuildToolInfo;
import com.android.utils.GrabProcessOutput;
import com.android.utils.GrabProcessOutput.IProcessOutput;
import com.android.utils.GrabProcessOutput.Wait;
import com.android.utils.SdkUtils;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
/**
* Runs dexdump on the classes.dex of a selected project.
*/
public class DexDumpAction implements IObjectActionDelegate {
private ISelection mSelection;
@Override
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
// pass
}
@Override
public void run(IAction action) {
if (mSelection instanceof IStructuredSelection) {
for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
Object element = it.next();
IProject project = null;
if (element instanceof IProject) {
project = (IProject)element;
} else if (element instanceof IAdaptable) {
project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
}
if (project != null) {
dexDumpProject(project);
}
}
}
}
@Override
public void selectionChanged(IAction action, ISelection selection) {
mSelection = selection;
}
/**
* Calls {@link #runDexDump(IProject, IProgressMonitor)} inside a job.
*
* @param project on which to run dexdump.
*/
private void dexDumpProject(final IProject project) {
new Job("Dexdump") {
@Override
protected IStatus run(IProgressMonitor monitor) {
return runDexDump(project, monitor);
}
}.schedule();
}
/**
* Runs <code>dexdump</code> on the classex.dex of the project.
* Saves the output in a temporary file.
* On success, opens the file in the default text editor.
*
* @param project on which to run dexdump.
* @param monitor The job's monitor.
*/
private IStatus runDexDump(final IProject project, IProgressMonitor monitor) {
File dstFile = null;
boolean removeDstFile = true;
try {
if (monitor != null) {
monitor.beginTask(String.format("Dump dex of %1$s", project.getName()), 2);
}
Sdk current = Sdk.getCurrent();
if (current == null) {
AdtPlugin.printErrorToConsole(project,
"DexDump: missing current SDK"); //$NON-NLS-1$
return Status.OK_STATUS;
}
BuildToolInfo buildToolInfo = current.getLatestBuildTool();
if (buildToolInfo == null) {
AdtPlugin.printErrorToConsole(project,
"SDK missing build tools. Please install build tools using SDK Manager.");
return Status.OK_STATUS;
}
File buildToolsFolder = buildToolInfo.getLocation();
File dexDumpFile = new File(buildToolsFolder, SdkConstants.FN_DEXDUMP);
IPath binPath = project.getFolder(SdkConstants.FD_OUTPUT).getLocation();
if (binPath == null) {
AdtPlugin.printErrorToConsole(project,
"DexDump: missing project /bin folder. Please compile first."); //$NON-NLS-1$
return Status.OK_STATUS;
}
File classesDexFile =
new File(binPath.toOSString(), SdkConstants.FN_APK_CLASSES_DEX);
if (!classesDexFile.exists()) {
AdtPlugin.printErrorToConsole(project,
"DexDump: missing classex.dex for project. Please compile first.");//$NON-NLS-1$
return Status.OK_STATUS;
}
try {
dstFile = File.createTempFile(
"dexdump_" + project.getName() + "_", //$NON-NLS-1$ //$NON-NLS-2$
".txt"); //$NON-NLS-1$
} catch (Exception e) {
AdtPlugin.logAndPrintError(e, project.getName(),
"DexDump: createTempFile failed."); //$NON-NLS-1$
return Status.OK_STATUS;
}
// --- Exec command line and save result to dst file
String[] command = new String[2];
command[0] = dexDumpFile.getAbsolutePath();
command[1] = classesDexFile.getAbsolutePath();
try {
final Process process = Runtime.getRuntime().exec(command);
final BufferedWriter writer = new BufferedWriter(new FileWriter(dstFile));
try {
final String lineSep = SdkUtils.getLineSeparator();
int err = GrabProcessOutput.grabProcessOutput(
process,
Wait.WAIT_FOR_READERS,
new IProcessOutput() {
@Override
public void out(@Nullable String line) {
if (line != null) {
try {
writer.write(line);
writer.write(lineSep);
} catch (IOException ignore) {}
}
}
@Override
public void err(@Nullable String line) {
if (line != null) {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE,
project, line);
}
}
});
if (err == 0) {
// The command worked. In this case we don't remove the
// temp file in the finally block.
removeDstFile = false;
} else {
AdtPlugin.printErrorToConsole(project,
"DexDump failed with code " + Integer.toString(err)); //$NON-NLS-1$
return Status.OK_STATUS;
}
} finally {
writer.close();
}
} catch (InterruptedException e) {
// ?
}
if (monitor != null) {
monitor.worked(1);
}
// --- Open the temp file in an editor
final String dstPath = dstFile.getAbsolutePath();
AdtPlugin.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
IFileStore fileStore =
EFS.getLocalFileSystem().getStore(new Path(dstPath));
if (!fileStore.fetchInfo().isDirectory() &&
fileStore.fetchInfo().exists()) {
IWorkbench wb = PlatformUI.getWorkbench();
IWorkbenchWindow win = wb == null ? null : wb.getActiveWorkbenchWindow();
final IWorkbenchPage page = win == null ? null : win.getActivePage();
if (page != null) {
try {
IDE.openEditorOnFileStore(page, fileStore);
} catch (PartInitException e) {
AdtPlugin.logAndPrintError(e, project.getName(),
"Opening DexDump result failed. Result is available at %1$s", //$NON-NLS-1$
dstPath);
}
}
}
}
});
if (monitor != null) {
monitor.worked(1);
}
return Status.OK_STATUS;
} catch (IOException e) {
AdtPlugin.logAndPrintError(e, project.getName(),
"DexDump failed."); //$NON-NLS-1$
return Status.OK_STATUS;
} finally {
// By default we remove the temp file on failure.
if (removeDstFile && dstFile != null) {
try {
dstFile.delete();
} catch (Exception e) {
AdtPlugin.logAndPrintError(e, project.getName(),
"DexDump: can't delete temp file %1$s.", //$NON-NLS-1$
dstFile.getAbsoluteFile());
}
}
if (monitor != null) {
monitor.done();
}
}
}
}