| /* |
| * 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()); |
| } |
| } |