Home | History | Annotate | Download | only in build
      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.internal.build;
     18 
     19 import com.android.annotations.NonNull;
     20 import com.android.ide.eclipse.adt.AdtConstants;
     21 import com.android.ide.eclipse.adt.AdtPlugin;
     22 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     23 import com.android.sdklib.build.RenderScriptProcessor.CommandLineLauncher;
     24 
     25 import org.eclipse.core.resources.IFile;
     26 import org.eclipse.core.resources.IFolder;
     27 import org.eclipse.core.resources.IMarker;
     28 import org.eclipse.core.resources.IProject;
     29 import org.eclipse.core.resources.IResource;
     30 import org.eclipse.core.runtime.CoreException;
     31 import org.eclipse.core.runtime.IProgressMonitor;
     32 import org.eclipse.core.runtime.IStatus;
     33 import org.eclipse.core.runtime.Path;
     34 
     35 import java.io.File;
     36 import java.io.IOException;
     37 import java.util.ArrayList;
     38 import java.util.List;
     39 import java.util.Map;
     40 import java.util.regex.Matcher;
     41 import java.util.regex.Pattern;
     42 
     43 /**
     44  * A {@link SourceProcessor} for RenderScript files.
     45  */
     46 public class RenderScriptLauncher implements CommandLineLauncher {
     47 
     48     /**
     49      * Single line llvm-rs-cc error: {@code <path>:<line>:<col>: <error>}
     50      */
     51     private static Pattern sLlvmPattern1 = Pattern.compile("^(.+?):(\\d+):(\\d+):\\s(.+)$"); //$NON-NLS-1$
     52 
     53     @NonNull
     54     private final IProject mProject;
     55     @NonNull
     56     private final IFolder mSourceOutFolder;
     57     @NonNull
     58     private final IFolder mResOutFolder;
     59     @NonNull
     60     private final IProgressMonitor mMonitor;
     61     private final boolean mVerbose;
     62 
     63     public RenderScriptLauncher(
     64             @NonNull IProject project,
     65             @NonNull IFolder sourceOutFolder,
     66             @NonNull IFolder resOutFolder,
     67             @NonNull IProgressMonitor monitor,
     68             boolean verbose) {
     69         mProject = project;
     70         mSourceOutFolder = sourceOutFolder;
     71         mResOutFolder = resOutFolder;
     72         mMonitor = monitor;
     73         mVerbose = verbose;
     74     }
     75 
     76     @Override
     77     public void launch(File executable, List<String> arguments, Map<String, String> envVariableMap)
     78             throws IOException, InterruptedException {
     79         // do the exec
     80         try {
     81             if (mVerbose) {
     82                 StringBuilder sb = new StringBuilder(executable.getAbsolutePath());
     83                 for (String c : arguments) {
     84                     sb.append(' ').append(c);
     85                 }
     86                 String cmd_line = sb.toString();
     87                 AdtPlugin.printToConsole(mProject, cmd_line);
     88             }
     89 
     90             String[] commandArray = new String[1 + arguments.size()];
     91             commandArray[0] = executable.getAbsolutePath();
     92             System.arraycopy(arguments.toArray(), 0, commandArray, 1, arguments.size());
     93 
     94             ProcessBuilder processBuilder = new ProcessBuilder(commandArray);
     95             Map<String, String> processEnvs = processBuilder.environment();
     96             for (Map.Entry<String, String> entry : envVariableMap.entrySet()) {
     97                 processEnvs.put(entry.getKey(), entry.getValue());
     98             }
     99 
    100             Process p = processBuilder.start();
    101 
    102             // list to store each line of stderr
    103             ArrayList<String> stdErr = new ArrayList<String>();
    104 
    105             // get the output and return code from the process
    106             int returnCode = BuildHelper.grabProcessOutput(mProject, p, stdErr);
    107 
    108             if (stdErr.size() > 0) {
    109                 // attempt to parse the error output
    110                 boolean parsingError = parseLlvmOutput(stdErr);
    111 
    112                 // If the process failed and we couldn't parse the output
    113                 // we print a message, mark the project and exit
    114                 if (returnCode != 0) {
    115 
    116                     if (parsingError || mVerbose) {
    117                         // display the message in the console.
    118                         if (parsingError) {
    119                             AdtPlugin.printErrorToConsole(mProject, stdErr.toArray());
    120 
    121                             // mark the project
    122                             BaseProjectHelper.markResource(mProject,
    123                                     AdtConstants.MARKER_RENDERSCRIPT,
    124                                     "Unparsed Renderscript error! Check the console for output.",
    125                                     IMarker.SEVERITY_ERROR);
    126                         } else {
    127                             AdtPlugin.printToConsole(mProject, stdErr.toArray());
    128                         }
    129                     }
    130                     return;
    131                 }
    132             } else if (returnCode != 0) {
    133                 // no stderr output but exec failed.
    134                 String msg = String.format("Error executing Renderscript: Return code %1$d",
    135                         returnCode);
    136 
    137                 BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_AIDL,
    138                        msg, IMarker.SEVERITY_ERROR);
    139 
    140                 return;
    141             }
    142         } catch (IOException e) {
    143             // mark the project and exit
    144             String msg = String.format(
    145                     "Error executing Renderscript. Please check %1$s is present at %2$s",
    146                     executable.getName(), executable.getAbsolutePath());
    147             AdtPlugin.log(IStatus.ERROR, msg);
    148             BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_RENDERSCRIPT, msg,
    149                     IMarker.SEVERITY_ERROR);
    150             throw e;
    151         } catch (InterruptedException e) {
    152             // mark the project and exit
    153             String msg = String.format(
    154                     "Error executing Renderscript. Please check %1$s is present at %2$s",
    155                     executable.getName(), executable.getAbsolutePath());
    156             AdtPlugin.log(IStatus.ERROR, msg);
    157             BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_RENDERSCRIPT, msg,
    158                     IMarker.SEVERITY_ERROR);
    159             throw e;
    160         }
    161 
    162         try {
    163             mSourceOutFolder.refreshLocal(IResource.DEPTH_ONE, mMonitor);
    164             mResOutFolder.refreshLocal(IResource.DEPTH_ONE, mMonitor);
    165         } catch (CoreException e) {
    166             AdtPlugin.log(e, "failed to refresh folders");
    167         }
    168 
    169         return;
    170     }
    171 
    172     /**
    173      * Parse the output of llvm-rs-cc and mark the file with any errors.
    174      * @param lines The output to parse.
    175      * @return true if the parsing failed, false if success.
    176      */
    177     private boolean parseLlvmOutput(ArrayList<String> lines) {
    178         // nothing to parse? just return false;
    179         if (lines.size() == 0) {
    180             return false;
    181         }
    182 
    183         // get the root folder for the project as we're going to ignore everything that's
    184         // not in the project
    185         String rootPath = mProject.getLocation().toOSString();
    186         int rootPathLength = rootPath.length();
    187 
    188         Matcher m;
    189 
    190         boolean parsing = false;
    191 
    192         for (int i = 0; i < lines.size(); i++) {
    193             String p = lines.get(i);
    194 
    195             m = sLlvmPattern1.matcher(p);
    196             if (m.matches()) {
    197                 // get the file path. This may, or may not be the main file being compiled.
    198                 String filePath = m.group(1);
    199                 if (filePath.startsWith(rootPath) == false) {
    200                     // looks like the error in a non-project file. Keep parsing, but
    201                     // we'll return true
    202                     parsing = true;
    203                     continue;
    204                 }
    205 
    206                 // get the actual file.
    207                 filePath = filePath.substring(rootPathLength);
    208                 // remove starting separator since we want the path to be relative
    209                 if (filePath.startsWith(File.separator)) {
    210                     filePath = filePath.substring(1);
    211                 }
    212 
    213                 // get the file
    214                 IFile f = mProject.getFile(new Path(filePath));
    215 
    216                 String lineStr = m.group(2);
    217                 // ignore group 3 for now, this is the col number
    218                 String msg = m.group(4);
    219 
    220                 // get the line number
    221                 int line = 0;
    222                 try {
    223                     line = Integer.parseInt(lineStr);
    224                 } catch (NumberFormatException e) {
    225                     // looks like the string we extracted wasn't a valid
    226                     // file number. Parsing failed and we return true
    227                     return true;
    228                 }
    229 
    230                 // mark the file
    231                 BaseProjectHelper.markResource(f, AdtConstants.MARKER_RENDERSCRIPT, msg, line,
    232                         IMarker.SEVERITY_ERROR);
    233 
    234                 // success, go to the next line
    235                 continue;
    236             }
    237 
    238             // invalid line format, flag as error, and keep going
    239             parsing = true;
    240         }
    241 
    242         return parsing;
    243     }
    244 }
    245