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.adt.ndk.internal.discovery; 18 19 import com.android.ide.eclipse.adt.ndk.internal.Activator; 20 import com.android.ide.eclipse.adt.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 51 private final NdkDiscoveredPathInfo mPathInfo; 52 private final IProject mProject; 53 54 private boolean mCPlusPlus = false; 55 private String mCommand; 56 private List<String> mArguments = new ArrayList<String>(); 57 58 public NdkDiscoveryUpdater(NdkDiscoveredPathInfo pathInfo) { 59 mPathInfo = pathInfo; 60 mProject = pathInfo.getProject(); 61 } 62 63 public void runUpdate(IProgressMonitor monitor) throws CoreException { 64 try { 65 // Run ndk-build -nB to get the list of commands 66 IPath commandPath = new Path("ndk-build"); //$NON-NLS-1$ 67 String[] args = { 68 "-nB"}; //$NON-NLS-1$ 69 String[] env = calcEnvironment(); 70 File projectDir = new File(mProject.getLocationURI()); 71 IPath changeToDirectory = new Path(projectDir.getAbsolutePath()); 72 Process proc = new NdkCommandLauncher().execute(commandPath, args, env, 73 changeToDirectory, monitor); 74 if (proc == null) 75 // proc failed to start 76 return; 77 BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); 78 String line = reader.readLine(); 79 while (line != null) { 80 checkBuildLine(line); 81 line = reader.readLine(); 82 } 83 84 if (mCommand == null) 85 return; 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 @SuppressWarnings("unchecked") 138 Map<String, String> builderEnv = builder.getExpandedEnvironment(); 139 if (builderEnv != null) 140 envMap.putAll(builderEnv); 141 List<String> strings = new ArrayList<String>(envMap.size()); 142 for (Entry<String, String> entry : envMap.entrySet()) { 143 StringBuffer buffer = new StringBuffer(entry.getKey()); 144 buffer.append('=').append(entry.getValue()); 145 strings.add(buffer.toString()); 146 } 147 return strings.toArray(new String[strings.size()]); 148 } 149 150 private static class Line { 151 private final String line; 152 private int pos; 153 154 public Line(String line) { 155 this.line = line; 156 } 157 158 public Line(String line, int pos) { 159 this(line); 160 this.pos = pos; 161 } 162 163 public String getToken() { 164 skipWhiteSpace(); 165 if (pos == line.length()) 166 return null; 167 168 int start = pos; 169 boolean inQuote = false; 170 171 while (true) { 172 char c = line.charAt(pos); 173 if (c == ' ') { 174 if (!inQuote) 175 return line.substring(start, pos); 176 } else if (c == '"') { 177 inQuote = !inQuote; 178 } 179 180 if (++pos == line.length()) 181 return null; 182 } 183 184 } 185 186 private String getRemaining() { 187 if (pos == line.length()) 188 return null; 189 190 skipWhiteSpace(); 191 String rc = line.substring(pos); 192 pos = line.length(); 193 return rc; 194 } 195 196 private void skipWhiteSpace() { 197 while (true) { 198 if (pos == line.length()) 199 return; 200 char c = line.charAt(pos); 201 if (c == ' ') 202 pos++; 203 else 204 return; 205 } 206 } 207 } 208 209 private void checkBuildLine(String text) { 210 Line line = new Line(text); 211 String cmd = line.getToken(); 212 if (cmd == null) { 213 return; 214 } else if (cmd.endsWith("g++")) { //$NON-NLS-1$ 215 if (mCommand == null || !mCPlusPlus) { 216 mCommand = cmd; 217 mCPlusPlus = true; 218 } 219 gatherOptions(line); 220 } else if (cmd.endsWith("gcc")) { //$NON-NLS-1$ 221 if (mCommand == null) 222 mCommand = cmd; 223 gatherOptions(line); 224 } 225 } 226 227 private void gatherOptions(Line line) { 228 for (String option = line.getToken(); option != null; option = line.getToken()) { 229 if (option.startsWith("-")) { //$NON-NLS-1$ 230 // only look at options 231 if (option.equals("-I")) { //$NON-NLS-1$ 232 String dir = line.getToken(); 233 if (dir != null) 234 addArg(option + dir); 235 } else if (option.startsWith("-I")) { //$NON-NLS-1$ 236 addArg(option); 237 } else if (option.equals("-D")) { //$NON-NLS-1$ 238 String def = line.getToken(); 239 if (def != null) 240 addArg(option + def); 241 } else if (option.startsWith("-D")) { //$NON-NLS-1$ 242 addArg(option); 243 } else if (option.startsWith("-f")) { //$NON-NLS-1$ 244 addArg(option); 245 } else if (option.startsWith("-m")) { //$NON-NLS-1$ 246 addArg(option); 247 } else if (option.startsWith("--sysroot")) { //$NON-NLS-1$ 248 addArg(option); 249 } 250 } 251 } 252 } 253 254 private void addArg(String arg) { 255 if (!mArguments.contains(arg)) 256 mArguments.add(arg); 257 } 258 259 private void checkIncludes(InputStream in) { 260 try { 261 List<String> includes = new ArrayList<String>(); 262 boolean inIncludes1 = false; 263 boolean inIncludes2 = false; 264 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 265 String line = reader.readLine(); 266 while (line != null) { 267 if (!inIncludes1) { 268 if (line.equals("#include \"...\" search starts here:")) //$NON-NLS-1$ 269 inIncludes1 = true; 270 } else { 271 if (!inIncludes2) { 272 if (line.equals("#include <...> search starts here:")) //$NON-NLS-1$ 273 inIncludes2 = true; 274 else 275 includes.add(line.trim()); 276 } else { 277 if (line.equals("End of search list.")) { //$NON-NLS-1$ 278 mPathInfo.setIncludePaths(includes); 279 } else { 280 includes.add(line.trim()); 281 } 282 } 283 } 284 line = reader.readLine(); 285 } 286 } catch (IOException e) { 287 Activator.log(e); 288 } 289 } 290 291 private void checkDefines(InputStream in) { 292 try { 293 Map<String, String> defines = new HashMap<String, String>(); 294 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 295 String line = reader.readLine(); 296 while (line != null) { 297 if (line.startsWith("#define")) { //$NON-NLS-1$ 298 Line l = new Line(line, 7); 299 String var = l.getToken(); 300 if (var == null) 301 continue; 302 String value = l.getRemaining(); 303 if (value == null) 304 value = ""; //$NON-NLS-1$ 305 defines.put(var, value); 306 } 307 line = reader.readLine(); 308 } 309 mPathInfo.setSymbols(defines); 310 } catch (IOException e) { 311 Activator.log(e); 312 } 313 } 314 315 } 316