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