1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::env;
6 use std::error::Error;
7 use std::fs::{self, File};
8 use std::io::Write;
9 use std::path::{Path, PathBuf};
10
11 type Result<T> = std::result::Result<T, Box<dyn Error>>;
12
13 struct ExternalProto {
14 // Where to find protos during builds within cros_sdk. Relative to
15 // $SYSROOT environment variable set by emerge builds.
16 dir_relative_to_sysroot: &'static str,
17
18 // Where to find protos during "cargo build" in a local developer
19 // environment. Relative to the platform/crosvm/protos directory.
20 dir_relative_to_us: &'static str,
21
22 // *.proto file expected to exist in both of the above directories.
23 proto_file_name: &'static str,
24
25 // Code generated by proto compiler will be placed under
26 // protos::generated::$module_name.
27 module: &'static str,
28 }
29
30 // Rustfmt bug: https://github.com/rust-lang/rustfmt/issues/3498
31 #[rustfmt::skip]
32 static EXTERNAL_PROTOS: &[ExternalProto] = &[];
33
34 struct LocalProto {
35 // Corresponding to the input file src/$module.proto.
36 module: &'static str,
37 }
38
39 #[rustfmt::skip]
40 static LOCAL_PROTOS: &[LocalProto] = &[
41 #[cfg(feature = "plugin")]
42 LocalProto { module: "plugin" },
43 #[cfg(feature = "composite-disk")]
44 LocalProto { module: "cdisk_spec" },
45 ];
46
main() -> Result<()>47 fn main() -> Result<()> {
48 let out_dir = env::var("OUT_DIR")?;
49 let sysroot = env::var_os("SYSROOT");
50
51 // Write out a Rust module that imports the modules generated by protoc.
52 let generated = PathBuf::from(&out_dir).join("generated.rs");
53 let out = File::create(generated)?;
54
55 // Compile external protos.
56 for proto in EXTERNAL_PROTOS {
57 let dir = match &sysroot {
58 Some(dir) => PathBuf::from(dir).join(proto.dir_relative_to_sysroot),
59 None => PathBuf::from(proto.dir_relative_to_us),
60 };
61 let input_path = dir.join(proto.proto_file_name);
62 protoc(proto.module, input_path, &out)?;
63 }
64
65 // Compile protos from the local src directory.
66 for proto in LOCAL_PROTOS {
67 let input_path = format!("src/{}.proto", proto.module);
68 protoc(proto.module, input_path, &out)?;
69 }
70
71 Ok(())
72 }
73
74 // Compile a single proto file located at $input_path, placing the generated
75 // code at $OUT_DIR/$module and emitting the right `pub mod $module` into the
76 // output file.
protoc<P: AsRef<Path>>(module: &str, input_path: P, mut out: &File) -> Result<()>77 fn protoc<P: AsRef<Path>>(module: &str, input_path: P, mut out: &File) -> Result<()> {
78 let input_path = input_path.as_ref();
79 let input_dir = input_path.parent().unwrap();
80
81 // Place output in a subdirectory so that different protos with the same
82 // common filename (like interface.proto) do not conflict.
83 let out_dir = format!("{}/{}", env::var("OUT_DIR")?, module);
84 fs::create_dir_all(&out_dir)?;
85
86 // Invoke protobuf compiler.
87 protoc_rust::Codegen::new()
88 .input(input_path.as_os_str().to_str().unwrap())
89 .include(input_dir.as_os_str().to_str().unwrap())
90 .out_dir(&out_dir)
91 .run()
92 .expect("protoc");
93
94 // Write out a `mod` that refers to the generated module.
95 //
96 // The lint suppression is because protoc-rust emits
97 // #![cfg_attr(feature = "cargo-clippy", allow(clippy))]
98 // which still works but is deprecated in favor of tool attributes:
99 // #![allow(clippy::all)].
100 let file_stem = input_path.file_stem().unwrap().to_str().unwrap();
101 writeln!(out, "#[path = \"{}/{}.rs\"]", out_dir, file_stem)?;
102 writeln!(out, "#[allow(renamed_and_removed_lints)]")?;
103 writeln!(out, "pub mod {};", module)?;
104
105 Ok(())
106 }
107