aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Activator.java84
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Messages.java63
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NativeAbi.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkHelper.java184
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkManager.java74
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkVariables.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/actions/AddNativeAction.java69
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkCommandLauncher.java133
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkEnvSupplier.java121
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveredPathInfo.java203
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveryUpdater.java314
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkScannerInfoCollector.java103
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/GdbServerTask.java118
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/Messages.java62
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkDebuggerTab.java311
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java39
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java506
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchShortcut.java127
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkLaunchConstants.java44
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkMainLaunchConfigTab.java61
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/messages.properties32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/messages.properties16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferenceInitializer.java33
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferencePage.java93
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SetFolders.java105
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SimpleFile.java125
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/TemplatedInputStream.java87
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizard.java130
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizardPage.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/NdkWizardHandler.java41
30 files changed, 3437 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Activator.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Activator.java
new file mode 100644
index 000000000..e165df1c5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Activator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import java.net.URL;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.android.ide.eclipse.ndk"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator mPlugin;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ mPlugin = this;
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ mPlugin = null;
+ super.stop(context);
+ }
+
+ public static Activator getDefault() {
+ return mPlugin;
+ }
+
+ public static <T> T getService(Class<T> clazz) {
+ BundleContext context = mPlugin.getBundle().getBundleContext();
+ ServiceReference ref = context.getServiceReference(clazz.getName());
+ return (ref != null) ? (T) context.getService(ref) : null;
+ }
+
+ public static Bundle getBundle(String id) {
+ for (Bundle bundle : mPlugin.getBundle().getBundleContext().getBundles()) {
+ if (bundle.getSymbolicName().equals(id)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ public static IStatus newStatus(Exception e) {
+ return new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage(), e);
+ }
+
+ public static void log(Exception e) {
+ mPlugin.getLog().log(newStatus(e));
+ }
+
+ public static URL findFile(IPath path) {
+ return FileLocator.find(mPlugin.getBundle(), path, null);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Messages.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Messages.java
new file mode 100644
index 000000000..cd6c4a31f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/Messages.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "com.android.ide.eclipse.ndk.internal.messages"; //$NON-NLS-1$
+
+ public static String AddNativeWizardPage_Description;
+
+ public static String AddNativeWizardPage_LibraryName;
+
+ public static String AddNativeWizardPage_Location_not_valid;
+
+ public static String AddNativeWizardPage_Title;
+
+ public static String NDKPreferencePage_Location;
+
+ public static String NDKPreferencePage_not_a_valid_directory;
+
+ public static String NDKPreferencePage_not_a_valid_NDK_directory;
+
+ public static String NDKPreferencePage_Preferences;
+
+ public static String SetFolders_Missing_project_name;
+
+ public static String SetFolders_No_folders;
+
+ public static String SetFolders_Project_does_not_exist;
+
+ public static String SimpleFile_Bad_file_operation;
+
+ public static String SimpleFile_Bundle_not_found;
+
+ public static String SimpleFile_Could_not_fine_source;
+
+ public static String SimpleFile_No_project_name;
+
+ public static String SimpleFile_Project_does_not_exist;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NativeAbi.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NativeAbi.java
new file mode 100644
index 000000000..3c7654266
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NativeAbi.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal;
+
+import com.android.SdkConstants;
+
+public enum NativeAbi {
+ armeabi(SdkConstants.ABI_ARMEABI),
+ armeabi_v7a(SdkConstants.ABI_ARMEABI_V7A),
+ mips(SdkConstants.ABI_MIPS),
+ x86(SdkConstants.ABI_INTEL_ATOM);
+
+ private final String mAbi;
+
+ private NativeAbi(String abi) {
+ mAbi = abi;
+ }
+
+ public String getAbi() {
+ return mAbi;
+ }
+
+ public static NativeAbi getByString(String abi) {
+ for (NativeAbi a: values()) {
+ if (a.getAbi().equals(abi)) {
+ return a;
+ }
+ }
+
+ throw new IllegalArgumentException("Unknown abi: " + abi);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkHelper.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkHelper.java
new file mode 100644
index 000000000..8ad1f24c4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkHelper.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.ndk.internal.launch.NdkLaunchConstants;
+
+import org.eclipse.cdt.core.CommandLauncher;
+import org.eclipse.cdt.core.ICommandLauncher;
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
+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.runtime.Platform;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("restriction")
+public class NdkHelper {
+ private static final String MAKE = "make"; //$NON-NLS-1$
+ private static final String CORE_MAKEFILE_PATH = "/build/core/build-local.mk"; //$NON-NLS-1$
+
+ /**
+ * 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 Collection<NativeAbi> getApplicationAbis(IProject project,
+ IProgressMonitor monitor) {
+ ICommandLauncher launcher = new CommandLauncher();
+ launcher.setProject(project);
+ String[] args = new String[] {
+ "--no-print-dir", //$NON-NLS-1$
+ "-f", //$NON-NLS-1$
+ NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH,
+ "-C", //$NON-NLS-1$
+ project.getLocation().toOSString(),
+ "DUMP_APP_ABI", //$NON-NLS-1$
+ };
+ 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();
+ Set<NativeAbi> nativeAbis = EnumSet.noneOf(NativeAbi.class);
+ for (String abi: abis.split(" ")) { //$NON-NLS-1$
+ if (abi.equals("all")) { //$NON-NLS-1$
+ return EnumSet.allOf(NativeAbi.class);
+ }
+
+ try {
+ nativeAbis.add(NativeAbi.getByString(abi));
+ } catch (IllegalArgumentException e) {
+ AdtPlugin.printErrorToConsole(project, "Unknown Application ABI: ", abi);
+ }
+ }
+
+ 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", //$NON-NLS-1$
+ "-f", //$NON-NLS-1$
+ NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH,
+ "-C", //$NON-NLS-1$
+ project.getLocation().toOSString(),
+ "DUMP_TOOLCHAIN_PREFIX", //$NON-NLS-1$
+ "APP_ABI=" + abi.getAbi(), //$NON-NLS-1$
+ };
+ 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 getFullPathTo(MAKE);
+ }
+
+ /**
+ * Obtain a path to the utilities prebuilt folder in NDK. This is typically
+ * "${NdkRoot}/prebuilt/<platform>/bin/". If the executable is not found, it simply returns
+ * the name of the executable (which is equal to assuming that it is available on the path).
+ */
+ private static synchronized IPath getFullPathTo(String executable) {
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ executable += ".exe";
+ }
+
+ IPath ndkRoot = new Path(NdkManager.getNdkLocation());
+ IPath prebuilt = ndkRoot.append("prebuilt"); //$NON-NLS-1$
+ if (!prebuilt.toFile().exists() || !prebuilt.toFile().canRead()) {
+ return new Path(executable);
+ }
+
+ File[] platforms = prebuilt.toFile().listFiles();
+ if (platforms != null) {
+ for (File p: platforms) {
+ IPath exePath = prebuilt.append(p.getName())
+ .append("bin") //$NON-NLS-1$
+ .append(executable);
+ if (exePath.toFile().exists()) {
+ return exePath;
+ }
+ }
+ }
+
+ return new Path(executable);
+ }
+
+ public static void setLaunchConfigDefaults(ILaunchConfigurationWorkingCopy config) {
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, true);
+ config.setAttribute(NdkLaunchConstants.ATTR_NDK_GDB, NdkLaunchConstants.DEFAULT_GDB);
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT,
+ NdkLaunchConstants.DEFAULT_GDBINIT);
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT,
+ NdkLaunchConstants.DEFAULT_GDB_PORT);
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, "localhost"); //$NON-NLS-1$
+ 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,
+ NdkLaunchConstants.DEFAULT_PROGRAM);
+
+ config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
+ IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE);
+ config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID,
+ "gdbserver"); //$NON-NLS-1$
+
+ List<String> solibPaths = new ArrayList<String>(2);
+ solibPaths.add(NdkLaunchConstants.DEFAULT_SOLIB_PATH);
+ config.setAttribute(NdkLaunchConstants.ATTR_NDK_SOLIB, solibPaths);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkManager.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkManager.java
new file mode 100644
index 000000000..98fccff02
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkManager.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010, 2011 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.ndk.internal;
+
+import org.eclipse.cdt.core.templateengine.TemplateCore;
+import org.eclipse.cdt.core.templateengine.TemplateEngine;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import java.io.File;
+import java.util.Map;
+
+public class NdkManager {
+
+ public static final String NDK_LOCATION = "ndkLocation"; //$NON-NLS-1$
+
+ public static final String LIBRARY_NAME = "libraryName"; //$NON-NLS-1$
+
+ public static String getNdkLocation() {
+ return Activator.getDefault().getPreferenceStore().getString(NDK_LOCATION);
+ }
+
+ public static boolean isNdkLocationValid() {
+ String location = getNdkLocation();
+ if (location.length() == 0)
+ return false;
+
+ return isValidNdkLocation(location);
+ }
+
+ public static boolean isValidNdkLocation(String location) {
+ File dir = new File(location);
+ if (!dir.isDirectory())
+ return false;
+
+ // Must contain the ndk-build script which we call to build
+ if (!new File(dir, "ndk-build").isFile()) //$NON-NLS-1$
+ return false;
+
+ return true;
+ }
+
+ public static void addNativeSupport(final IProject project, Map<String, String> templateArgs,
+ IProgressMonitor monitor)
+ throws CoreException {
+ // Launch our template to set up the project contents
+ TemplateCore template = TemplateEngine.getDefault().getTemplateById("AddNdkSupport"); //$NON-NLS-1$
+ Map<String, String> valueStore = template.getValueStore();
+ valueStore.put("projectName", project.getName()); //$NON-NLS-1$
+ valueStore.putAll(templateArgs);
+ template.executeTemplateProcesses(monitor, false);
+
+ // refresh project resources
+ project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 10));
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkVariables.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkVariables.java
new file mode 100644
index 000000000..0e1cd206c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/NdkVariables.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 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.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.ndk/src/com/android/ide/eclipse/ndk/internal/actions/AddNativeAction.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/actions/AddNativeAction.java
new file mode 100644
index 000000000..11f65a4bf
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/actions/AddNativeAction.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.actions;
+
+import com.android.ide.eclipse.ndk.internal.wizards.AddNativeWizard;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class AddNativeAction implements IObjectActionDelegate {
+
+ private IWorkbenchPart mPart;
+ private ISelection mSelection;
+
+ @Override
+ public void run(IAction action) {
+ IProject project = null;
+ if (mSelection instanceof IStructuredSelection) {
+ IStructuredSelection ss = (IStructuredSelection) mSelection;
+ if (ss.size() == 1) {
+ Object obj = ss.getFirstElement();
+ if (obj instanceof IProject) {
+ project = (IProject) obj;
+ } else if (obj instanceof PlatformObject) {
+ project = (IProject) ((PlatformObject) obj).getAdapter(IProject.class);
+ }
+ }
+ }
+
+ if (project != null) {
+ AddNativeWizard wizard = new AddNativeWizard(project, mPart.getSite()
+ .getWorkbenchWindow());
+ WizardDialog dialog = new WizardDialog(mPart.getSite().getShell(), wizard);
+ dialog.open();
+ }
+
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ mSelection = selection;
+ }
+
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ mPart = targetPart;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkCommandLauncher.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkCommandLauncher.java
new file mode 100644
index 000000000..0a1f7dc81
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkCommandLauncher.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.build;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.ndk.internal.NdkManager;
+
+import org.eclipse.cdt.core.CommandLauncher;
+import org.eclipse.cdt.utils.CygPath;
+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.runtime.Platform;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+@SuppressWarnings("restriction") // for AdtPlugin internal classes
+public class NdkCommandLauncher extends CommandLauncher {
+ private static CygPath sCygPath = null;
+
+ private static final List<String> WINDOWS_NATIVE_EXECUTABLES = Arrays.asList(
+ "exe", //$NON-NLS-1$
+ "cmd", //$NON-NLS-1$
+ "bat" //$NON-NLS-1$
+ );
+
+ static {
+ if (Platform.OS_WIN32.equals(Platform.getOS())) {
+ try {
+ sCygPath = new CygPath();
+ } catch (IOException e) {
+ AdtPlugin.printErrorToConsole("Unable to launch cygpath. Is Cygwin on the path?",
+ e);
+ }
+ }
+ }
+
+ @Override
+ public Process execute(IPath commandPath, String[] args, String[] env, IPath changeToDirectory,
+ IProgressMonitor monitor)
+ throws CoreException {
+ if (!commandPath.isAbsolute())
+ commandPath = new Path(NdkManager.getNdkLocation()).append(commandPath);
+
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ // convert cygwin paths to standard paths
+ if (sCygPath != null && commandPath.toString().startsWith("/cygdrive")) { //$NON-NLS-1$
+ try {
+ String path = sCygPath.getFileName(commandPath.toString());
+ commandPath = new Path(path);
+ } catch (IOException e) {
+ AdtPlugin.printErrorToConsole(
+ "Unexpected error while transforming cygwin path.", e);
+ }
+ }
+
+ if (isWindowsExecutable(commandPath)) {
+ // append necessary extension
+ commandPath = appendExecutableExtension(commandPath);
+ } else {
+ // Invoke using Cygwin shell if this is not a native windows executable
+ String[] newargs = new String[args.length + 1];
+ newargs[0] = commandPath.toOSString();
+ System.arraycopy(args, 0, newargs, 1, args.length);
+
+ commandPath = new Path("sh"); //$NON-NLS-1$
+ args = newargs;
+ }
+ }
+
+ return super.execute(commandPath, args, env, changeToDirectory, monitor);
+ }
+
+ private boolean isWindowsExecutable(IPath commandPath) {
+ String ext = commandPath.getFileExtension();
+ if (isWindowsExecutableExtension(ext)) {
+ return true;
+ }
+
+ ext = findWindowsExecutableExtension(commandPath);
+ if (ext != null) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private IPath appendExecutableExtension(IPath commandPath) {
+ if (isWindowsExecutableExtension(commandPath.getFileExtension())) {
+ return commandPath;
+ }
+
+ String ext = findWindowsExecutableExtension(commandPath);
+ if (ext != null) {
+ return commandPath.addFileExtension(ext);
+ }
+
+ return commandPath;
+ }
+
+ private String findWindowsExecutableExtension(IPath command) {
+ for (String e: WINDOWS_NATIVE_EXECUTABLES) {
+ File exeFile = command.addFileExtension(e).toFile();
+ if (exeFile.exists()) {
+ return e;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isWindowsExecutableExtension(String extension) {
+ return extension != null && WINDOWS_NATIVE_EXECUTABLES.contains(extension);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkEnvSupplier.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkEnvSupplier.java
new file mode 100644
index 000000000..65645537f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/build/NdkEnvSupplier.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.build;
+
+import org.eclipse.cdt.managedbuilder.core.IConfiguration;
+import org.eclipse.cdt.managedbuilder.envvar.IBuildEnvironmentVariable;
+import org.eclipse.cdt.managedbuilder.envvar.IConfigurationEnvironmentVariableSupplier;
+import org.eclipse.cdt.managedbuilder.envvar.IEnvironmentVariableProvider;
+import org.eclipse.core.runtime.Platform;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NdkEnvSupplier implements IConfigurationEnvironmentVariableSupplier {
+
+ private static Map<String, IBuildEnvironmentVariable> mEnvVars;
+
+ private synchronized void init() {
+ if (mEnvVars != null)
+ return;
+
+ mEnvVars = new HashMap<String, IBuildEnvironmentVariable>();
+
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ // For Windows, need to add a shell to the path
+ IBuildEnvironmentVariable path = new IBuildEnvironmentVariable() {
+ @Override
+ public String getName() {
+ return "PATH"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getValue() {
+ // I'm giving MSYS precedence over Cygwin. I'm biased that
+ // way :)
+ // TODO using the default paths for now, need smarter ways
+ // to get at them
+ // Alternatively the user can add the bin to their path
+ // themselves.
+ File bin = new File("C:\\MinGW\\msys\\1.0\\bin"); //$NON-NLS-1$
+ if (bin.isDirectory()) {
+ return bin.getAbsolutePath();
+ }
+
+ bin = new File("C:\\cygwin\\bin"); //$NON-NLS-1$
+ if (bin.isDirectory())
+ return bin.getAbsolutePath();
+
+ return null;
+ }
+
+ @Override
+ public int getOperation() {
+ return ENVVAR_PREPEND;
+ }
+
+ @Override
+ public String getDelimiter() {
+ return ";"; //$NON-NLS-1$
+ }
+ };
+ if (path.getValue() != null)
+ mEnvVars.put(path.getName(), path);
+
+ // Since we're using real paths, need to tell cygwin it's OK
+ IBuildEnvironmentVariable cygwin = new IBuildEnvironmentVariable() {
+ @Override
+ public String getName() {
+ return "CYGWIN"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getValue() {
+ return "nodosfilewarning"; //$NON-NLS-1$
+ }
+
+ @Override
+ public int getOperation() {
+ return ENVVAR_REPLACE;
+ }
+
+ @Override
+ public String getDelimiter() {
+ return null;
+ }
+ };
+
+ mEnvVars.put(cygwin.getName(), cygwin);
+ }
+ }
+
+ @Override
+ public IBuildEnvironmentVariable getVariable(String variableName,
+ IConfiguration configuration, IEnvironmentVariableProvider provider) {
+ init();
+ return mEnvVars.get(variableName);
+ }
+
+ @Override
+ public IBuildEnvironmentVariable[] getVariables(
+ IConfiguration configuration, IEnvironmentVariableProvider provider) {
+ init();
+ return mEnvVars.values().toArray(new IBuildEnvironmentVariable[mEnvVars.size()]);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveredPathInfo.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveredPathInfo.java
new file mode 100644
index 000000000..83ce7f40e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveredPathInfo.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.discovery;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IDiscoveredPathInfo;
+import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IDiscoveredScannerInfoSerializable;
+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 java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class NdkDiscoveredPathInfo implements IDiscoveredPathInfo {
+
+ private final IProject mProject;
+ private long mLastUpdate = IFile.NULL_STAMP;
+ private IPath[] mIncludePaths;
+ private Map<String, String> mSymbols;
+ private boolean mNeedReindexing = false;
+ private static final IPath ANDROID_MK = new Path("jni/Android.mk");
+
+ // Keys for preferences
+ public static final String LAST_UPDATE = "lastUpdate"; //$NON-NLS-1$
+
+ public NdkDiscoveredPathInfo(IProject project) {
+ this.mProject = project;
+ load();
+ }
+
+ @Override
+ public IProject getProject() {
+ return mProject;
+ }
+
+ @Override
+ public IPath[] getIncludePaths() {
+ if (mNeedReindexing) {
+ // Call for a reindex
+ // TODO this is probably a bug. a new include path should trigger
+ // reindexing anyway, no?
+ // BTW, can't do this in the update since the indexer runs before
+ // this gets called
+ CCorePlugin.getIndexManager().reindex(CoreModel.getDefault().create(mProject));
+ mNeedReindexing = false;
+ }
+ return mIncludePaths;
+ }
+
+ void setIncludePaths(List<String> pathStrings) {
+ mIncludePaths = new IPath[pathStrings.size()];
+ int i = 0;
+ for (String path : pathStrings)
+ mIncludePaths[i++] = new Path(path);
+ mNeedReindexing = true;
+ }
+
+ @Override
+ public Map<String, String> getSymbols() {
+ if (mSymbols == null)
+ mSymbols = new HashMap<String, String>();
+ return mSymbols;
+ }
+
+ void setSymbols(Map<String, String> symbols) {
+ this.mSymbols = symbols;
+ }
+
+ @Override
+ public IDiscoveredScannerInfoSerializable getSerializable() {
+ return null;
+ }
+
+ public void update(IProgressMonitor monitor) throws CoreException {
+ if (!needUpdating())
+ return;
+
+ new NdkDiscoveryUpdater(this).runUpdate(monitor);
+
+ if (mIncludePaths != null && mSymbols != null) {
+ recordUpdate();
+ save();
+ }
+ }
+
+ private boolean needUpdating() {
+ if (mLastUpdate == IFile.NULL_STAMP)
+ return true;
+ return mProject.getFile(ANDROID_MK).getLocalTimeStamp() > mLastUpdate;
+ }
+
+ private void recordUpdate() {
+ mLastUpdate = mProject.getFile(ANDROID_MK).getLocalTimeStamp();
+ }
+
+ public void delete() {
+ mLastUpdate = IFile.NULL_STAMP;
+ }
+
+ private File getInfoFile() {
+ File stateLoc = Activator.getDefault().getStateLocation().toFile();
+ return new File(stateLoc, mProject.getName() + ".pathInfo"); //$NON-NLS-1$
+ }
+
+ private void save() {
+ try {
+ File infoFile = getInfoFile();
+ infoFile.getParentFile().mkdirs();
+ PrintStream out = new PrintStream(infoFile);
+
+ // timestamp
+ out.print("t,"); //$NON-NLS-1$
+ out.print(mLastUpdate);
+ out.println();
+
+ for (IPath include : mIncludePaths) {
+ out.print("i,"); //$NON-NLS-1$
+ out.print(include.toPortableString());
+ out.println();
+ }
+
+ for (Entry<String, String> symbol : mSymbols.entrySet()) {
+ out.print("d,"); //$NON-NLS-1$
+ out.print(symbol.getKey());
+ out.print(","); //$NON-NLS-1$
+ out.print(symbol.getValue());
+ out.println();
+ }
+
+ out.close();
+ } catch (IOException e) {
+ Activator.log(e);
+ }
+
+ }
+
+ private void load() {
+ try {
+ File infoFile = getInfoFile();
+ if (!infoFile.exists())
+ return;
+
+ long timestamp = IFile.NULL_STAMP;
+ List<IPath> includes = new ArrayList<IPath>();
+ Map<String, String> defines = new HashMap<String, String>();
+
+ BufferedReader reader = new BufferedReader(new FileReader(infoFile));
+ for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+ switch (line.charAt(0)) {
+ case 't':
+ timestamp = Long.valueOf(line.substring(2));
+ break;
+ case 'i':
+ includes.add(Path.fromPortableString(line.substring(2)));
+ break;
+ case 'd':
+ int n = line.indexOf(',', 2);
+ if (n == -1)
+ defines.put(line.substring(2), ""); //$NON-NLS-1$
+ else
+ defines.put(line.substring(2, n), line.substring(n + 1));
+ break;
+ }
+ }
+ reader.close();
+
+ mLastUpdate = timestamp;
+ mIncludePaths = includes.toArray(new IPath[includes.size()]);
+ mSymbols = defines;
+ } catch (IOException e) {
+ Activator.log(e);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveryUpdater.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveryUpdater.java
new file mode 100644
index 000000000..a6b88f462
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkDiscoveryUpdater.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.discovery;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+import com.android.ide.eclipse.ndk.internal.build.NdkCommandLauncher;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
+import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
+import org.eclipse.cdt.managedbuilder.core.IBuilder;
+import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class NdkDiscoveryUpdater {
+ private final NdkDiscoveredPathInfo mPathInfo;
+ private final IProject mProject;
+
+ private boolean mCPlusPlus = false;
+ private String mCommand;
+ private List<String> mArguments = new ArrayList<String>();
+
+ public NdkDiscoveryUpdater(NdkDiscoveredPathInfo pathInfo) {
+ mPathInfo = pathInfo;
+ mProject = pathInfo.getProject();
+ }
+
+ public void runUpdate(IProgressMonitor monitor) throws CoreException {
+ try {
+ // Run ndk-build -nB to get the list of commands
+ IPath commandPath = new Path("ndk-build"); //$NON-NLS-1$
+ String[] args = {
+ "-nB"}; //$NON-NLS-1$
+ String[] env = calcEnvironment();
+ File projectDir = new File(mProject.getLocationURI());
+ IPath changeToDirectory = new Path(projectDir.getAbsolutePath());
+ Process proc = new NdkCommandLauncher().execute(commandPath, args, env,
+ changeToDirectory, monitor);
+ if (proc == null)
+ // proc failed to start
+ return;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ String line = reader.readLine();
+ while (line != null) {
+ checkBuildLine(line);
+ line = reader.readLine();
+ }
+
+ if (mCommand == null) {
+ return;
+ }
+
+ // Run the unique commands with special gcc options to extract the
+ // symbols and paths
+ // -E -P -v -dD
+ mArguments.add("-E"); //$NON-NLS-1$
+ mArguments.add("-P"); //$NON-NLS-1$
+ mArguments.add("-v"); //$NON-NLS-1$
+ mArguments.add("-dD"); //$NON-NLS-1$
+
+ URL url = Activator.findFile(new Path(
+ "discovery/" + (mCPlusPlus ? "test.cpp" : "test.c"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ File testFile = new File(FileLocator.toFileURL(url).toURI());
+ String testFileName = testFile.getAbsolutePath().replace('\\', '/');
+ mArguments.add(testFileName);
+
+ args = mArguments.toArray(new String[mArguments.size()]);
+ proc = new NdkCommandLauncher().execute(new Path(mCommand), args, env,
+ changeToDirectory, monitor);
+ // Error stream has the includes
+ final InputStream errStream = proc.getErrorStream();
+ new Thread() {
+ @Override
+ public void run() {
+ checkIncludes(errStream);
+ };
+ }.start();
+
+ // Input stream has the defines
+ checkDefines(proc.getInputStream());
+ } catch (IOException e) {
+ throw new CoreException(Activator.newStatus(e));
+ } catch (URISyntaxException e) {
+ throw new CoreException(Activator.newStatus(e));
+ }
+ }
+
+ private String[] calcEnvironment() throws CoreException {
+ IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(mProject);
+ IBuilder builder = info.getDefaultConfiguration().getBuilder();
+ HashMap<String, String> envMap = new HashMap<String, String>();
+ if (builder.appendEnvironment()) {
+ ICConfigurationDescription cfgDes = ManagedBuildManager
+ .getDescriptionForConfiguration(builder.getParent().getParent());
+ IEnvironmentVariableManager mngr = CCorePlugin.getDefault()
+ .getBuildEnvironmentManager();
+ IEnvironmentVariable[] vars = mngr.getVariables(cfgDes, true);
+ for (IEnvironmentVariable var : vars) {
+ envMap.put(var.getName(), var.getValue());
+ }
+ }
+ // Add variables from build info
+ Map<String, String> builderEnv = builder.getExpandedEnvironment();
+ if (builderEnv != null)
+ envMap.putAll(builderEnv);
+ List<String> strings = new ArrayList<String>(envMap.size());
+ for (Entry<String, String> entry : envMap.entrySet()) {
+ StringBuffer buffer = new StringBuffer(entry.getKey());
+ buffer.append('=').append(entry.getValue());
+ strings.add(buffer.toString());
+ }
+ return strings.toArray(new String[strings.size()]);
+ }
+
+ private static class Line {
+ private final String line;
+ private int pos;
+
+ public Line(String line) {
+ this.line = line;
+ }
+
+ public Line(String line, int pos) {
+ this(line);
+ this.pos = pos;
+ }
+
+ public String getToken() {
+ skipWhiteSpace();
+ if (pos == line.length())
+ return null;
+
+ int start = pos;
+ boolean inQuote = false;
+
+ while (true) {
+ char c = line.charAt(pos);
+ if (c == ' ') {
+ if (!inQuote)
+ return line.substring(start, pos);
+ } else if (c == '"') {
+ inQuote = !inQuote;
+ }
+
+ if (++pos == line.length())
+ return null;
+ }
+
+ }
+
+ private String getRemaining() {
+ if (pos == line.length())
+ return null;
+
+ skipWhiteSpace();
+ String rc = line.substring(pos);
+ pos = line.length();
+ return rc;
+ }
+
+ private void skipWhiteSpace() {
+ while (true) {
+ if (pos == line.length())
+ return;
+ char c = line.charAt(pos);
+ if (c == ' ')
+ pos++;
+ else
+ return;
+ }
+ }
+ }
+
+ private void checkBuildLine(String text) {
+ Line line = new Line(text);
+ String cmd = line.getToken();
+ if (cmd == null) {
+ return;
+ } else if (cmd.endsWith("g++")) { //$NON-NLS-1$
+ if (mCommand == null || !mCPlusPlus) {
+ mCommand = cmd;
+ mCPlusPlus = true;
+ }
+ gatherOptions(line);
+ } else if (cmd.endsWith("gcc")) { //$NON-NLS-1$
+ if (mCommand == null)
+ mCommand = cmd;
+ gatherOptions(line);
+ }
+ }
+
+ private void gatherOptions(Line line) {
+ for (String option = line.getToken(); option != null; option = line.getToken()) {
+ if (option.startsWith("-")) { //$NON-NLS-1$
+ // only look at options
+ if (option.equals("-I")) { //$NON-NLS-1$
+ String dir = line.getToken();
+ if (dir != null)
+ addArg(option + dir);
+ } else if (option.startsWith("-I")) { //$NON-NLS-1$
+ addArg(option);
+ } else if (option.equals("-D")) { //$NON-NLS-1$
+ String def = line.getToken();
+ if (def != null)
+ addArg(option + def);
+ } else if (option.startsWith("-D")) { //$NON-NLS-1$
+ addArg(option);
+ } else if (option.startsWith("-f")) { //$NON-NLS-1$
+ addArg(option);
+ } else if (option.startsWith("-m")) { //$NON-NLS-1$
+ addArg(option);
+ } else if (option.startsWith("--sysroot")) { //$NON-NLS-1$
+ addArg(option);
+ }
+ }
+ }
+ }
+
+ private void addArg(String arg) {
+ if (!mArguments.contains(arg))
+ mArguments.add(arg);
+ }
+
+ private void checkIncludes(InputStream in) {
+ try {
+ List<String> includes = new ArrayList<String>();
+ boolean inIncludes1 = false;
+ boolean inIncludes2 = false;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ String line = reader.readLine();
+ while (line != null) {
+ if (!inIncludes1) {
+ if (line.equals("#include \"...\" search starts here:")) //$NON-NLS-1$
+ inIncludes1 = true;
+ } else {
+ if (!inIncludes2) {
+ if (line.equals("#include <...> search starts here:")) //$NON-NLS-1$
+ inIncludes2 = true;
+ else
+ includes.add(line.trim());
+ } else {
+ if (line.equals("End of search list.")) { //$NON-NLS-1$
+ mPathInfo.setIncludePaths(includes);
+ } else {
+ includes.add(line.trim());
+ }
+ }
+ }
+ line = reader.readLine();
+ }
+ } catch (IOException e) {
+ Activator.log(e);
+ }
+ }
+
+ private void checkDefines(InputStream in) {
+ try {
+ Map<String, String> defines = new HashMap<String, String>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ String line = reader.readLine();
+ while (line != null) {
+ if (line.startsWith("#define")) { //$NON-NLS-1$
+ Line l = new Line(line, 7);
+ String var = l.getToken();
+ if (var == null)
+ continue;
+ String value = l.getRemaining();
+ if (value == null)
+ value = ""; //$NON-NLS-1$
+ defines.put(var, value);
+ }
+ line = reader.readLine();
+ }
+ mPathInfo.setSymbols(defines);
+ } catch (IOException e) {
+ Activator.log(e);
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkScannerInfoCollector.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkScannerInfoCollector.java
new file mode 100644
index 000000000..29f3e7f09
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/discovery/NdkScannerInfoCollector.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.discovery;
+
+import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IDiscoveredPathInfo;
+import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector3;
+import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorCleaner;
+import org.eclipse.cdt.make.core.scannerconfig.InfoContext;
+import org.eclipse.cdt.make.core.scannerconfig.ScannerInfoTypes;
+import org.eclipse.cdt.managedbuilder.scannerconfig.IManagedScannerInfoCollector;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.util.List;
+import java.util.Map;
+
+public class NdkScannerInfoCollector implements IScannerInfoCollector3,
+ IScannerInfoCollectorCleaner, IManagedScannerInfoCollector {
+
+ private NdkDiscoveredPathInfo mPathInfo;
+
+ @Override
+ public void contributeToScannerConfig(Object resource, Map scannerInfo) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public List getCollectedScannerInfo(Object resource, ScannerInfoTypes type) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void setProject(IProject project) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void updateScannerConfiguration(IProgressMonitor monitor) throws CoreException {
+ mPathInfo.update(monitor);
+ }
+
+ @Override
+ public IDiscoveredPathInfo createPathInfoObject() {
+ return mPathInfo;
+ }
+
+ @Override
+ public Map<String, String> getDefinedSymbols() {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public List getIncludePaths() {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void setInfoContext(InfoContext context) {
+ mPathInfo = new NdkDiscoveredPathInfo(context.getProject());
+ }
+
+ @Override
+ public void deleteAllPaths(IResource resource) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void deleteAllSymbols(IResource resource) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void deletePath(IResource resource, String path) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void deleteSymbol(IResource resource, String symbol) {
+ throw new Error("Not implemented"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void deleteAll(IResource resource) {
+ mPathInfo.delete();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/GdbServerTask.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/GdbServerTask.java
new file mode 100644
index 000000000..23486ee6f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/GdbServerTask.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 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.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.ndk/src/com/android/ide/eclipse/ndk/internal/launch/Messages.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/Messages.java
new file mode 100644
index 000000000..10f68e48e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/Messages.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal.launch;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "com.android.ide.eclipse.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;
+ public static String NdkGdbLaunchDelegate_LaunchError_VerifyIfDebugBuild;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkDebuggerTab.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkDebuggerTab.java
new file mode 100644
index 000000000..64e7dd80d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkDebuggerTab.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal.launch;
+
+import com.android.ide.eclipse.ndk.internal.NdkHelper;
+import com.android.ide.eclipse.ndk.internal.NdkManager;
+
+import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class NdkDebuggerTab extends AbstractLaunchConfigurationTab {
+ private static String sLastGdbPath;
+ private static String sLastSolibPath;
+
+ private Text mGdbPathText;
+ private Text mGdbInitPathText;
+ private Text mGdbRemotePortText;
+
+ private org.eclipse.swt.widgets.List mSoliblist;
+ private Button mAddSolibButton;
+ private Button mDeleteSolibButton;
+
+ /**
+ * @wbp.parser.entryPoint (Window Builder Entry Point)
+ */
+ @Override
+ public void createControl(Composite parent) {
+ Composite comp = new Composite(parent, SWT.NONE);
+ setControl(comp);
+ comp.setLayout(new GridLayout(1, false));
+
+ Group grpGdb = new Group(comp, SWT.NONE);
+ grpGdb.setText("Launch Options");
+ grpGdb.setLayout(new GridLayout(3, false));
+ grpGdb.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ Label lblDebugger = new Label(grpGdb, SWT.NONE);
+ lblDebugger.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblDebugger.setText("Debugger:");
+
+ mGdbPathText = new Text(grpGdb, SWT.BORDER);
+ mGdbPathText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ final Button btnBrowseGdb = new Button(grpGdb, SWT.NONE);
+ btnBrowseGdb.setText("Browse...");
+
+ Label lblNewLabel = new Label(grpGdb, SWT.NONE);
+ lblNewLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblNewLabel.setText("GDB Command File:");
+
+ mGdbInitPathText = new Text(grpGdb, SWT.BORDER);
+ mGdbInitPathText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ final Button btnBrowseGdbInit = new Button(grpGdb, SWT.NONE);
+ btnBrowseGdbInit.setText("Browse...");
+
+ SelectionListener browseListener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Shell shell = ((Control) e.getSource()).getShell();
+ if (e.getSource() == btnBrowseGdb) {
+ browseForGdb(shell);
+ } else {
+ browseForGdbInit(shell);
+ }
+ checkParameters();
+ updateLaunchConfigurationDialog();
+ }
+ };
+ btnBrowseGdb.addSelectionListener(browseListener);
+ btnBrowseGdbInit.addSelectionListener(browseListener);
+
+ Label lblPort = new Label(grpGdb, SWT.NONE);
+ lblPort.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblPort.setText("Port:");
+
+ mGdbRemotePortText = new Text(grpGdb, SWT.BORDER);
+ GridData gd_text_2 = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ gd_text_2.widthHint = 100;
+ mGdbRemotePortText.setLayoutData(gd_text_2);
+
+ ModifyListener m = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ checkParameters();
+ updateLaunchConfigurationDialog();
+ }
+ };
+ mGdbPathText.addModifyListener(m);
+ mGdbInitPathText.addModifyListener(m);
+ mGdbRemotePortText.addModifyListener(m);
+
+ Group grpSharedLibraries = new Group(comp, SWT.NONE);
+ grpSharedLibraries.setText("Shared Libraries");
+ grpSharedLibraries.setLayout(new GridLayout(2, false));
+ GridData gd_grpSharedLibraries = new GridData(GridData.FILL_BOTH);
+ gd_grpSharedLibraries.verticalAlignment = SWT.TOP;
+ gd_grpSharedLibraries.grabExcessVerticalSpace = true;
+ grpSharedLibraries.setLayoutData(gd_grpSharedLibraries);
+
+ mSoliblist = new org.eclipse.swt.widgets.List(grpSharedLibraries,
+ SWT.BORDER | SWT.V_SCROLL | SWT.SINGLE);
+ GridData gd_list = new GridData(GridData.FILL_BOTH);
+ gd_list.heightHint = 133;
+ gd_list.grabExcessVerticalSpace = false;
+ gd_list.verticalSpan = 1;
+ mSoliblist.setLayoutData(gd_list);
+
+ Composite composite = new Composite(grpSharedLibraries, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+ composite.setLayout(new RowLayout(SWT.VERTICAL));
+
+ mAddSolibButton = new Button(composite, SWT.NONE);
+ mAddSolibButton.setText("Add...");
+
+ mDeleteSolibButton = new Button(composite, SWT.NONE);
+ mDeleteSolibButton.setText("Remove");
+ mDeleteSolibButton.setEnabled(false);
+
+ SelectionListener l = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Control c = (Control) e.getSource();
+ if (c == mSoliblist) {
+ // enable delete only if there is a selection
+ mDeleteSolibButton.setEnabled(mSoliblist.getSelectionCount() > 0);
+ } else if (c == mAddSolibButton) {
+ addSolib(c.getShell());
+ } else {
+ // delete current selection
+ int index = mSoliblist.getSelectionIndex();
+ if (index >= 0) {
+ mSoliblist.remove(index);
+ }
+ }
+ updateLaunchConfigurationDialog();
+ }
+ };
+
+ mSoliblist.addSelectionListener(l);
+ mAddSolibButton.addSelectionListener(l);
+ mDeleteSolibButton.addSelectionListener(l);
+ }
+
+ private void addSolib(Shell shell) {
+ DirectoryDialog dd = new DirectoryDialog(shell);
+ if (sLastSolibPath != null) {
+ dd.setFilterPath(sLastSolibPath);
+ }
+ String solibPath = dd.open();
+
+ if (solibPath != null) {
+ mSoliblist.add(solibPath);
+ sLastSolibPath = new File(solibPath).getParent();
+ }
+ }
+
+ private void browseForGdb(Shell shell) {
+ if (sLastGdbPath == null) {
+ sLastGdbPath = NdkManager.getNdkLocation();
+ }
+
+ FileDialog fd = new FileDialog(shell);
+ fd.setFilterPath(sLastGdbPath);
+
+ String gdbPath = fd.open();
+ if (gdbPath != null) {
+ mGdbPathText.setText(gdbPath);
+ sLastGdbPath = new File(gdbPath).getParent();
+ }
+ }
+
+ private void browseForGdbInit(Shell shell) {
+ FileDialog fd = new FileDialog(shell);
+ String gdbInit = fd.open();
+ if (gdbInit != null) {
+ mGdbInitPathText.setText(gdbInit);
+ }
+ }
+
+ private void checkParameters() {
+ // check gdb path
+ String gdb = mGdbPathText.getText().trim();
+ if (!gdb.equals(NdkLaunchConstants.DEFAULT_GDB)) {
+ File f = new File(gdb);
+ if (!f.exists() || !f.canExecute()) {
+ setErrorMessage("Invalid gdb location.");
+ return;
+ }
+ }
+
+ // check gdb init path
+ String gdbInit = mGdbInitPathText.getText().trim();
+ if (!gdbInit.isEmpty()) {
+ File f = new File(gdbInit);
+ if (!f.exists() || !f.isFile()) {
+ setErrorMessage("Invalid gdbinit location.");
+ return;
+ }
+ }
+
+ // port should be a valid integer
+ String port = mGdbRemotePortText.getText().trim();
+ try {
+ Integer.parseInt(port, 10);
+ } catch (NumberFormatException e) {
+ setErrorMessage("Port should be a valid integer");
+ return;
+ }
+
+ // no errors
+ setErrorMessage(null);
+ setMessage(null);
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy config) {
+ NdkHelper.setLaunchConfigDefaults(config);
+ }
+
+ @Override
+ public void initializeFrom(ILaunchConfiguration config) {
+ mGdbPathText.setText(getAttribute(config, NdkLaunchConstants.ATTR_NDK_GDB,
+ NdkLaunchConstants.DEFAULT_GDB));
+ mGdbInitPathText.setText(getAttribute(config,
+ IGDBLaunchConfigurationConstants.ATTR_GDB_INIT,
+ NdkLaunchConstants.DEFAULT_GDBINIT));
+ mGdbRemotePortText.setText(getAttribute(config, IGDBLaunchConfigurationConstants.ATTR_PORT,
+ NdkLaunchConstants.DEFAULT_GDB_PORT));
+
+ List<String> solibs = getAttribute(config, NdkLaunchConstants.ATTR_NDK_SOLIB,
+ Collections.EMPTY_LIST);
+ mSoliblist.removeAll();
+ for (String s: solibs) {
+ mSoliblist.add(s);
+ }
+ }
+
+ private String getAttribute(ILaunchConfiguration config, String key, String defaultValue) {
+ try {
+ return config.getAttribute(key, defaultValue);
+ } catch (CoreException e) {
+ return defaultValue;
+ }
+ }
+
+ private List<String> getAttribute(ILaunchConfiguration config, String key,
+ List<String> defaultValue) {
+ try {
+ return config.getAttribute(key, defaultValue);
+ } catch (CoreException e) {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy config) {
+ config.setAttribute(NdkLaunchConstants.ATTR_NDK_GDB, mGdbPathText.getText().trim());
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT,
+ mGdbInitPathText.getText().trim());
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT,
+ mGdbRemotePortText.getText().trim());
+ config.setAttribute(NdkLaunchConstants.ATTR_NDK_SOLIB,
+ Arrays.asList(mSoliblist.getItems()));
+ }
+
+ @Override
+ public String getName() {
+ return "Debugger";
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java
new file mode 100644
index 000000000..f8cf73c9f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 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.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 NdkDebuggerTab(),
+ new SourceLookupTab(),
+ new CommonTab()
+ };
+ setTabs(tabs);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java
new file mode 100644
index 000000000..0b124f249
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal.launch;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.CollectingOutputReceiver;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IDevice.DeviceUnixSocketNamespace;
+import com.android.ddmlib.InstallException;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.TimeoutException;
+import com.android.ide.common.xml.ManifestData;
+import com.android.ide.common.xml.ManifestData.Activity;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
+import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
+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.ndk.internal.NativeAbi;
+import com.android.ide.eclipse.ndk.internal.NdkHelper;
+import com.android.ide.eclipse.ndk.internal.NdkVariables;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.google.common.base.Joiner;
+
+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.Collection;
+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 {
+ public static final String LAUNCH_TYPE_ID =
+ "com.android.ide.eclipse.ndk.debug.LaunchConfigType"; //$NON-NLS-1$
+
+ private static final Joiner JOINER = Joiner.on(", ").skipNulls();
+
+ 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(final 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);
+ final ManifestInfo manifestInfo = ManifestInfo.get(project);
+ final AndroidVersion minSdkVersion = new AndroidVersion(
+ manifestInfo.getMinSdkVersion(),
+ manifestInfo.getMinSdkCodeName());
+
+ // 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);
+ Collection<NativeAbi> appAbis = NdkHelper.getApplicationAbis(project, monitor);
+ if (appAbis.size() == 0) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi);
+ return false;
+ }
+
+ // Obtain device to use:
+ // - if there is only 1 device, just use that
+ // - if we have previously launched this config, and the device used is present, use that
+ // - otherwise show the DeviceChooserDialog
+ final String configName = config.getName();
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainDevice);
+ IDevice device = null;
+ IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
+ if (devices.length == 1) {
+ device = devices[0];
+ } else if ((device = getLastUsedDevice(config, devices)) == null) {
+ 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, minSdkVersion, false /*** FIXME! **/);
+ if (dialog.open() == Dialog.OK) {
+ AndroidLaunchController.updateLaunchConfigWithLastUsedDevice(config,
+ response);
+ continueLaunch[0] = true;
+ }
+ };
+ });
+
+ if (!continueLaunch[0]) {
+ return false;
+ }
+
+ 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);
+ AdtPlugin.printErrorToConsole(project,
+ String.format("ABI's supported by the application: %s", JOINER.join(appAbis)));
+ AdtPlugin.printErrorToConsole(project,
+ String.format("ABI's supported by the device: %s, %s", //$NON-NLS-1$
+ deviceAbi1,
+ deviceAbi2));
+ 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);
+ CollectingOutputReceiver receiver = new CollectingOutputReceiver(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.getAbi());
+ 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());
+ }
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_VerifyIfDebugBuild);
+
+ // 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,
+ NdkLaunchConstants.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
+ ILaunchConfiguration config2 = performVariableSubstitutions(config, project, compatAbi,
+ monitor);
+
+ // launch gdb
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_LaunchHostGdb);
+ super.launch(config2, mode, launch, monitor);
+ return true;
+ }
+
+ @Nullable
+ private IDevice getLastUsedDevice(ILaunchConfiguration config, @NonNull IDevice[] devices) {
+ try {
+ boolean reuse = config.getAttribute(LaunchConfigDelegate.ATTR_REUSE_LAST_USED_DEVICE,
+ false);
+ if (!reuse) {
+ return null;
+ }
+
+ String serial = config.getAttribute(LaunchConfigDelegate.ATTR_LAST_USED_DEVICE,
+ (String)null);
+ return AndroidLaunchController.getDeviceIfOnline(serial, devices);
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ 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.getAbi());
+
+ IValueVariable[] ndkVars = new IValueVariable[] { ndkGdb, ndkProject, ndkCompatAbi };
+ manager.addVariables(ndkVars);
+
+ // fix path to gdb
+ String userGdbPath = wcopy.getAttribute(NdkLaunchConstants.ATTR_NDK_GDB,
+ NdkLaunchConstants.DEFAULT_GDB);
+ wcopy.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME,
+ elaborateExpression(manager, userGdbPath));
+
+ // setup program name
+ wcopy.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
+ elaborateExpression(manager, NdkLaunchConstants.DEFAULT_PROGRAM));
+
+ // fix solib paths
+ List<String> solibPaths = wcopy.getAttribute(
+ NdkLaunchConstants.ATTR_NDK_SOLIB,
+ Collections.singletonList(NdkLaunchConstants.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,
+ Collection<NativeAbi> appAbis) {
+ for (NativeAbi abi: appAbis) {
+ if (abi.getAbi().equals(deviceAbi1) || abi.getAbi().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);
+ CollectingOutputReceiver receiver = new CollectingOutputReceiver(commandCompleteLatch);
+ device.executeShellCommand(command, receiver);
+ commandCompleteLatch.await(timeout, timeoutUnit);
+ return receiver.getOutput().trim();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchShortcut.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchShortcut.java
new file mode 100644
index 000000000..22eb118a2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchShortcut.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal.launch;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.ndk.internal.NdkHelper;
+
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorPart;
+
+@SuppressWarnings("restriction") // for adt.internal classes
+public class NdkGdbLaunchShortcut implements ILaunchShortcut {
+ @Override
+ public void launch(ISelection selection, String mode) {
+ if (!(selection instanceof IStructuredSelection)) {
+ return;
+ }
+
+ Object s = ((IStructuredSelection) selection).getFirstElement();
+ if (!(s instanceof IAdaptable)) {
+ return;
+ }
+
+ IResource r = (IResource) ((IAdaptable) s).getAdapter(IResource.class);
+ if (r == null) {
+ return;
+ }
+
+ IProject project = r.getProject();
+ if (project == null) {
+ return;
+ }
+
+ // verify that this is a non library Android project
+ ProjectState state = Sdk.getProjectState(project);
+ if (state == null || state.isLibrary()) {
+ return;
+ }
+
+ // verify that this project has C/C++ nature
+ if (!CoreModel.hasCCNature(project) && !CoreModel.hasCNature(project)) {
+ AdtPlugin.printErrorToConsole(project,
+ String.format("Selected project (%s) does not have C/C++ nature. "
+ + "To add native support, right click on the project, "
+ + "Android Tools -> Add Native Support",
+ project.getName()));
+ return;
+ }
+
+ debugProject(project, mode);
+ }
+
+ @Override
+ public void launch(IEditorPart editor, String mode) {
+ }
+
+ private void debugProject(IProject project, String mode) {
+ // obtain existing native debug config for project
+ ILaunchConfiguration config = AndroidLaunchController.getLaunchConfig(project,
+ NdkGdbLaunchDelegate.LAUNCH_TYPE_ID);
+ if (config == null) {
+ return;
+ }
+
+ // Set the ndk gdb specific launch attributes in the config (if necessary)
+ if (!hasNdkAttributes(config)) {
+ try {
+ config = setNdkDefaults(config, project);
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(project,
+ "Unable to create launch configuration for project.");
+ return;
+ }
+ }
+
+ // launch
+ DebugUITools.launch(config, mode);
+ }
+
+ private boolean hasNdkAttributes(ILaunchConfiguration config) {
+ try {
+ // All NDK launch configurations have ATTR_REMOTE_TCP set to true
+ boolean isRemote = config.getAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP,
+ false);
+ return isRemote;
+ } catch (CoreException e) {
+ return false;
+ }
+ }
+
+ private ILaunchConfiguration setNdkDefaults(ILaunchConfiguration config, IProject project)
+ throws CoreException {
+ ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
+ NdkHelper.setLaunchConfigDefaults(wc);
+ wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName());
+ return wc.doSave();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkLaunchConstants.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkLaunchConstants.java
new file mode 100644
index 000000000..ff70b2e1f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkLaunchConstants.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 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.ndk.internal.launch;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+import com.android.ide.eclipse.ndk.internal.NdkVariables;
+
+public class NdkLaunchConstants {
+ private static final String PREFIX = Activator.PLUGIN_ID + ".ndklaunch."; //$NON-NLS-1$
+
+ public static final String ATTR_NDK_GDB = PREFIX + "gdb"; //$NON-NLS-1$
+ public static final String ATTR_NDK_GDBINIT = PREFIX + "gdbinit"; //$NON-NLS-1$
+ public static final String ATTR_NDK_SOLIB = PREFIX + "solib"; //$NON-NLS-1$
+
+ public static final String DEFAULT_GDB_PORT = "5039"; //$NON-NLS-1$
+ public static final String DEFAULT_GDB = getVar(NdkVariables.NDK_GDB);
+ public static final String DEFAULT_GDBINIT = ""; //$NON-NLS-1$
+ public static final String DEFAULT_PROGRAM =
+ String.format("%1$s/obj/local/%2$s/app_process", //$NON-NLS-1$
+ getVar(NdkVariables.NDK_PROJECT),
+ getVar(NdkVariables.NDK_COMPAT_ABI));
+ public static final String DEFAULT_SOLIB_PATH =
+ String.format("%1$s/obj/local/%2$s/", //$NON-NLS-1$
+ getVar(NdkVariables.NDK_PROJECT),
+ getVar(NdkVariables.NDK_COMPAT_ABI));
+
+ private static String getVar(String varName) {
+ return "${" + varName + '}'; //$NON-NLS-1$
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkMainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkMainLaunchConfigTab.java
new file mode 100644
index 000000000..1e4b39e91
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkMainLaunchConfigTab.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 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.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.ndk/src/com/android/ide/eclipse/ndk/internal/launch/messages.properties b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/messages.properties
new file mode 100644
index 000000000..6770f2de4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/messages.properties
@@ -0,0 +1,32 @@
+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's 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.
+NdkGdbLaunchDelegate_LaunchError_VerifyIfDebugBuild=Verify if the application was built with NDK_DEBUG=1
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/messages.properties b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/messages.properties
new file mode 100644
index 000000000..63400ffc7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/messages.properties
@@ -0,0 +1,16 @@
+AddNativeWizardPage_Description=Settings for generated native components for project.
+AddNativeWizardPage_LibraryName=Library Name:
+AddNativeWizardPage_Location_not_valid=NDK location not valid in preferences.
+AddNativeWizardPage_Title=Add Android Native Support
+NDKPreferencePage_Location=NDK Location
+NDKPreferencePage_not_a_valid_directory=Not a valid directory
+NDKPreferencePage_not_a_valid_NDK_directory=Not a valid NDK directory
+NDKPreferencePage_Preferences=Android NDK Preferences
+SetFolders_Missing_project_name=Missing project name
+SetFolders_No_folders=No folders
+SetFolders_Project_does_not_exist=Project does not exist
+SimpleFile_Bad_file_operation=bad file operation
+SimpleFile_Bundle_not_found=bundle not found
+SimpleFile_Could_not_fine_source=could not find source file:
+SimpleFile_No_project_name=no project name
+SimpleFile_Project_does_not_exist=project does not exist
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferenceInitializer.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferenceInitializer.java
new file mode 100644
index 000000000..9c0350517
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferenceInitializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.preferences;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+import com.android.ide.eclipse.ndk.internal.NdkManager;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class NdkPreferenceInitializer extends AbstractPreferenceInitializer {
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = Activator.getDefault().getPreferenceStore();
+
+ store.setDefault(NdkManager.NDK_LOCATION, ""); //$NON-NLS-1$
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferencePage.java
new file mode 100644
index 000000000..e1ab24768
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/preferences/NdkPreferencePage.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.preferences;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+import com.android.ide.eclipse.ndk.internal.Messages;
+import com.android.ide.eclipse.ndk.internal.NdkManager;
+
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class NdkPreferencePage extends FieldEditorPreferencePage implements
+ IWorkbenchPreferencePage {
+
+ private NdkDirectoryFieldEditor mNdkDirectoryEditor;
+
+ public NdkPreferencePage() {
+ super(GRID);
+ setPreferenceStore(Activator.getDefault().getPreferenceStore());
+ setDescription(Messages.NDKPreferencePage_Preferences);
+ }
+
+ @Override
+ protected void createFieldEditors() {
+ mNdkDirectoryEditor = new NdkDirectoryFieldEditor(NdkManager.NDK_LOCATION,
+ Messages.NDKPreferencePage_Location, getFieldEditorParent());
+ addField(mNdkDirectoryEditor);
+ }
+
+ private static class NdkDirectoryFieldEditor extends DirectoryFieldEditor {
+ public NdkDirectoryFieldEditor(String name, String labelText, Composite parent) {
+ super(name, labelText, parent);
+ setEmptyStringAllowed(true);
+ }
+
+ @Override
+ protected boolean doCheckState() {
+ if (!super.doCheckState()) {
+ setErrorMessage(Messages.NDKPreferencePage_not_a_valid_directory);
+ return false;
+ }
+
+ String dirname = getTextControl().getText().trim();
+ if (!dirname.isEmpty() && !NdkManager.isValidNdkLocation(dirname)) {
+ setErrorMessage(Messages.NDKPreferencePage_not_a_valid_NDK_directory);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public Text getTextControl(Composite parent) {
+ setValidateStrategy(VALIDATE_ON_KEY_STROKE);
+ return super.getTextControl(parent);
+ }
+
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ // Nothing to do herea
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ if (mNdkDirectoryEditor != null) {
+ mNdkDirectoryEditor.dispose();
+ mNdkDirectoryEditor = null;
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SetFolders.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SetFolders.java
new file mode 100644
index 000000000..2e8f714bb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SetFolders.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.templates;
+
+import com.android.ide.eclipse.ndk.internal.Messages;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.core.model.IPathEntry;
+import org.eclipse.cdt.core.templateengine.TemplateCore;
+import org.eclipse.cdt.core.templateengine.process.ProcessArgument;
+import org.eclipse.cdt.core.templateengine.process.ProcessFailureException;
+import org.eclipse.cdt.core.templateengine.process.ProcessRunner;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SetFolders extends ProcessRunner {
+
+ @Override
+ public void process(TemplateCore template, ProcessArgument[] args, String processId,
+ IProgressMonitor monitor)
+ throws ProcessFailureException {
+ String projectName = null;
+ String[] sourceFolders = null;
+ String[] outputFolders = null;
+
+ for (ProcessArgument arg : args) {
+ String argName = arg.getName();
+ if (argName.equals("projectName")) { //$NON-NLS-1$
+ projectName = arg.getSimpleValue();
+ } else if (argName.equals("sourceFolders")) { //$NON-NLS-1$
+ sourceFolders = arg.getSimpleArrayValue();
+ } else if (argName.equals("outputFolders")) { //$NON-NLS-1$
+ outputFolders = arg.getSimpleArrayValue();
+ }
+ }
+
+ // Get the project
+ if (projectName == null)
+ throw new ProcessFailureException(Messages.SetFolders_Missing_project_name);
+
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if (!project.exists())
+ throw new ProcessFailureException(Messages.SetFolders_Project_does_not_exist);
+
+ // Create the folders
+ if (sourceFolders == null && outputFolders == null)
+ throw new ProcessFailureException(Messages.SetFolders_No_folders);
+
+ try {
+ // Add them in
+ ICProject cproject = CCorePlugin.getDefault().getCoreModel().create(project);
+ IPathEntry[] pathEntries = cproject.getRawPathEntries();
+ List<IPathEntry> newEntries = new ArrayList<IPathEntry>(pathEntries.length);
+ for (IPathEntry pathEntry : pathEntries) {
+ // remove the old source and output entries
+ if (pathEntry.getEntryKind() != IPathEntry.CDT_SOURCE
+ && pathEntry.getEntryKind() != IPathEntry.CDT_OUTPUT) {
+ newEntries.add(pathEntry);
+ }
+ }
+ if (sourceFolders != null)
+ for (String sourceFolder : sourceFolders) {
+ IFolder folder = project.getFolder(new Path(sourceFolder));
+ if (!folder.exists())
+ folder.create(true, true, monitor);
+ newEntries.add(CoreModel.newSourceEntry(folder.getFullPath()));
+ }
+ if (outputFolders != null)
+ for (String outputFolder : outputFolders) {
+ IFolder folder = project.getFolder(new Path(outputFolder));
+ if (!folder.exists())
+ folder.create(true, true, monitor);
+ newEntries.add(CoreModel.newOutputEntry(folder.getFullPath()));
+ }
+ cproject.setRawPathEntries(newEntries.toArray(new IPathEntry[newEntries.size()]),
+ monitor);
+ } catch (CoreException e) {
+ throw new ProcessFailureException(e);
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SimpleFile.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SimpleFile.java
new file mode 100644
index 000000000..7f249cacb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/SimpleFile.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.templates;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+import com.android.ide.eclipse.ndk.internal.Messages;
+
+import org.eclipse.cdt.core.templateengine.TemplateCore;
+import org.eclipse.cdt.core.templateengine.process.ProcessArgument;
+import org.eclipse.cdt.core.templateengine.process.ProcessFailureException;
+import org.eclipse.cdt.core.templateengine.process.ProcessRunner;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.osgi.framework.Bundle;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SimpleFile extends ProcessRunner {
+
+ private static final class FileOp {
+ public String source;
+ public String destination;
+ }
+
+ @Override
+ public void process(TemplateCore template, ProcessArgument[] args, String processId,
+ IProgressMonitor monitor)
+ throws ProcessFailureException {
+
+ // Fetch the args
+ String projectName = null;
+ List<FileOp> fileOps = new ArrayList<FileOp>();
+
+ for (ProcessArgument arg : args) {
+ if (arg.getName().equals("projectName")) //$NON-NLS-1$
+ projectName = arg.getSimpleValue();
+ else if (arg.getName().equals("files")) { //$NON-NLS-1$
+ ProcessArgument[][] files = arg.getComplexArrayValue();
+ for (ProcessArgument[] file : files) {
+ FileOp op = new FileOp();
+ for (ProcessArgument fileArg : file) {
+ if (fileArg.getName().equals("source")) //$NON-NLS-1$
+ op.source = fileArg.getSimpleValue();
+ else if (fileArg.getName().equals("destination")) //$NON-NLS-1$
+ op.destination = fileArg.getSimpleValue();
+ }
+ if (op.source == null || op.destination == null)
+ throw new ProcessFailureException(Messages.SimpleFile_Bad_file_operation);
+ fileOps.add(op);
+ }
+ }
+ }
+
+ if (projectName == null)
+ throw new ProcessFailureException(Messages.SimpleFile_No_project_name);
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if (!project.exists())
+ throw new ProcessFailureException(Messages.SimpleFile_Project_does_not_exist);
+
+ // Find bundle to find source files
+ Bundle bundle = Activator.getBundle(template.getTemplateInfo().getPluginId());
+ if (bundle == null)
+ throw new ProcessFailureException(Messages.SimpleFile_Bundle_not_found);
+
+ try {
+ for (FileOp op : fileOps) {
+ IFile destFile = project.getFile(new Path(op.destination));
+ if (destFile.exists())
+ // don't overwrite files if they exist already
+ continue;
+
+ // Make sure parent folders are created
+ mkDirs(project, destFile.getParent(), monitor);
+
+ URL sourceURL = FileLocator.find(bundle, new Path(op.source), null);
+ if (sourceURL == null)
+ throw new ProcessFailureException(Messages.SimpleFile_Could_not_fine_source
+ + op.source);
+
+ TemplatedInputStream in = new TemplatedInputStream(sourceURL.openStream(),
+ template.getValueStore());
+ destFile.create(in, true, monitor);
+ in.close();
+ }
+ } catch (IOException e) {
+ throw new ProcessFailureException(e);
+ } catch (CoreException e) {
+ throw new ProcessFailureException(e);
+ }
+
+ }
+
+ private void mkDirs(IProject project, IContainer container, IProgressMonitor monitor)
+ throws CoreException {
+ if (container.exists())
+ return;
+ mkDirs(project, container.getParent(), monitor);
+ ((IFolder) container).create(true, true, monitor);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/TemplatedInputStream.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/TemplatedInputStream.java
new file mode 100644
index 000000000..129caa327
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/templates/TemplatedInputStream.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.templates;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Reads from a template substituting marked values from the supplied Map.
+ */
+public class TemplatedInputStream extends InputStream {
+
+ private final InputStream mIn;
+ private final Map<String, String> mMap;
+ private char[] mSub;
+ private int mPos;
+ private int mMark;
+
+ public TemplatedInputStream(InputStream in, Map<String, String> map) {
+ this.mIn = in;
+ this.mMap = map;
+ }
+
+ @Override
+ public int read() throws IOException {
+ // if from a mark, return the char
+ if (mMark != 0) {
+ int c = mMark;
+ mMark = 0;
+ return c;
+ }
+
+ // return char from sub layer if available
+ if (mSub != null) {
+ char c = mSub[mPos++];
+ if (mPos >= mSub.length)
+ mSub = null;
+ return c;
+ }
+
+ int c = mIn.read();
+ if (c == '%') {
+ // check if it's a sub
+ c = mIn.read();
+ if (c == '{') {
+ // it's a sub
+ StringBuffer buff = new StringBuffer();
+ for (c = mIn.read(); c != '}' && c >= 0; c = mIn.read())
+ buff.append((char) c);
+ String str = mMap.get(buff.toString());
+ if (str != null) {
+ mSub = str.toCharArray();
+ mPos = 0;
+ }
+ return read(); // recurse to get the real char
+ } else {
+ // not a sub
+ mMark = c;
+ return '%';
+ }
+ }
+
+ return c;
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ mIn.close();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizard.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizard.java
new file mode 100644
index 000000000..b3675ed27
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizard.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.wizards;
+
+import com.android.ide.eclipse.ndk.internal.Activator;
+import com.android.ide.eclipse.ndk.internal.NdkManager;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.make.core.MakeCorePlugin;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.WorkbenchException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AddNativeWizard extends Wizard {
+
+ private final IProject mProject;
+ private final IWorkbenchWindow mWindow;
+
+ private AddNativeWizardPage mAddNativeWizardPage;
+ private Map<String, String> mTemplateArgs = new HashMap<String, String>();
+
+ public AddNativeWizard(IProject project, IWorkbenchWindow window) {
+ mProject = project;
+ mWindow = window;
+ mTemplateArgs.put(NdkManager.LIBRARY_NAME, project.getName());
+ }
+
+ @Override
+ public void addPages() {
+ mAddNativeWizardPage = new AddNativeWizardPage(mTemplateArgs);
+ addPage(mAddNativeWizardPage);
+ }
+
+ @Override
+ public boolean performFinish() {
+ // Switch to C/C++ Perspective
+ try {
+ mWindow.getWorkbench().showPerspective(CUIPlugin.ID_CPERSPECTIVE, mWindow);
+ } catch (WorkbenchException e1) {
+ Activator.log(e1);
+ }
+
+ mAddNativeWizardPage.updateArgs(mTemplateArgs);
+
+ IRunnableWithProgress op = new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ IWorkspaceRunnable op1 = new IWorkspaceRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor1) throws CoreException {
+ // Convert to CDT project
+ CCorePlugin.getDefault().convertProjectToCC(mProject, monitor1,
+ MakeCorePlugin.MAKE_PROJECT_ID);
+ // Set up build information
+ new NdkWizardHandler().convertProject(mProject, monitor1);
+
+ // When using CDT 8.1.x, disable the language settings provider mechanism
+ // for scanner discovery. Use the classloader to load the class since it
+ // will not be available pre 8.1.
+ try {
+ @SuppressWarnings("rawtypes")
+ Class c = getClass().getClassLoader().loadClass(
+ "org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport"); //$NON-NLS-1$
+
+ @SuppressWarnings("unchecked")
+ Method m = c.getMethod(
+ "setLanguageSettingsProvidersFunctionalityEnabled", //$NON-NLS-1$
+ IProject.class, boolean.class);
+
+ m.invoke(null, mProject, false);
+ } catch (Exception e) {
+ // ignore all exceptions: On pre 8.1.x CDT, this class will not be
+ // found, but this options only needs to be set in 8.1.x
+ }
+
+ // Run the template
+ NdkManager.addNativeSupport(mProject, mTemplateArgs, monitor1);
+ }
+ };
+ // TODO run from a job
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ try {
+ workspace.run(op1, workspace.getRoot(), 0, new NullProgressMonitor());
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ };
+ try {
+ getContainer().run(false, true, op);
+ return true;
+ } catch (InterruptedException e) {
+ Activator.log(e);
+ return false;
+ } catch (InvocationTargetException e) {
+ Activator.log(e);
+ return false;
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizardPage.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizardPage.java
new file mode 100644
index 000000000..65af270b0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/AddNativeWizardPage.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.wizards;
+
+import com.android.ide.eclipse.ndk.internal.Messages;
+import com.android.ide.eclipse.ndk.internal.NdkManager;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.Map;
+
+public class AddNativeWizardPage extends WizardPage {
+
+ private final String defaultLibraryName;
+
+ private Text libraryNameText;
+
+ public AddNativeWizardPage(Map<String, String> templateArgs) {
+ super("addNativeWizardPage"); //$NON-NLS-1$
+ setDescription(Messages.AddNativeWizardPage_Description);
+ setTitle(Messages.AddNativeWizardPage_Title);
+
+ defaultLibraryName = templateArgs.get(NdkManager.LIBRARY_NAME);
+ if (!NdkManager.isNdkLocationValid()) {
+ setErrorMessage(Messages.AddNativeWizardPage_Location_not_valid);
+ }
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ return NdkManager.isNdkLocationValid();
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ Composite container = new Composite(parent, SWT.NULL);
+ setControl(container);
+ container.setLayout(new GridLayout(2, false));
+
+ Label lblLibraryName = new Label(container, SWT.NONE);
+ lblLibraryName.setText(Messages.AddNativeWizardPage_LibraryName);
+
+ Composite composite = new Composite(container, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ composite.setLayout(new GridLayout(3, false));
+
+ Label lblLib = new Label(composite, SWT.NONE);
+ lblLib.setText("lib"); //$NON-NLS-1$
+
+ libraryNameText = new Text(composite, SWT.BORDER);
+ libraryNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ libraryNameText.setText(defaultLibraryName);
+
+ Label lblso = new Label(composite, SWT.NONE);
+ lblso.setText(".so"); //$NON-NLS-1$
+ }
+
+ public void updateArgs(Map<String, String> templateArgs) {
+ templateArgs.put(NdkManager.LIBRARY_NAME, libraryNameText.getText());
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/NdkWizardHandler.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/NdkWizardHandler.java
new file mode 100644
index 000000000..fa0b92bb0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/wizards/NdkWizardHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 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.ndk.internal.wizards;
+
+import org.eclipse.cdt.managedbuilder.core.IToolChain;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
+import org.eclipse.cdt.managedbuilder.ui.wizards.STDWizardHandler;
+
+public class NdkWizardHandler extends STDWizardHandler {
+
+ public NdkWizardHandler() {
+ super(null, null);
+ }
+
+ @Override
+ public IToolChain[] getSelectedToolChains() {
+ IToolChain[] tcs = ManagedBuildManager.getRealToolChains();
+ for (IToolChain tc : tcs) {
+ if (tc.getId().equals("com.android.toolchain.gcc")) //$NON-NLS-1$
+ return new IToolChain[] {
+ tc
+ };
+ }
+ return super.getSelectedToolChains();
+ }
+
+}