blob: 53480d4095ef64273123051b83fb05452c1f2d78 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.build.config;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.ArrayList;
import java.nio.charset.StandardCharsets;
public class KatiCommandImpl implements KatiCommand {
final Errors mErrors;
final Options mOptions;
/**
* Runnable that consumes all of an InputStream until EOF, writes the contents
* into a StringBuilder, and then closes the stream.
*/
class OutputReader implements Runnable {
private final InputStream mStream;
private final StringBuilder mOutput;
OutputReader(InputStream stream, StringBuilder output) {
mStream = stream;
mOutput = output;
}
@Override
public void run() {
final char[] buf = new char[16*1024];
final InputStreamReader reader = new InputStreamReader(mStream, StandardCharsets.UTF_8);
try {
int amt;
while ((amt = reader.read(buf, 0, buf.length)) >= 0) {
mOutput.append(buf, 0, amt);
}
} catch (IOException ex) {
mErrors.ERROR_KATI.add("Error reading from kati: " + ex.getMessage());
} finally {
try {
reader.close();
} catch (IOException ex) {
// Close doesn't throw
}
}
}
}
public KatiCommandImpl(Errors errors, Options options) {
mErrors = errors;
mOptions = options;
}
/**
* Run kati directly. Returns stdout data.
*
* @throws KatiException if there is an error. KatiException will contain
* the stderr from the kati invocation.
*/
public String run(String[] args) throws KatiException {
final ArrayList<String> cmd = new ArrayList();
cmd.add(mOptions.getCKatiBin());
for (String arg: args) {
cmd.add(arg);
}
final ProcessBuilder builder = new ProcessBuilder(cmd);
builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
builder.redirectError(ProcessBuilder.Redirect.PIPE);
Process process = null;
try {
process = builder.start();
} catch (IOException ex) {
throw new KatiException(cmd, "IOException running process: " + ex.getMessage());
}
final StringBuilder stdout = new StringBuilder();
final Thread stdoutThread = new Thread(new OutputReader(process.getInputStream(), stdout),
"kati_stdout_reader");
stdoutThread.start();
final StringBuilder stderr = new StringBuilder();
final Thread stderrThread = new Thread(new OutputReader(process.getErrorStream(), stderr),
"kati_stderr_reader");
stderrThread.start();
int returnCode = waitForProcess(process);
joinThread(stdoutThread);
joinThread(stderrThread);
if (returnCode != 0) {
throw new KatiException(cmd, stderr.toString());
}
return stdout.toString();
}
/**
* Wrap Process.waitFor() because it throws InterruptedException.
*/
private static int waitForProcess(Process proc) {
while (true) {
try {
return proc.waitFor();
} catch (InterruptedException ex) {
}
}
}
/**
* Wrap Thread.join() because it throws InterruptedException.
*/
private static void joinThread(Thread thread) {
while (true) {
try {
thread.join();
return;
} catch (InterruptedException ex) {
}
}
}
}