1 /* 2 * Copyright (C) 2011 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.discovery; 18 19 import com.android.ide.eclipse.ndk.internal.Activator; 20 import com.android.ide.eclipse.ndk.internal.build.NdkCommandLauncher; 21 22 import org.eclipse.cdt.core.CCorePlugin; 23 import org.eclipse.cdt.core.envvar.IEnvironmentVariable; 24 import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager; 25 import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; 26 import org.eclipse.cdt.managedbuilder.core.IBuilder; 27 import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo; 28 import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; 29 import org.eclipse.core.resources.IProject; 30 import org.eclipse.core.runtime.CoreException; 31 import org.eclipse.core.runtime.FileLocator; 32 import org.eclipse.core.runtime.IPath; 33 import org.eclipse.core.runtime.IProgressMonitor; 34 import org.eclipse.core.runtime.Path; 35 36 import java.io.BufferedReader; 37 import java.io.File; 38 import java.io.IOException; 39 import java.io.InputStream; 40 import java.io.InputStreamReader; 41 import java.net.URISyntaxException; 42 import java.net.URL; 43 import java.util.ArrayList; 44 import java.util.HashMap; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.Map.Entry; 48 49 public class NdkDiscoveryUpdater { 50 private final NdkDiscoveredPathInfo mPathInfo; 51 private final IProject mProject; 52 53 private boolean mCPlusPlus = false; 54 private String mCommand; 55 private List<String> mArguments = new ArrayList<String>(); 56 57 public NdkDiscoveryUpdater(NdkDiscoveredPathInfo pathInfo) { 58 mPathInfo = pathInfo; 59 mProject = pathInfo.getProject(); 60 } 61 62 public void runUpdate(IProgressMonitor monitor) throws CoreException { 63 try { 64 // Run ndk-build -nB to get the list of commands 65 IPath commandPath = new Path("ndk-build"); //$NON-NLS-1$ 66 String[] args = { 67 "-nB"}; //$NON-NLS-1$ 68 String[] env = calcEnvironment(); 69 File projectDir = new File(mProject.getLocationURI()); 70 IPath changeToDirectory = new Path(projectDir.getAbsolutePath()); 71 Process proc = new NdkCommandLauncher().execute(commandPath, args, env, 72 changeToDirectory, monitor); 73 if (proc == null) 74 // proc failed to start 75 return; 76 BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); 77 String line = reader.readLine(); 78 while (line != null) { 79 checkBuildLine(line); 80 line = reader.readLine(); 81 } 82 83 if (mCommand == null) { 84 return; 85 } 86 87 // Run the unique commands with special gcc options to extract the 88 // symbols and paths 89 // -E -P -v -dD 90 mArguments.add("-E"); //$NON-NLS-1$ 91 mArguments.add("-P"); //$NON-NLS-1$ 92 mArguments.add("-v"); //$NON-NLS-1$ 93 mArguments.add("-dD"); //$NON-NLS-1$ 94 95 URL url = Activator.findFile(new Path( 96 "discovery/" + (mCPlusPlus ? "test.cpp" : "test.c"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 97 File testFile = new File(FileLocator.toFileURL(url).toURI()); 98 String testFileName = testFile.getAbsolutePath().replace('\\', '/'); 99 mArguments.add(testFileName); 100 101 args = mArguments.toArray(new String[mArguments.size()]); 102 proc = new NdkCommandLauncher().execute(new Path(mCommand), args, env, 103 changeToDirectory, monitor); 104 // Error stream has the includes 105 final InputStream errStream = proc.getErrorStream(); 106 new Thread() { 107 @Override 108 public void run() { 109 checkIncludes(errStream); 110 }; 111 }.start(); 112 113 // Input stream has the defines 114 checkDefines(proc.getInputStream()); 115 } catch (IOException e) { 116 throw new CoreException(Activator.newStatus(e)); 117 } catch (URISyntaxException e) { 118 throw new CoreException(Activator.newStatus(e)); 119 } 120 } 121 122 private String[] calcEnvironment() throws CoreException { 123 IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(mProject); 124 IBuilder builder = info.getDefaultConfiguration().getBuilder(); 125 HashMap<String, String> envMap = new HashMap<String, String>(); 126 if (builder.appendEnvironment()) { 127 ICConfigurationDescription cfgDes = ManagedBuildManager 128 .getDescriptionForConfiguration(builder.getParent().getParent()); 129 IEnvironmentVariableManager mngr = CCorePlugin.getDefault() 130 .getBuildEnvironmentManager(); 131 IEnvironmentVariable[] vars = mngr.getVariables(cfgDes, true); 132 for (IEnvironmentVariable var : vars) { 133 envMap.put(var.getName(), var.getValue()); 134 } 135 } 136 // Add variables from build info 137 Map<String, String> builderEnv = builder.getExpandedEnvironment(); 138 if (builderEnv != null) 139 envMap.putAll(builderEnv); 140 List<String> strings = new ArrayList<String>(envMap.size()); 141 for (Entry<String, String> entry : envMap.entrySet()) { 142 StringBuffer buffer = new StringBuffer(entry.getKey()); 143 buffer.append('=').append(entry.getValue()); 144 strings.add(buffer.toString()); 145 } 146 return strings.toArray(new String[strings.size()]); 147 } 148 149 private static class Line { 150 private final String line; 151 private int pos; 152 153 public Line(String line) { 154 this.line = line; 155 } 156 157 public Line(String line, int pos) { 158 this(line); 159 this.pos = pos; 160 } 161 162 public String getToken() { 163 skipWhiteSpace(); 164 if (pos == line.length()) 165 return null; 166 167 int start = pos; 168 boolean inQuote = false; 169 170 while (true) { 171 char c = line.charAt(pos); 172 if (c == ' ') { 173 if (!inQuote) 174 return line.substring(start, pos); 175 } else if (c == '"') { 176 inQuote = !inQuote; 177 } 178 179 if (++pos == line.length()) 180 return null; 181 } 182 183 } 184 185 private String getRemaining() { 186 if (pos == line.length()) 187 return null; 188 189 skipWhiteSpace(); 190 String rc = line.substring(pos); 191 pos = line.length(); 192 return rc; 193 } 194 195 private void skipWhiteSpace() { 196 while (true) { 197 if (pos == line.length()) 198 return; 199 char c = line.charAt(pos); 200 if (c == ' ') 201 pos++; 202 else 203 return; 204 } 205 } 206 } 207 208 private void checkBuildLine(String text) { 209 Line line = new Line(text); 210 String cmd = line.getToken(); 211 if (cmd == null) { 212 return; 213 } else if (cmd.endsWith("g++")) { //$NON-NLS-1$ 214 if (mCommand == null || !mCPlusPlus) { 215 mCommand = cmd; 216 mCPlusPlus = true; 217 } 218 gatherOptions(line); 219 } else if (cmd.endsWith("gcc")) { //$NON-NLS-1$ 220 if (mCommand == null) 221 mCommand = cmd; 222 gatherOptions(line); 223 } 224 } 225 226 private void gatherOptions(Line line) { 227 for (String option = line.getToken(); option != null; option = line.getToken()) { 228 if (option.startsWith("-")) { //$NON-NLS-1$ 229 // only look at options 230 if (option.equals("-I")) { //$NON-NLS-1$ 231 String dir = line.getToken(); 232 if (dir != null) 233 addArg(option + dir); 234 } else if (option.startsWith("-I")) { //$NON-NLS-1$ 235 addArg(option); 236 } else if (option.equals("-D")) { //$NON-NLS-1$ 237 String def = line.getToken(); 238 if (def != null) 239 addArg(option + def); 240 } else if (option.startsWith("-D")) { //$NON-NLS-1$ 241 addArg(option); 242 } else if (option.startsWith("-f")) { //$NON-NLS-1$ 243 addArg(option); 244 } else if (option.startsWith("-m")) { //$NON-NLS-1$ 245 addArg(option); 246 } else if (option.startsWith("--sysroot")) { //$NON-NLS-1$ 247 addArg(option); 248 } 249 } 250 } 251 } 252 253 private void addArg(String arg) { 254 if (!mArguments.contains(arg)) 255 mArguments.add(arg); 256 } 257 258 private void checkIncludes(InputStream in) { 259 try { 260 List<String> includes = new ArrayList<String>(); 261 boolean inIncludes1 = false; 262 boolean inIncludes2 = false; 263 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 264 String line = reader.readLine(); 265 while (line != null) { 266 if (!inIncludes1) { 267 if (line.equals("#include \"...\" search starts here:")) //$NON-NLS-1$ 268 inIncludes1 = true; 269 } else { 270 if (!inIncludes2) { 271 if (line.equals("#include <...> search starts here:")) //$NON-NLS-1$ 272 inIncludes2 = true; 273 else 274 includes.add(line.trim()); 275 } else { 276 if (line.equals("End of search list.")) { //$NON-NLS-1$ 277 mPathInfo.setIncludePaths(includes); 278 } else { 279 includes.add(line.trim()); 280 } 281 } 282 } 283 line = reader.readLine(); 284 } 285 } catch (IOException e) { 286 Activator.log(e); 287 } 288 } 289 290 private void checkDefines(InputStream in) { 291 try { 292 Map<String, String> defines = new HashMap<String, String>(); 293 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 294 String line = reader.readLine(); 295 while (line != null) { 296 if (line.startsWith("#define")) { //$NON-NLS-1$ 297 Line l = new Line(line, 7); 298 String var = l.getToken(); 299 if (var == null) 300 continue; 301 String value = l.getRemaining(); 302 if (value == null) 303 value = ""; //$NON-NLS-1$ 304 defines.put(var, value); 305 } 306 line = reader.readLine(); 307 } 308 mPathInfo.setSymbols(defines); 309 } catch (IOException e) { 310 Activator.log(e); 311 } 312 } 313 314 } 315