Home | History | Annotate | Download | only in discovery
      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