aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.ndk
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ndk')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/.classpath10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/.project28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/.settings/org.eclipse.jdt.core.prefs98
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/META-INF/MANIFEST.MF28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/MODULE_LICENSE_EPL0
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/NOTICE224
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/about.html106
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/about.ini2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.ndk/about.properties7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/build.properties12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.c0
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.cpp0
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/icons/android-32.pngbin0 -> 2446 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/icons/android_app.pngbin0 -> 454 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/icons/obj16/c_app.gifbin0 -> 606 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/debugger_tab.gifbin0 -> 348 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/startup_tab.gifbin0 -> 527 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/plugin.xml208
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/pom.xml17
-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
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/templates/addNdkSupport.xml33
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/main.cpp1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/tAndroid.mk8
52 files changed, 4219 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath b/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath
new file mode 100644
index 000000000..36a1bdaec
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/sdklib"/>
+</classpath>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/.project b/eclipse/plugins/com.android.ide.eclipse.ndk/.project
new file mode 100644
index 000000000..416aa2edc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>plugin-ndk</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/.settings/org.eclipse.jdt.core.prefs b/eclipse/plugins/com.android.ide.eclipse.ndk/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..ea661960a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,98 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=enabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.ndk/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..df1e88c78
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/META-INF/MANIFEST.MF
@@ -0,0 +1,28 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: ADT CDT Integration
+Bundle-SymbolicName: com.android.ide.eclipse.ndk;singleton:=true
+Bundle-Version: 24.3.3.qualifier
+Bundle-Activator: com.android.ide.eclipse.ndk.internal.Activator
+Bundle-Vendor: The Android Open Source Project
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.core.variables,
+ org.eclipse.ui,
+ org.eclipse.debug.core,
+ org.eclipse.debug.ui,
+ org.eclipse.cdt.core,
+ org.eclipse.cdt.ui,
+ org.eclipse.cdt.managedbuilder.core,
+ org.eclipse.cdt.managedbuilder.ui,
+ org.eclipse.cdt.debug.core,
+ org.eclipse.cdt.debug.ui,
+ org.eclipse.cdt.dsf,
+ org.eclipse.cdt.dsf.gdb,
+ org.eclipse.cdt.dsf.gdb.ui,
+ org.eclipse.cdt.launch,
+ com.android.ide.eclipse.adt,
+ com.android.ide.eclipse.base,
+ com.android.ide.eclipse.ddms
+Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: .
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/MODULE_LICENSE_EPL b/eclipse/plugins/com.android.ide.eclipse.ndk/MODULE_LICENSE_EPL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/MODULE_LICENSE_EPL
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/NOTICE b/eclipse/plugins/com.android.ide.eclipse.ndk/NOTICE
new file mode 100644
index 000000000..49c101df7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/NOTICE
@@ -0,0 +1,224 @@
+*Eclipse Public License - v 1.0*
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+*1. DEFINITIONS*
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and
+are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program by such
+Contributor itself or anyone acting on such Contributor's behalf.
+Contributions do not include additions to the Program which: (i) are
+separate modules of software distributed in conjunction with the Program
+under their own license agreement, and (ii) are not derivative works of
+the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+*2. GRANT OF RIGHTS*
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free copyright
+license to reproduce, prepare derivative works of, publicly display,
+publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and
+object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free patent license
+under Licensed Patents to make, use, sell, offer to sell, import and
+otherwise transfer the Contribution of such Contributor, if any, in
+source code and object code form. This patent license shall apply to the
+combination of the Contribution and the Program if, at the time the
+Contribution is added by the Contributor, such addition of the
+Contribution causes such combination to be covered by the Licensed
+Patents. The patent license shall not apply to any other combinations
+which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+licenses to its Contributions set forth herein, no assurances are
+provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright
+license set forth in this Agreement.
+
+*3. REQUIREMENTS*
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+and conditions, express and implied, including warranties or conditions
+of title and non-infringement, and implied warranties or conditions of
+merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and
+consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are
+offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such
+Contributor, and informs licensees how to obtain it in a reasonable
+manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+*4. COMMERCIAL DISTRIBUTION*
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program, the
+Contributor who includes the Program in a commercial product offering
+should do so in a manner which does not create potential liability for
+other Contributors. Therefore, if a Contributor includes the Program in
+a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the
+Program in a commercial product offering. The obligations in this
+section do not apply to any claims or Losses relating to any actual or
+alleged intellectual property infringement. In order to qualify, an
+Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial
+Contributor to control, and cooperate with the Commercial Contributor
+in, the defense and any related settlement negotiations. The Indemnified
+Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+*5. NO WARRANTY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
+ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
+OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
+A PARTICULAR PURPOSE. Each Recipient is solely responsible for
+determining the appropriateness of using and distributing the Program
+and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program
+errors, compliance with applicable laws, damage to or loss of data,
+programs or equipment, and unavailability or interruption of operations.
+
+*6. DISCLAIMER OF LIABILITY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. GENERAL*
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including
+a cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails
+to comply with any of the material terms or conditions of this Agreement
+and does not cure such failure in a reasonable period of time after
+becoming aware of such noncompliance. If all Recipient's rights under
+this Agreement terminate, Recipient agrees to cease use and distribution
+of the Program as soon as reasonably practicable. However, Recipient's
+obligations under this Agreement and any licenses granted by Recipient
+relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and may
+only be modified in the following manner. The Agreement Steward reserves
+the right to publish new versions (including revisions) of this
+Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the
+initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated in
+Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
+to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in
+the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to
+this Agreement will bring a legal action under this Agreement more than
+one year after the cause of action arose. Each party waives its rights
+to a jury trial in any resulting litigation.
+
+
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/about.html b/eclipse/plugins/com.android.ide.eclipse.ndk/about.html
new file mode 100644
index 000000000..bc621cb3e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/about.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>September 19, 2010</p>
+<h3>License</h3>
+
+<pre>
+ Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&apos;S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+&quot;Contribution&quot; means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution &apos;originates&apos; from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor&apos;s behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+&quot;Contributor&quot; means any person or entity that distributes the Program.
+
+&quot;Licensed Patents&quot; mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+&quot;Program&quot; means the Contributions distributed in accordance with this Agreement.
+
+&quot;Recipient&quot; means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient&apos;s responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (&quot;Commercial Contributor&quot;) hereby agrees to defend and indemnify every other Contributor (&quot;Indemnified Contributor&quot;) against any losses, damages and costs (collectively &quot;Losses&quot;) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor&apos;s responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient&apos;s patent(s), then such Recipient&apos;s rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient&apos;s rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient&apos;s rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient&apos;s obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
+
+</pre>
+
+</body>
+</html>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/about.ini b/eclipse/plugins/com.android.ide.eclipse.ndk/about.ini
new file mode 100644
index 000000000..2d7cdaad6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/about.ini
@@ -0,0 +1,2 @@
+aboutText=%blurb
+featureImage=icons/android-32.png \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/about.properties b/eclipse/plugins/com.android.ide.eclipse.ndk/about.properties
new file mode 100755
index 000000000..34218e997
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/about.properties
@@ -0,0 +1,7 @@
+blurb=Android Development Toolkit\n\
+\n\
+Version\: {featureVersion}\n\
+\n\
+(c) Copyright 2007-2011 The Android Open Source Project. All rights reserved.\n\
+Visit http://developer.android.com/sdk/eclipse-adt.html
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/build.properties b/eclipse/plugins/com.android.ide.eclipse.ndk/build.properties
new file mode 100644
index 000000000..4842ac1c7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/build.properties
@@ -0,0 +1,12 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/,\
+ templates/,\
+ discovery/,\
+ NOTICE,\
+ about.html,\
+ about.ini,\
+ about.properties
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.c b/eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.c
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.cpp b/eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/discovery/test.cpp
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/icons/android-32.png b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/android-32.png
new file mode 100644
index 000000000..4e0cc138d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/android-32.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/icons/android_app.png b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/android_app.png
new file mode 100644
index 000000000..8ca377009
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/android_app.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/icons/obj16/c_app.gif b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/obj16/c_app.gif
new file mode 100644
index 000000000..504ef509f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/obj16/c_app.gif
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/debugger_tab.gif b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/debugger_tab.gif
new file mode 100644
index 000000000..d90a29fea
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/debugger_tab.gif
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/startup_tab.gif b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/startup_tab.gif
new file mode 100644
index 000000000..7b3a92e09
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/icons/view16/startup_tab.gif
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.ndk/plugin.xml
new file mode 100644
index 000000000..93e9bc440
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/plugin.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.cdt.managedbuilder.core.buildDefinitions">
+ <managedBuildRevision
+ fileVersion="4.0.0">
+ </managedBuildRevision>
+ <toolChain
+ configurationEnvironmentSupplier="com.android.ide.eclipse.ndk.internal.build.NdkEnvSupplier"
+ id="com.android.toolchain.gcc"
+ isAbstract="false"
+ name="Android GCC">
+ <targetPlatform
+ binaryParser="org.eclipse.cdt.core.ELF"
+ id="com.android.targetPlatform"
+ isAbstract="false">
+ </targetPlatform>
+ <builder
+ cleanBuildTarget="clean"
+ command="ndk-build"
+ commandLauncher="com.android.ide.eclipse.ndk.internal.build.NdkCommandLauncher"
+ id="com.android.builder"
+ isAbstract="false"
+ isVariableCaseSensitive="false"
+ name="Android Builder">
+ </builder>
+ <tool
+ id="com.android.gcc.compiler"
+ isAbstract="false"
+ name="Android GCC Compiler"
+ natureFilter="both">
+ <option
+ browseType="directory"
+ id="com.android.gcc.option.includePath"
+ isAbstract="false"
+ resourceFilter="all"
+ valueType="includePath">
+ </option>
+ <inputType
+ id="com.android.gcc.inputType"
+ scannerConfigDiscoveryProfileId="com.android.AndroidPerProjectProfile"
+ sources="c,cpp">
+ </inputType>
+ </tool>
+ </toolChain>
+ </extension>
+ <extension
+ id="com.android.AndroidPerProjectProfile"
+ name="Android Per Project Profile"
+ point="org.eclipse.cdt.make.core.ScannerConfigurationDiscoveryProfile">
+ <scannerInfoCollector
+ class="com.android.ide.eclipse.ndk.internal.discovery.NdkScannerInfoCollector"
+ scope="project">
+ </scannerInfoCollector>
+ </extension>
+ <extension
+ point="org.eclipse.cdt.core.templateProcessTypes">
+ <processType
+ name="SetFolders"
+ processRunner="com.android.ide.eclipse.ndk.internal.templates.SetFolders">
+ <simple
+ name="projectName">
+ </simple>
+ <simpleArray
+ name="sourceFolders">
+ </simpleArray>
+ <simpleArray
+ name="outputFolders">
+ </simpleArray>
+ </processType>
+ <processType
+ name="SimpleFile"
+ processRunner="com.android.ide.eclipse.ndk.internal.templates.SimpleFile">
+ <simple
+ name="projectName">
+ </simple>
+ <complexArray
+ name="files">
+ <baseType>
+ <simple
+ name="source">
+ </simple>
+ <simple
+ name="destination">
+ </simple></baseType>
+ </complexArray>
+ </processType>
+ </extension>
+ <extension
+ point="org.eclipse.cdt.core.templates">
+ <template
+ id="com.android.ide.eclipse.ndk.addNdkSupport"
+ location="templates/addNdkSupport.xml"
+ projectType="none">
+ </template>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.android.ide.eclipse.preferences.main"
+ class="com.android.ide.eclipse.ndk.internal.preferences.NdkPreferencePage"
+ id="org.eclipse.cdt.android.page"
+ name="NDK">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ adaptable="true"
+ id="com.android.ide.eclipse.ndk.projectContribution"
+ objectClass="org.eclipse.core.resources.IProject">
+ <visibility>
+ <and>
+ <objectState
+ name="projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </objectState>
+ <not>
+ <objectState
+ name="projectNature"
+ value="org.eclipse.cdt.core.cnature">
+ </objectState>
+ </not>
+ </and>
+ </visibility>
+ <action
+ class="com.android.ide.eclipse.ndk.internal.actions.AddNativeAction"
+ enablesFor="1"
+ id="org.eclipse.cdt.android.action1"
+ label="Add Native Support..."
+ menubarPath="com.android.ide.eclipse.adt.AndroidTools/ndk">
+ </action>
+ </objectContribution>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.preferences">
+ <initializer
+ class="com.android.ide.eclipse.ndk.internal.preferences.NdkPreferenceInitializer">
+ </initializer>
+ </extension>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ delegate="com.android.ide.eclipse.ndk.internal.launch.NdkGdbLaunchDelegate"
+ delegateName="Android ndk-gdb Launcher"
+ id="com.android.ide.eclipse.ndk.debug.LaunchConfigType"
+ modes="debug"
+ name="Android Native Application"
+ public="true"
+ sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
+ sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="com.android.ide.eclipse.ndk.debug.LaunchConfigType"
+ icon="icons/android_app.png"
+ id="com.android.ide.eclipse.ndk.debug.LaunchConfigTypeImage">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="com.android.ide.eclipse.ndk.internal.launch.NdkGdbLaunchConfigTabGroups"
+ description="Android Native Application"
+ id="com.android.ide.eclipse.ndk.debug.LaunchConfigTabGroup"
+ type="com.android.ide.eclipse.ndk.debug.LaunchConfigType">
+ </launchConfigurationTabGroup>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchShortcuts">
+ <shortcut
+ class="com.android.ide.eclipse.ndk.internal.launch.NdkGdbLaunchShortcut"
+ id="com.android.ide.eclipse.ndk.debug.LaunchConfigShortcut"
+ label="Android Native Application"
+ modes="debug">
+ <contextualLaunch>
+ <enablement>
+ <with variable="selection">
+ <count value="1" />
+ <iterate>
+ <and>
+ <test property="org.eclipse.jdt.launching.isContainer" />
+ <test
+ property="org.eclipse.jdt.launching.hasProjectNature"
+ args="com.android.ide.eclipse.adt.AndroidNature" />
+ </and>
+ </iterate>
+ </with>
+ </enablement>
+ </contextualLaunch>
+ <perspective id="org.eclipse.jdt.ui.JavaPerspective" />
+ <perspective id="org.eclipse.debug.ui.DebugPerspective" />
+ <perspective
+ id="org.eclipse.cdt.ui.CPerspective">
+ </perspective>
+ <configurationType id="com.android.ide.eclipse.ndk.debug.LaunchConfigType">
+ </configurationType>
+ <description
+ description="Debug as Android Native Application"
+ mode="debug">
+ </description>
+ </shortcut>
+ </extension>
+
+</plugin>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/pom.xml b/eclipse/plugins/com.android.ide.eclipse.ndk/pom.xml
new file mode 100644
index 000000000..53d9c47a0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/pom.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <version>24.3.3-SNAPSHOT</version>
+ <artifactId>com.android.ide.eclipse.ndk</artifactId>
+ <packaging>eclipse-plugin</packaging>
+ <name>ndk</name>
+
+ <parent>
+ <relativePath>../../pom.xml</relativePath>
+ <groupId>adt.group</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+</project>
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();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/templates/addNdkSupport.xml b/eclipse/plugins/com.android.ide.eclipse.ndk/templates/addNdkSupport.xml
new file mode 100644
index 000000000..40c795460
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/templates/addNdkSupport.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<template
+ type="ProjTempl"
+ version="1.0"
+ revision="1.0"
+ id="AddNdkSupport"
+ label="Add Android NDK Support"
+ description="Adds NDK support to Android Java projects"
+ help="help.html">
+ <process type="com.android.ide.eclipse.ndk.SetFolders">
+ <simple name="projectName" value="$(projectName)"/>
+ <simple-array name="sourceFolders">
+ <element value="jni"/>
+ </simple-array>
+ <simple-array name="outputFolders">
+ <element value="obj"/>
+ <element value="libs"/>
+ </simple-array>
+ </process>
+ <process type="com.android.ide.eclipse.ndk.SimpleFile">
+ <simple name="projectName" value="$(projectName)"/>
+ <complex-array name="files">
+ <element>
+ <simple name="source" value="templates/resources/tAndroid.mk"/>
+ <simple name="destination" value="jni/Android.mk"/>
+ </element>
+ <element>
+ <simple name="source" value="templates/resources/main.cpp"/>
+ <simple name="destination" value="jni/$(libraryName).cpp"/>
+ </element>
+ </complex-array>
+ </process>
+</template>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/main.cpp b/eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/main.cpp
new file mode 100644
index 000000000..6434c6677
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/main.cpp
@@ -0,0 +1 @@
+#include <jni.h>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/tAndroid.mk b/eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/tAndroid.mk
new file mode 100644
index 000000000..cd9d479db
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/templates/resources/tAndroid.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := %{libraryName}
+LOCAL_SRC_FILES := %{libraryName}.cpp
+
+include $(BUILD_SHARED_LIBRARY)