diff options
| author | Siva Velusamy <vsiva@google.com> | 2012-03-19 19:24:32 -0700 |
|---|---|---|
| committer | Siva Velusamy <vsiva@google.com> | 2012-03-26 15:45:19 -0700 |
| commit | 96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247 (patch) | |
| tree | 324234ddd7822203a35f9b23c07d90d0a2b39ffd /eclipse | |
| parent | 805a03988e828ff574ab02e9b7d812b07b493156 (diff) | |
| download | sdk-96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247.zip sdk-96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247.tar.gz sdk-96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247.tar.bz2 | |
Add support for debugging native code.
This CL adds necessary Eclipse support to provide native
code debugging. Native debugging is simply a wrapper around
CDT DSF's remote process debugging via a gdbserver.
Change-Id: Ifeb7c5d31dd94e49f0d5266dd881b0f60532ad9a
Diffstat (limited to 'eclipse')
15 files changed, 1103 insertions, 4 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath index 1fa3e68..d37aeb2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath @@ -3,5 +3,8 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> + <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> + <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> + <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF index 40400bc..35e8537 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF @@ -7,6 +7,7 @@ Bundle-Activator: com.android.ide.eclipse.adt.ndk.internal.Activator Bundle-Vendor: The Android Open Source Project Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.resources, + org.eclipse.core.variables, org.eclipse.ui, org.eclipse.debug.core, org.eclipse.debug.ui, @@ -14,8 +15,13 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.cdt.ui, org.eclipse.cdt.managedbuilder.core, org.eclipse.cdt.managedbuilder.ui, - org.eclipse.cdt.dsf.gdb, + org.eclipse.cdt.debug.core, org.eclipse.cdt.debug.ui, + org.eclipse.cdt.dsf, + org.eclipse.cdt.dsf.gdb, + org.eclipse.cdt.dsf.gdb.ui, org.eclipse.cdt.launch, - com.android.ide.eclipse.adt + com.android.ide.eclipse.adt, + com.android.ide.eclipse.ddms Bundle-ActivationPolicy: lazy +Bundle-ClassPath: . diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.png b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.png Binary files differnew file mode 100644 index 0000000..8ca3770 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml index 5179dd6..591092f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml @@ -139,5 +139,35 @@ class="com.android.ide.eclipse.adt.ndk.internal.preferences.NdkPreferenceInitializer">
</initializer>
</extension>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ delegate="com.android.ide.eclipse.adt.ndk.internal.launch.NdkGdbLaunchDelegate"
+ delegateName="Android ndk-gdb Launcher"
+ id="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigType"
+ modes="debug"
+ name="Android Native Application"
+ public="true"
+ sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
+ sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigType"
+ icon="icons/android_app.png"
+ id="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigTypeImage">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="com.android.ide.eclipse.adt.ndk.internal.launch.NdkGdbLaunchConfigTabGroups"
+ description="Android Native Application"
+ id="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigTabGroup"
+ type="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigType">
+ </launchConfigurationTabGroup>
+ </extension>
</plugin>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java new file mode 100644 index 0000000..ccaf412 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal; + +public enum NativeAbi { + armeabi("armeabi"), + x86("x86"); + + private final String mAbi; + + private NativeAbi(String abi) { + mAbi = abi; + } + + public String getAbi() { + return mAbi; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java new file mode 100644 index 0000000..b44f216 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal; + +import com.android.ide.eclipse.adt.AdtPlugin; + +import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("restriction") +public class NdkHelper { + private static final String MAKE = "make"; + private static final String CORE_MAKEFILE_PATH = "/build/core/build-local.mk"; + + /** + * Obtain the ABI's the application is compatible with. + * The ABI's are obtained by reading the result of the following command: + * make --no-print-dir -f ${NdkRoot}/build/core/build-local.mk -C <project-root> DUMP_APP_ABI + */ + public static List<NativeAbi> getApplicationAbis(IProject project, IProgressMonitor monitor) { + ICommandLauncher launcher = new CommandLauncher(); + launcher.setProject(project); + String[] args = new String[] { + "--no-print-dir", + "-f", + NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH, + "-C", + project.getLocation().toOSString(), + "DUMP_APP_ABI", + }; + try { + launcher.execute(getPathToMake(), args, null, project.getLocation(), monitor); + } catch (CoreException e) { + AdtPlugin.printErrorToConsole(e.getLocalizedMessage()); + return Collections.emptyList(); + } + + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + launcher.waitAndRead(stdout, stderr, monitor); + + String abis = stdout.toString().trim(); + List<NativeAbi> nativeAbis = new ArrayList<NativeAbi>(3); + + for (String abi: abis.split(" ")) { + try { + nativeAbis.add(NativeAbi.valueOf(abi)); + } catch (IllegalArgumentException e) { + + } + } + + return nativeAbis; + } + + /** + * Obtain the toolchain prefix to use for given project and abi. + * The prefix is obtained by reading the result of: + * make --no-print-dir -f ${NdkRoot}/build/core/build-local.mk \ + * -C <project-root> \ + * DUMP_TOOLCHAIN_PREFIX APP_ABI=abi + */ + public static String getToolchainPrefix(IProject project, NativeAbi abi, + IProgressMonitor monitor) { + ICommandLauncher launcher = new CommandLauncher(); + launcher.setProject(project); + String[] args = new String[] { + "--no-print-dir", + "-f", + NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH, + "-C", + project.getLocation().toOSString(), + "DUMP_TOOLCHAIN_PREFIX", + }; + try { + launcher.execute(getPathToMake(), args, null, project.getLocation(), monitor); + } catch (CoreException e) { + AdtPlugin.printErrorToConsole(e.getLocalizedMessage()); + return null; + } + + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + launcher.waitAndRead(stdout, stderr, monitor); + return stdout.toString().trim(); + } + + private static IPath getPathToMake() { + return getUtilitiesFolder().append(MAKE); + } + + /** + * Obtain a path to the utilities prebult folder in NDK. This is typically + * "${NdkRoot}/prebuilt/<platform>/" + */ + private static synchronized IPath getUtilitiesFolder() { + IPath ndkRoot = new Path(NdkManager.getNdkLocation()); + IPath prebuilt = ndkRoot.append("prebuilt"); //$NON-NLS-1$ + if (!prebuilt.toFile().exists() || !prebuilt.toFile().canRead()) { + return ndkRoot; + } + + File[] platforms = prebuilt.toFile().listFiles(); + if (platforms.length == 1) { + return prebuilt.append(platforms[0].getName()).append("bin"); //$NON-NLS-1$ + } + + return ndkRoot; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java new file mode 100644 index 0000000..3e95c79 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal; + +/** Eclipse variables that are understood by the NDK while launching programs. */ +public class NdkVariables { + /** Variable that expands to the full path of NDK's ABI specific gdb. */ + public static final String NDK_GDB = "NdkGdb"; + + /** Variable that expands to point to the full path of the project used in the launch + * configuration. */ + public static final String NDK_PROJECT = "NdkProject"; + + /** Variable that indicates the ABI that is compatible between the device and the + * application being launched. */ + public static final String NDK_COMPAT_ABI = "NdkCompatAbi"; +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java new file mode 100644 index 0000000..7d58bd9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal.launch; + +import com.android.ddmlib.IDevice; +import com.android.ddmlib.IShellOutputReceiver; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The {@link GdbServerTask} launches gdbserver on the given device and attaches it to + * provided pid. + */ +public class GdbServerTask implements Runnable { + private IDevice mDevice; + private String mRunAs; + private String mSocket; + private int mPid; + private CountDownLatch mAttachLatch; + + private GdbServerOutputReceiver mOutputReceiver; + private Exception mLaunchException; + + private AtomicBoolean mCancelled = new AtomicBoolean(false); + private AtomicBoolean mHasCompleted = new AtomicBoolean(false); + + /** + * Construct a gdbserver task. + * @param device device to run gdbserver on + * @param runAsPackage name of the package in which gdbserver resides + * @param socketName name of the local socket on which the server will listen + * @param pid pid of task to attach to + * @param attachLatch latch to notify when gdbserver gets attached to the task + */ + public GdbServerTask(IDevice device, String runAsPackage, String socketName, int pid, + CountDownLatch attachLatch) { + mDevice = device; + mRunAs = runAsPackage; + mSocket = socketName; + mPid = pid; + mAttachLatch = attachLatch; + + mOutputReceiver = new GdbServerOutputReceiver(); + } + + /** + * Runs gdbserver on the device and connects to the given task. If gdbserver manages to + * successfully attach itself to the process, then it counts down on its attach latch. + */ + @Override + public void run() { + // Launch gdbserver on the device. + String command = String.format("run-as %s lib/gdbserver +%s --attach %d", + mRunAs, mSocket, mPid); + try { + mDevice.executeShellCommand(command, mOutputReceiver, 0); + } catch (Exception e) { + mLaunchException = e; + } + } + + /** Returns any exceptions that might have occurred while launching gdbserver. */ + public Exception getLaunchException() { + return mLaunchException; + } + + /** Cancel gdbserver if it is running. */ + public void setCancelled() { + mCancelled.set(true); + } + + public String getShellOutput() { + return mOutputReceiver.getOutput(); + } + + private class GdbServerOutputReceiver implements IShellOutputReceiver { + private StringBuffer mOutput = new StringBuffer(100); + + @Override + public synchronized void addOutput(byte[] data, int offset, int length) { + mOutput.append(new String(data, offset, length)); + + // notify other threads that gdbserver has attached to the task + if (mOutput.toString().contains("Attached")) { + mAttachLatch.countDown(); + } + } + + @Override + public void flush() { + mHasCompleted.set(true); + } + + @Override + public boolean isCancelled() { + return mCancelled.get(); + } + + public synchronized String getOutput() { + return mOutput.toString(); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java new file mode 100644 index 0000000..52112cd --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal.launch; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.ndk.internal.launch.messages"; //$NON-NLS-1$ + public static String NdkGdbLaunchDelegate_LaunchError_gdbserverOutput; + public static String NdkGdbLaunchDelegate_Action_ActivityLaunch; + public static String NdkGdbLaunchDelegate_Action_CheckAndroidDeviceVersion; + public static String NdkGdbLaunchDelegate_Action_KillExistingGdbServer; + public static String NdkGdbLaunchDelegate_Action_LaunchHostGdb; + public static String NdkGdbLaunchDelegate_Action_LaunchingGdbServer; + public static String NdkGdbLaunchDelegate_Action_ObtainAppAbis; + public static String NdkGdbLaunchDelegate_Action_ObtainDevice; + public static String NdkGdbLaunchDelegate_Action_ObtainDeviceABI; + public static String NdkGdbLaunchDelegate_Action_PerformIncrementalBuild; + public static String NdkGdbLaunchDelegate_Action_SettingUpPortForward; + public static String NdkGdbLaunchDelegate_Action_SyncAppToDevice; + public static String NdkGdbLaunchDelegate_Action_WaitGdbServerAttach; + public static String NdkGdbLaunchDelegate_Action_WaitingForActivity; + public static String NdkGdbLaunchDelegate_LaunchError_ActivityLaunchError; + public static String NdkGdbLaunchDelegate_LaunchError_Api8Needed; + public static String NdkGdbLaunchDelegate_LaunchError_CouldNotGetProject; + public static String NdkGdbLaunchDelegate_LaunchError_gdbserverLaunchException; + public static String NdkGdbLaunchDelegate_LaunchError_InstallError; + public static String NdkGdbLaunchDelegate_LaunchError_InterruptedWaitingForGdbserver; + public static String NdkGdbLaunchDelegate_LaunchError_NoActivityInManifest; + public static String NdkGdbLaunchDelegate_LaunchError_NoCompatibleAbi; + public static String NdkGdbLaunchDelegate_LaunchError_NoLauncherActivity; + public static String NdkGdbLaunchDelegate_LaunchError_NoSuchActivity; + public static String NdkGdbLaunchDelegate_LaunchError_NullApk; + public static String NdkGdbLaunchDelegate_LaunchError_ObtainingAppFolder; + public static String NdkGdbLaunchDelegate_LaunchError_PortForwarding; + public static String NdkGdbLaunchDelegate_LaunchError_ProjectHasErrors; + public static String NdkGdbLaunchDelegate_LaunchError_PullFileError; + public static String NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi; + public static String NdkGdbLaunchDelegate_LaunchError_UnknownAndroidDeviceVersion; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java new file mode 100644 index 0000000..cd68b6a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal.launch; + +import com.android.ide.eclipse.adt.ndk.internal.NdkVariables; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.launching.RemoteApplicationCDebuggerTab; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("restriction") +public class NdkDebuggerConfigTab extends RemoteApplicationCDebuggerTab { + public static final String DEFAULT_GDB_PORT = "5039"; + public static final String DEFAULT_GDB = getVar(NdkVariables.NDK_GDB); + public static final String DEFAULT_PROGRAM = + String.format("%1$s/obj/local/%2$s/app_process", + getVar(NdkVariables.NDK_PROJECT), + getVar(NdkVariables.NDK_COMPAT_ABI)); + public static final String DEFAULT_SOLIB_PATH = + getVar(NdkVariables.NDK_PROJECT) + "/obj/local/armeabi/"; + + + @Override + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + super.setDefaults(config); + + config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, DEFAULT_GDB); + config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, ""); //$NON-NLS-1$ + config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, DEFAULT_GDB_PORT); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false); + + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + DEFAULT_PROGRAM); + + List<String> solibPaths = new ArrayList<String>(2); + solibPaths.add(DEFAULT_SOLIB_PATH); + config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, solibPaths); + } + + private static String getVar(String varName) { + return "${" + varName + "}"; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java new file mode 100644 index 0000000..1882ae5 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal.launch; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +public class NdkGdbLaunchConfigTabGroups extends AbstractLaunchConfigurationTabGroup { + public NdkGdbLaunchConfigTabGroups() { + } + + @Override + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { + new NdkMainLaunchConfigTab(), + new NdkDebuggerConfigTab(), + new SourceLookupTab(), + new CommonTab() + }; + setTabs(tabs); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java new file mode 100644 index 0000000..a456e73 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal.launch; + +import com.android.ddmlib.AdbCommandRejectedException; +import com.android.ddmlib.Client; +import com.android.ddmlib.IDevice; +import com.android.ddmlib.IDevice.DeviceUnixSocketNamespace; +import com.android.ddmlib.IShellOutputReceiver; +import com.android.ddmlib.InstallException; +import com.android.ddmlib.ShellCommandUnresponsiveException; +import com.android.ddmlib.SyncException; +import com.android.ddmlib.TimeoutException; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.launch.DeviceChooserDialog; +import com.android.ide.eclipse.adt.internal.launch.DeviceChooserDialog.DeviceChooserResponse; +import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate; +import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectHelper; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.ide.eclipse.adt.ndk.internal.NativeAbi; +import com.android.ide.eclipse.adt.ndk.internal.NdkHelper; +import com.android.ide.eclipse.adt.ndk.internal.NdkVariables; +import com.android.sdklib.AndroidVersion; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.xml.ManifestData; +import com.android.sdklib.xml.ManifestData.Activity; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.debug.core.CDebugUtils; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.variables.IStringVariableManager; +import org.eclipse.core.variables.IValueVariable; +import org.eclipse.core.variables.VariablesPlugin; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.jface.dialogs.Dialog; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@SuppressWarnings("restriction") +public class NdkGdbLaunchDelegate extends GdbLaunchDelegate { + private static final String DEBUG_SOCKET = "debugsock"; //$NON-NLS-1$ + + @Override + public void launch(ILaunchConfiguration config, String mode, ILaunch launch, + IProgressMonitor monitor) throws CoreException { + boolean launched = doLaunch(config, mode, launch, monitor); + if (!launched) { + if (launch.canTerminate()) { + launch.terminate(); + } + DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch); + } + } + + public boolean doLaunch(ILaunchConfiguration config, String mode, ILaunch launch, + IProgressMonitor monitor) throws CoreException { + IProject project = null; + ICProject cProject = CDebugUtils.getCProject(config); + if (cProject != null) { + project = cProject.getProject(); + } + + if (project == null) { + AdtPlugin.printErrorToConsole( + Messages.NdkGdbLaunchDelegate_LaunchError_CouldNotGetProject); + return false; + } + + // make sure the project and its dependencies are built and PostCompilerBuilder runs. + // This is a synchronous call which returns when the build is done. + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_PerformIncrementalBuild); + ProjectHelper.doFullIncrementalDebugBuild(project, monitor); + + // check if the project has errors, and abort in this case. + if (ProjectHelper.hasError(project, true)) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_ProjectHasErrors); + return false; + } + + final ManifestData manifestData = AndroidManifestHelper.parseForData(project); + + // Get the activity name to launch + String activityName = getActivityToLaunch( + getActivityNameInLaunchConfig(config), + manifestData.getLauncherActivity(), + manifestData.getActivities(), + project); + + // Get ABI's supported by the application + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainAppAbis); + List<NativeAbi> appAbis = NdkHelper.getApplicationAbis(project, monitor); + if (appAbis.size() == 0) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi); + return false; + } + + // Show device chooser dialog and get device to use. + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainDevice); + final IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); + final DeviceChooserResponse response = new DeviceChooserResponse(); + final boolean continueLaunch[] = new boolean[] { false }; + AdtPlugin.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + DeviceChooserDialog dialog = new DeviceChooserDialog( + AdtPlugin.getDisplay().getActiveShell(), + response, + manifestData.getPackage(), + projectTarget); + if (dialog.open() == Dialog.OK) { + continueLaunch[0] = true; + } + }; + }); + + if (!continueLaunch[0]) { + return false; + } + + IDevice device = response.getDeviceToUse(); + + // ndk-gdb requires device > Froyo + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_CheckAndroidDeviceVersion); + AndroidVersion deviceVersion = Sdk.getDeviceVersion(device); + if (deviceVersion == null) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_UnknownAndroidDeviceVersion); + return false; + } else if (!deviceVersion.isGreaterOrEqualThan(8)) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_Api8Needed); + return false; + } + + // get Device ABI + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainDeviceABI); + String deviceAbi1 = device.getProperty("ro.product.cpu.abi"); //$NON-NLS-1$ + String deviceAbi2 = device.getProperty("ro.product.cpu.abi2"); //$NON-NLS-1$ + + // get the abi that is supported by both the device and the application + NativeAbi compatAbi = getCompatibleAbi(deviceAbi1, deviceAbi2, appAbis); + if (compatAbi == null) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_NoCompatibleAbi); + return false; + } + + // sync app + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_SyncAppToDevice); + IFile apk = ProjectHelper.getApplicationPackage(project); + if (apk == null) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_NullApk); + return false; + } + try { + device.installPackage(apk.getLocation().toOSString(), true); + } catch (InstallException e1) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_InstallError, e1); + return false; + } + + // launch activity + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ActivityLaunch + activityName); + String command = String.format("am start -n %s/%s", manifestData.getPackage(), //$NON-NLS-1$ + activityName); + try { + CountDownLatch launchedLatch = new CountDownLatch(1); + ShellOutputReceiver receiver = new ShellOutputReceiver(launchedLatch); + device.executeShellCommand(command, receiver); + launchedLatch.await(5, TimeUnit.SECONDS); + String shellOutput = receiver.getOutput(); + if (shellOutput.contains("Error type")) { //$NON-NLS-1$ + throw new RuntimeException(receiver.getOutput()); + } + } catch (Exception e) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_ActivityLaunchError, e); + return false; + } + + // kill existing gdbserver + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_KillExistingGdbServer); + for (Client c: device.getClients()) { + String description = c.getClientData().getClientDescription(); + if (description != null && description.contains("gdbserver")) { //$NON-NLS-1$ + c.kill(); + } + } + + // pull app_process & libc from the device + IPath solibFolder = project.getLocation().append("obj/local").append(compatAbi.name()); + try { + pull(device, "/system/bin/app_process", solibFolder); //$NON-NLS-1$ + pull(device, "/system/lib/libc.so", solibFolder); //$NON-NLS-1$ + } catch (Exception e) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_PullFileError, e); + return false; + } + + // wait for a couple of seconds for activity to be launched + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_WaitingForActivity); + try { + Thread.sleep(2000); + } catch (InterruptedException e1) { + // uninterrupted + } + + // get pid of activity + Client app = device.getClient(manifestData.getPackage()); + int pid = app.getClientData().getPid(); + + // launch gdbserver + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_LaunchingGdbServer); + CountDownLatch attachLatch = new CountDownLatch(1); + GdbServerTask gdbServer = new GdbServerTask(device, manifestData.getPackage(), + DEBUG_SOCKET, pid, attachLatch); + new Thread(gdbServer, + String.format("gdbserver for %s", manifestData.getPackage())).start(); //$NON-NLS-1$ + + // wait for gdbserver to attach + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_WaitGdbServerAttach); + boolean attached = false; + try { + attached = attachLatch.await(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_InterruptedWaitingForGdbserver); + return false; + } + + // if gdbserver failed to attach, we report any errors that may have occurred + if (!attached) { + if (gdbServer.getLaunchException() != null) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_gdbserverLaunchException, + gdbServer.getLaunchException()); + } else { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_gdbserverOutput, + gdbServer.getShellOutput()); + } + + // shut down the gdbserver thread + gdbServer.setCancelled(); + return false; + } + + // Obtain application working directory + String appDir = null; + try { + appDir = getAppDirectory(device, manifestData.getPackage(), 5, TimeUnit.SECONDS); + } catch (Exception e) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_ObtainingAppFolder, e); + return false; + } + + // setup port forwarding between local port & remote (device) unix domain socket + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_SettingUpPortForward); + String localport = config.getAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, + NdkDebuggerConfigTab.DEFAULT_GDB_PORT); + try { + device.createForward(Integer.parseInt(localport), + String.format("%s/%s", appDir, DEBUG_SOCKET), //$NON-NLS-1$ + DeviceUnixSocketNamespace.FILESYSTEM); + } catch (Exception e) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_PortForwarding, e); + return false; + } + + // update launch attributes based on device + config = performVariableSubstitutions(config, project, compatAbi, monitor); + + // launch gdb + monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_LaunchHostGdb); + super.launch(config, mode, launch, monitor); + return true; + } + + private void pull(IDevice device, String remote, IPath solibFolder) throws + SyncException, IOException, AdbCommandRejectedException, TimeoutException { + String remoteFileName = new Path(remote).toFile().getName(); + String targetFile = solibFolder.append(remoteFileName).toString(); + device.pullFile(remote, targetFile); + } + + private ILaunchConfiguration performVariableSubstitutions(ILaunchConfiguration config, + IProject project, NativeAbi compatAbi, IProgressMonitor monitor) throws CoreException { + ILaunchConfigurationWorkingCopy wcopy = config.getWorkingCopy(); + + String toolchainPrefix = NdkHelper.getToolchainPrefix(project, compatAbi, monitor); + String gdb = toolchainPrefix + "gdb"; //$NON-NLS-1$ + + IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); + IValueVariable ndkGdb = manager.newValueVariable(NdkVariables.NDK_GDB, + NdkVariables.NDK_GDB, true, gdb); + IValueVariable ndkProject = manager.newValueVariable(NdkVariables.NDK_PROJECT, + NdkVariables.NDK_PROJECT, true, project.getLocation().toOSString()); + IValueVariable ndkCompatAbi = manager.newValueVariable(NdkVariables.NDK_COMPAT_ABI, + NdkVariables.NDK_COMPAT_ABI, true, compatAbi.name()); + + IValueVariable[] ndkVars = new IValueVariable[] { ndkGdb, ndkProject, ndkCompatAbi }; + manager.addVariables(ndkVars); + + // fix path to gdb + String userGdbPath = wcopy.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + NdkDebuggerConfigTab.DEFAULT_GDB); + wcopy.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + elaborateExpression(manager, userGdbPath)); + + // fix program name + String userProgramPath = wcopy.getAttribute( + ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + NdkDebuggerConfigTab.DEFAULT_PROGRAM); + wcopy.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + elaborateExpression(manager, userProgramPath)); + + // fix solib paths + List<String> solibPaths = wcopy.getAttribute( + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + Collections.singletonList(NdkDebuggerConfigTab.DEFAULT_SOLIB_PATH)); + List<String> fixedSolibPaths = new ArrayList<String>(solibPaths.size()); + for (String u : solibPaths) { + fixedSolibPaths.add(elaborateExpression(manager, u)); + } + wcopy.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + fixedSolibPaths); + + manager.removeVariables(ndkVars); + + return wcopy.doSave(); + } + + private String elaborateExpression(IStringVariableManager manager, String expr) + throws CoreException{ + boolean DEBUG = true; + + String eval = manager.performStringSubstitution(expr); + if (DEBUG) { + AdtPlugin.printToConsole("Substitute: ", expr, " --> ", eval); + } + + return eval; + } + + /** + * Returns the activity name to launch. If the user has requested a particular activity to + * be launched, then this method will confirm that the requested activity is defined in the + * manifest. If the user has not specified any activities, then it returns the default + * launcher activity. + * @param activityNameInLaunchConfig activity to launch as requested by the user. + * @param activities list of activities as defined in the application's manifest + * @param project android project + * @return activity name that should be launched, or null if no launchable activity. + */ + private String getActivityToLaunch(String activityNameInLaunchConfig, Activity launcherActivity, + Activity[] activities, IProject project) { + if (activities.length == 0) { + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_NoActivityInManifest); + return null; + } else if (activityNameInLaunchConfig == null && launcherActivity != null) { + return launcherActivity.getName(); + } else { + for (Activity a : activities) { + if (a != null && a.getName().equals(activityNameInLaunchConfig)) { + return activityNameInLaunchConfig; + } + } + + AdtPlugin.printErrorToConsole(project, + Messages.NdkGdbLaunchDelegate_LaunchError_NoSuchActivity); + if (launcherActivity != null) { + return launcherActivity.getName(); + } else { + AdtPlugin.printErrorToConsole( + Messages.NdkGdbLaunchDelegate_LaunchError_NoLauncherActivity); + return null; + } + } + } + + private NativeAbi getCompatibleAbi(String deviceAbi1, String deviceAbi2, + List<NativeAbi> appAbis) { + for (NativeAbi abi: appAbis) { + if (abi.toString().equals(deviceAbi1) || abi.toString().equals(deviceAbi2)) { + return abi; + } + } + + return null; + } + + /** Returns the name of the activity as defined in the launch configuration. */ + private String getActivityNameInLaunchConfig(ILaunchConfiguration configuration) { + String empty = ""; //$NON-NLS-1$ + String activityName; + try { + activityName = configuration.getAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, empty); + } catch (CoreException e) { + return null; + } + + return (activityName != empty) ? activityName : null; + } + + private String getAppDirectory(IDevice device, String app, long timeout, TimeUnit timeoutUnit) + throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, + IOException, InterruptedException { + String command = String.format("run-as %s /system/bin/sh -c pwd", app); //$NON-NLS-1$ + + CountDownLatch commandCompleteLatch = new CountDownLatch(1); + ShellOutputReceiver receiver = new ShellOutputReceiver(commandCompleteLatch); + device.executeShellCommand(command, receiver); + commandCompleteLatch.await(timeout, timeoutUnit); + return receiver.getOutput().trim(); + } + + private static class ShellOutputReceiver implements IShellOutputReceiver { + private StringBuffer sb = new StringBuffer(); + private CountDownLatch mCompleteLatch; + + public ShellOutputReceiver(CountDownLatch commandCompleteLatch) { + mCompleteLatch = commandCompleteLatch; + } + + @Override + public void addOutput(byte[] data, int offset, int length) { + sb.append(new String(data, offset, length)); + } + + @Override + public void flush() { + if (mCompleteLatch != null) { + mCompleteLatch.countDown(); + } + } + + @Override + public boolean isCancelled() { + return false; + } + + public String getOutput() { + return sb.toString(); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java new file mode 100644 index 0000000..841f1dc --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.ndk.internal.launch; + +import com.android.ide.eclipse.adt.internal.launch.MainLaunchConfigTab; +import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.IProjectChooserFilter; +import com.android.ide.eclipse.adt.internal.sdk.ProjectState; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; + +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.core.resources.IProject; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; + +@SuppressWarnings("restriction") +public class NdkMainLaunchConfigTab extends MainLaunchConfigTab { + private static class NdkProjectOnlyFilter implements IProjectChooserFilter { + @Override + public boolean accept(IProject project) { + ProjectState state = Sdk.getProjectState(project); + if (state == null) { + return false; + } + + return !state.isLibrary() + && (CoreModel.hasCCNature(project) || CoreModel.hasCNature(project)); + } + + @Override + public boolean useCache() { + return true; + } + } + + @Override + protected IProjectChooserFilter getProjectFilter() { + return new NdkProjectOnlyFilter(); + } + + @Override + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + super.performApply(configuration); + + configuration.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, + mProjText.getText().trim()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties new file mode 100644 index 0000000..362e625 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties @@ -0,0 +1,31 @@ +NdkGdbLaunchDelegate_Action_ActivityLaunch=Launching activity +NdkGdbLaunchDelegate_Action_CheckAndroidDeviceVersion=Checking Android version on device +NdkGdbLaunchDelegate_Action_KillExistingGdbServer=Killing existing gdbserver +NdkGdbLaunchDelegate_Action_LaunchHostGdb=Launching host gdb +NdkGdbLaunchDelegate_Action_LaunchingGdbServer=Launching gdbserver +NdkGdbLaunchDelegate_Action_ObtainAppAbis=Obtaining ABI's supported by the application +NdkGdbLaunchDelegate_Action_ObtainDevice=Obtaining device to use +NdkGdbLaunchDelegate_Action_ObtainDeviceABI=Obtaining ABI supported by the device +NdkGdbLaunchDelegate_Action_PerformIncrementalBuild=Performing Incremental Build +NdkGdbLaunchDelegate_Action_SettingUpPortForward=Setting up port forwarding +NdkGdbLaunchDelegate_Action_SyncAppToDevice=Syncing application to device +NdkGdbLaunchDelegate_Action_WaitGdbServerAttach=Waiting for gdbserver to attach to process +NdkGdbLaunchDelegate_Action_WaitingForActivity=Waiting for activity to be launched +NdkGdbLaunchDelegate_LaunchError_ActivityLaunchError=Error launching activity +NdkGdbLaunchDelegate_LaunchError_Api8Needed=Native debugging requires API level 8 or above. +NdkGdbLaunchDelegate_LaunchError_CouldNotGetProject=Couldn't get project object\! +NdkGdbLaunchDelegate_LaunchError_gdbserverLaunchException=Exception while launching gdbserver: +NdkGdbLaunchDelegate_LaunchError_gdbserverOutput=gdbserver output +NdkGdbLaunchDelegate_LaunchError_InstallError=Installation error +NdkGdbLaunchDelegate_LaunchError_InterruptedWaitingForGdbserver=Interrupted while waiting for gdbserver to attach +NdkGdbLaunchDelegate_LaunchError_NoActivityInManifest=The Manifest defines no activity\! +NdkGdbLaunchDelegate_LaunchError_NoCompatibleAbi=Unable to find a compatible ABI +NdkGdbLaunchDelegate_LaunchError_NoLauncherActivity=No launcher activity specified. Aborting launch. +NdkGdbLaunchDelegate_LaunchError_NoSuchActivity=The specified activity does not exist\! Getting the launcher activity. +NdkGdbLaunchDelegate_LaunchError_NullApk=Null APK +NdkGdbLaunchDelegate_LaunchError_ObtainingAppFolder=Error while obtaining application data folder on device +NdkGdbLaunchDelegate_LaunchError_PortForwarding=Error while setting up port forwarding +NdkGdbLaunchDelegate_LaunchError_ProjectHasErrors=Your project contains error(s), please fix them before running your application. +NdkGdbLaunchDelegate_LaunchError_PullFileError=Error while obtaining file from device +NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi=Unable to detect application ABI's +NdkGdbLaunchDelegate_LaunchError_UnknownAndroidDeviceVersion=Unknown Android version on device. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java index 4829c02..094ac63 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.launch; import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.IProjectChooserFilter; import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.NonLibraryProjectOnlyFilter; import com.android.sdklib.xml.ManifestData; import com.android.sdklib.xml.ManifestData.Activity; @@ -112,10 +113,13 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { public MainLaunchConfigTab() { } + protected IProjectChooserFilter getProjectFilter() { + return new NonLibraryProjectOnlyFilter(); + } + @Override public void createControl(Composite parent) { - mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), - new NonLibraryProjectOnlyFilter()); + mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), getProjectFilter()); Font font = parent.getFont(); Composite comp = new Composite(parent, SWT.NONE); |
