aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptLauncher.java
blob: 1d3c7bd3aa33d56c2fdb3bdea2b36ae7351af1c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.eclipse.org/org/documents/epl-v10.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ide.eclipse.adt.internal.build;

import com.android.annotations.NonNull;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.sdklib.build.RenderScriptProcessor.CommandLineLauncher;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A {@link SourceProcessor} for RenderScript files.
 */
public class RenderScriptLauncher implements CommandLineLauncher {

    /**
     * Single line llvm-rs-cc error: {@code <path>:<line>:<col>: <error>}
     */
    private static Pattern sLlvmPattern1 = Pattern.compile("^(.+?):(\\d+):(\\d+):\\s(.+)$"); //$NON-NLS-1$

    @NonNull
    private final IProject mProject;
    @NonNull
    private final IFolder mSourceOutFolder;
    @NonNull
    private final IFolder mResOutFolder;
    @NonNull
    private final IProgressMonitor mMonitor;
    private final boolean mVerbose;

    public RenderScriptLauncher(
            @NonNull IProject project,
            @NonNull IFolder sourceOutFolder,
            @NonNull IFolder resOutFolder,
            @NonNull IProgressMonitor monitor,
            boolean verbose) {
        mProject = project;
        mSourceOutFolder = sourceOutFolder;
        mResOutFolder = resOutFolder;
        mMonitor = monitor;
        mVerbose = verbose;
    }

    @Override
    public void launch(File executable, List<String> arguments, Map<String, String> envVariableMap)
            throws IOException, InterruptedException {
        // do the exec
        try {
            if (mVerbose) {
                StringBuilder sb = new StringBuilder(executable.getAbsolutePath());
                for (String c : arguments) {
                    sb.append(' ').append(c);
                }
                String cmd_line = sb.toString();
                AdtPlugin.printToConsole(mProject, cmd_line);
            }

            String[] commandArray = new String[1 + arguments.size()];
            commandArray[0] = executable.getAbsolutePath();
            System.arraycopy(arguments.toArray(), 0, commandArray, 1, arguments.size());

            ProcessBuilder processBuilder = new ProcessBuilder(commandArray);
            Map<String, String> processEnvs = processBuilder.environment();
            for (Map.Entry<String, String> entry : envVariableMap.entrySet()) {
                processEnvs.put(entry.getKey(), entry.getValue());
            }

            Process p = processBuilder.start();

            // list to store each line of stderr
            ArrayList<String> stdErr = new ArrayList<String>();

            // get the output and return code from the process
            int returnCode = BuildHelper.grabProcessOutput(mProject, p, stdErr);

            if (stdErr.size() > 0) {
                // attempt to parse the error output
                boolean parsingError = parseLlvmOutput(stdErr);

                // If the process failed and we couldn't parse the output
                // we print a message, mark the project and exit
                if (returnCode != 0) {

                    if (parsingError || mVerbose) {
                        // display the message in the console.
                        if (parsingError) {
                            AdtPlugin.printErrorToConsole(mProject, stdErr.toArray());

                            // mark the project
                            BaseProjectHelper.markResource(mProject,
                                    AdtConstants.MARKER_RENDERSCRIPT,
                                    "Unparsed Renderscript error! Check the console for output.",
                                    IMarker.SEVERITY_ERROR);
                        } else {
                            AdtPlugin.printToConsole(mProject, stdErr.toArray());
                        }
                    }
                    return;
                }
            } else if (returnCode != 0) {
                // no stderr output but exec failed.
                String msg = String.format("Error executing Renderscript: Return code %1$d",
                        returnCode);

                BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_AIDL,
                       msg, IMarker.SEVERITY_ERROR);

                return;
            }
        } catch (IOException e) {
            // mark the project and exit
            String msg = String.format(
                    "Error executing Renderscript. Please check %1$s is present at %2$s",
                    executable.getName(), executable.getAbsolutePath());
            AdtPlugin.log(IStatus.ERROR, msg);
            BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_RENDERSCRIPT, msg,
                    IMarker.SEVERITY_ERROR);
            throw e;
        } catch (InterruptedException e) {
            // mark the project and exit
            String msg = String.format(
                    "Error executing Renderscript. Please check %1$s is present at %2$s",
                    executable.getName(), executable.getAbsolutePath());
            AdtPlugin.log(IStatus.ERROR, msg);
            BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_RENDERSCRIPT, msg,
                    IMarker.SEVERITY_ERROR);
            throw e;
        }

        try {
            mSourceOutFolder.refreshLocal(IResource.DEPTH_ONE, mMonitor);
            mResOutFolder.refreshLocal(IResource.DEPTH_ONE, mMonitor);
        } catch (CoreException e) {
            AdtPlugin.log(e, "failed to refresh folders");
        }

        return;
    }

    /**
     * Parse the output of llvm-rs-cc and mark the file with any errors.
     * @param lines The output to parse.
     * @return true if the parsing failed, false if success.
     */
    private boolean parseLlvmOutput(ArrayList<String> lines) {
        // nothing to parse? just return false;
        if (lines.size() == 0) {
            return false;
        }

        // get the root folder for the project as we're going to ignore everything that's
        // not in the project
        String rootPath = mProject.getLocation().toOSString();
        int rootPathLength = rootPath.length();

        Matcher m;

        boolean parsing = false;

        for (int i = 0; i < lines.size(); i++) {
            String p = lines.get(i);

            m = sLlvmPattern1.matcher(p);
            if (m.matches()) {
                // get the file path. This may, or may not be the main file being compiled.
                String filePath = m.group(1);
                if (filePath.startsWith(rootPath) == false) {
                    // looks like the error in a non-project file. Keep parsing, but
                    // we'll return true
                    parsing = true;
                    continue;
                }

                // get the actual file.
                filePath = filePath.substring(rootPathLength);
                // remove starting separator since we want the path to be relative
                if (filePath.startsWith(File.separator)) {
                    filePath = filePath.substring(1);
                }

                // get the file
                IFile f = mProject.getFile(new Path(filePath));

                String lineStr = m.group(2);
                // ignore group 3 for now, this is the col number
                String msg = m.group(4);

                // get the line number
                int line = 0;
                try {
                    line = Integer.parseInt(lineStr);
                } catch (NumberFormatException e) {
                    // looks like the string we extracted wasn't a valid
                    // file number. Parsing failed and we return true
                    return true;
                }

                // mark the file
                BaseProjectHelper.markResource(f, AdtConstants.MARKER_RENDERSCRIPT, msg, line,
                        IMarker.SEVERITY_ERROR);

                // success, go to the next line
                continue;
            }

            // invalid line format, flag as error, and keep going
            parsing = true;
        }

        return parsing;
    }
}