blob: 48879c34a0397c24c140fdd3a5c46e2d6187950c [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{bail, Context, Result};
use argh::FromArgs;
use prost_build;
use std::collections::HashSet;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
#[derive(FromArgs, Debug)]
/// compiles .proto files into Rust
struct Options {
#[argh(option)]
/// path to the protoc binary
protoc: PathBuf,
#[argh(option)]
/// directory in which to generate rust code
out_dir: PathBuf,
#[argh(option)]
/// directories to search for imports
include_dirs: Vec<PathBuf>,
#[argh(option)]
/// protobuf files to compile
protos: Vec<PathBuf>,
}
fn main() -> Result<()> {
let options: Options = argh::from_env();
if options.protos.is_empty() {
bail!("Must provide at least one proto to compile")
}
if !options.out_dir.is_dir() {
bail!("out_dir must be a directory")
}
if !options.include_dirs.iter().all(|p| p.is_dir()) {
bail!("all includes should be directories")
}
if !options.protos.iter().all(|p| p.is_file()) {
bail!("all protos should be files")
}
// The protoc location is read by the library from an environment variable
std::env::set_var("PROTOC", options.protoc);
// Add the parent directories of the provided protos to the list of include dirs.
// Config::compile_protos requires that the .proto files passed in must be found in one of the
// include directories.
let mut include_set: HashSet<PathBuf> = HashSet::from_iter(options.include_dirs.into_iter());
include_set.extend(options.protos.iter().map(|p| p.parent().unwrap().to_owned()));
// If proto is a relative path without a CurDir component, parent() may yield ""
// Replace such instances with CurDir
if let Some(mut dir) = include_set.take(&PathBuf::new()) {
dir.push(".");
include_set.insert(dir);
}
let includes: Vec<PathBuf> = include_set.into_iter().collect();
// The prost_build library prints to stdout which is undesirable.
// Replace the stdout fd with /dev/null so as to be less disruptive.
let dev_null = File::open("/dev/null")?;
unsafe {
libc::dup2(dev_null.as_raw_fd(), std::io::stdout().as_raw_fd());
}
let mut config = prost_build::Config::new();
config.out_dir(&options.out_dir);
config.include_file("lib.rs");
config.compile_protos(&options.protos, &includes).context("Failed to compile protos")
}