1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.ndk.internal;
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.ndk.internal.launch.NdkLaunchConstants;
21 
22 import org.eclipse.cdt.core.CommandLauncher;
23 import org.eclipse.cdt.core.ICommandLauncher;
24 import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
25 import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
26 import org.eclipse.core.resources.IProject;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IPath;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.Path;
31 import org.eclipse.core.runtime.Platform;
32 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
33 
34 import java.io.ByteArrayOutputStream;
35 import java.io.File;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.EnumSet;
40 import java.util.List;
41 import java.util.Set;
42 
43 @SuppressWarnings("restriction")
44 public class NdkHelper {
45     private static final String MAKE = "make";                                      //$NON-NLS-1$
46     private static final String CORE_MAKEFILE_PATH = "/build/core/build-local.mk";  //$NON-NLS-1$
47 
48     /**
49      * Obtain the ABI's the application is compatible with.
50      * The ABI's are obtained by reading the result of the following command:
51      * make --no-print-dir -f ${NdkRoot}/build/core/build-local.mk -C <project-root> DUMP_APP_ABI
52      */
getApplicationAbis(IProject project, IProgressMonitor monitor)53     public static Collection<NativeAbi> getApplicationAbis(IProject project,
54                                                                 IProgressMonitor monitor) {
55         ICommandLauncher launcher = new CommandLauncher();
56         launcher.setProject(project);
57         String[] args = new String[] {
58             "--no-print-dir",                                           //$NON-NLS-1$
59             "-f",                                                       //$NON-NLS-1$
60             NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH,
61             "-C",                                                       //$NON-NLS-1$
62             project.getLocation().toOSString(),
63             "DUMP_APP_ABI",                                             //$NON-NLS-1$
64         };
65         try {
66             launcher.execute(getPathToMake(), args, null, project.getLocation(), monitor);
67         } catch (CoreException e) {
68             AdtPlugin.printErrorToConsole(e.getLocalizedMessage());
69             return Collections.emptyList();
70         }
71 
72         ByteArrayOutputStream stdout = new ByteArrayOutputStream();
73         ByteArrayOutputStream stderr = new ByteArrayOutputStream();
74         launcher.waitAndRead(stdout, stderr, monitor);
75 
76         String abis = stdout.toString().trim();
77         Set<NativeAbi> nativeAbis = EnumSet.noneOf(NativeAbi.class);
78         for (String abi: abis.split(" ")) {                             //$NON-NLS-1$
79             if (abi.equals("all")) {                                    //$NON-NLS-1$
80                 return EnumSet.allOf(NativeAbi.class);
81             }
82 
83             try {
84                 nativeAbis.add(NativeAbi.getByString(abi));
85             } catch (IllegalArgumentException e) {
86                 AdtPlugin.printErrorToConsole(project, "Unknown Application ABI: ", abi);
87             }
88         }
89 
90         return nativeAbis;
91     }
92 
93     /**
94      * Obtain the toolchain prefix to use for given project and abi.
95      * The prefix is obtained by reading the result of:
96      * make --no-print-dir -f ${NdkRoot}/build/core/build-local.mk \
97      *      -C <project-root> \
98      *      DUMP_TOOLCHAIN_PREFIX APP_ABI=abi
99      */
getToolchainPrefix(IProject project, NativeAbi abi, IProgressMonitor monitor)100     public static String getToolchainPrefix(IProject project, NativeAbi abi,
101             IProgressMonitor monitor) {
102         ICommandLauncher launcher = new CommandLauncher();
103         launcher.setProject(project);
104         String[] args = new String[] {
105             "--no-print-dir",                                           //$NON-NLS-1$
106             "-f",                                                       //$NON-NLS-1$
107             NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH,
108             "-C",                                                       //$NON-NLS-1$
109             project.getLocation().toOSString(),
110             "DUMP_TOOLCHAIN_PREFIX",                                    //$NON-NLS-1$
111             "APP_ABI=" + abi.getAbi(),                                  //$NON-NLS-1$
112         };
113         try {
114             launcher.execute(getPathToMake(), args, null, project.getLocation(), monitor);
115         } catch (CoreException e) {
116             AdtPlugin.printErrorToConsole(e.getLocalizedMessage());
117             return null;
118         }
119 
120         ByteArrayOutputStream stdout = new ByteArrayOutputStream();
121         ByteArrayOutputStream stderr = new ByteArrayOutputStream();
122         launcher.waitAndRead(stdout, stderr, monitor);
123         return stdout.toString().trim();
124     }
125 
getPathToMake()126     private static IPath getPathToMake() {
127         return getFullPathTo(MAKE);
128     }
129 
130     /**
131      * Obtain a path to the utilities prebuilt folder in NDK. This is typically
132      * "${NdkRoot}/prebuilt/<platform>/bin/". If the executable is not found, it simply returns
133      * the name of the executable (which is equal to assuming that it is available on the path).
134      */
getFullPathTo(String executable)135     private static synchronized IPath getFullPathTo(String executable) {
136         if (Platform.getOS().equals(Platform.OS_WIN32)) {
137             executable += ".exe";
138         }
139 
140         IPath ndkRoot = new Path(NdkManager.getNdkLocation());
141         IPath prebuilt = ndkRoot.append("prebuilt");                      //$NON-NLS-1$
142         if (!prebuilt.toFile().exists() || !prebuilt.toFile().canRead()) {
143             return new Path(executable);
144         }
145 
146         File[] platforms = prebuilt.toFile().listFiles();
147         if (platforms != null) {
148             for (File p: platforms) {
149                 IPath exePath = prebuilt.append(p.getName())
150                         .append("bin")          //$NON-NLS-1$
151                         .append(executable);
152                 if (exePath.toFile().exists()) {
153                     return exePath;
154                 }
155             }
156         }
157 
158         return new Path(executable);
159     }
160 
setLaunchConfigDefaults(ILaunchConfigurationWorkingCopy config)161     public static void setLaunchConfigDefaults(ILaunchConfigurationWorkingCopy config) {
162         config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, true);
163         config.setAttribute(NdkLaunchConstants.ATTR_NDK_GDB, NdkLaunchConstants.DEFAULT_GDB);
164         config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT,
165                 NdkLaunchConstants.DEFAULT_GDBINIT);
166         config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT,
167                 NdkLaunchConstants.DEFAULT_GDB_PORT);
168         config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, "localhost"); //$NON-NLS-1$
169         config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false);
170         config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
171                 IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH);
172         config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
173                 NdkLaunchConstants.DEFAULT_PROGRAM);
174 
175         config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
176                 IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE);
177         config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID,
178                 "gdbserver"); //$NON-NLS-1$
179 
180         List<String> solibPaths = new ArrayList<String>(2);
181         solibPaths.add(NdkLaunchConstants.DEFAULT_SOLIB_PATH);
182         config.setAttribute(NdkLaunchConstants.ATTR_NDK_SOLIB, solibPaths);
183     }
184 }
185