blob: d8f8c84997dd2cdc27694c9c9f6e847284f92415 [file] [log] [blame]
/*
* Copyright (C) 2019 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.google.tuningfork.validation;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import com.google.protobuf.Descriptors.DescriptorValidationException;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
/** Compiles .proto file into a {@link FileDescriptor} */
public class ExternalProtoCompiler {
private final String protoPath;
private final FileDescriptor[] emptyDeps = new FileDescriptor[0];
public ExternalProtoCompiler(File protoBinary) {
Preconditions.checkNotNull(protoBinary, "executable");
protoPath = protoBinary.getAbsolutePath();
}
public FileDescriptor compile(File file, Optional<File> outFile)
throws IOException, CompilationException {
Preconditions.checkNotNull(file, "file");
FileDescriptor descriptor = buildAndRunCompilerProcess(file, outFile);
return descriptor;
}
public byte[] runCommand(ProcessBuilder processBuilder) throws IOException, CompilationException {
Process process = processBuilder.start();
try {
process.waitFor();
} catch (InterruptedException e) {
throw new CompilationException("Process was interrupted", e);
}
InputStream stdin = process.getInputStream();
byte[] result = new byte[stdin.available()];
stdin.read(result);
return result;
}
private FileDescriptor buildAndRunCompilerProcess(File file, Optional<File> outFile)
throws IOException, CompilationException {
ImmutableList<String> commandLine = createCommandLine(file);
byte[] result = runCommand(new ProcessBuilder(commandLine));
try {
FileDescriptorSet fileSet = FileDescriptorSet.parseFrom(result);
if (outFile.isPresent()) {
Files.write(fileSet.toByteArray(), outFile.get());
}
for (FileDescriptorProto descProto : fileSet.getFileList()) {
if (descProto.getName().equals(file.getName())) {
return FileDescriptor.buildFrom(descProto, emptyDeps);
}
}
} catch (DescriptorValidationException | InvalidProtocolBufferException e) {
throw new IllegalStateException(e);
}
throw new CompilationException(
String.format("Descriptor for [%s] does not exist.", file.getName()));
}
/* Decode textproto message from text(textprotoFile) into binary(binFile) */
public File encodeFromTextprotoFile(
String message,
File protoFile,
File textprotoFile,
String binaryPath,
Optional<File> errorFile)
throws IOException, CompilationException {
ImmutableList<String> command = encodeCommandLine(message, protoFile);
File binFile = new File(binaryPath);
ProcessBuilder builder =
new ProcessBuilder(command).redirectInput(textprotoFile).redirectOutput(binFile);
if (errorFile.isPresent()) {
builder.redirectError(errorFile.get());
}
runCommand(builder);
return binFile;
}
/* Decode textproto message from binary(binFile) into text(textFile) */
public File decodeToTextprotoFile(
String message, File protoFile, String textprotoPath, File binFile, Optional<File> errorFile)
throws IOException, CompilationException {
ImmutableList<String> command = decodeCommandLine(message, protoFile);
File textprotoFile = new File(textprotoPath);
ProcessBuilder builder =
new ProcessBuilder(command).redirectInput(binFile).redirectOutput(textprotoFile);
if (errorFile.isPresent()) {
builder.redirectError(errorFile.get());
}
runCommand(builder);
return textprotoFile;
}
private ImmutableList<String> createCommandLine(File file) {
return ImmutableList.of(
protoPath,
"-o",
"/dev/stdout",
"-I",
file.getName() + "=" + file.getAbsolutePath(), // That should be one line
file.getName());
}
private ImmutableList<String> encodeCommandLine(String message, File protoFile) {
return ImmutableList.of(
protoPath,
"--encode=" + message,
"-I",
protoFile.getName() + "=" + protoFile.getAbsolutePath(), // That should be one line
protoFile.getName());
}
private ImmutableList<String> decodeCommandLine(String message, File protoFile) {
return ImmutableList.of(
protoPath,
"--decode=" + message,
"-I",
protoFile.getName() + "=" + protoFile.getAbsolutePath(), // That should be one line
protoFile.getName());
}
}