1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker
5*bb4ee6a4SAndroid Build Coastguard Worker //! Contains utilities to build protos & make them available to Rust.
6*bb4ee6a4SAndroid Build Coastguard Worker
7*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::HashSet;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::fs;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Write;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
12*bb4ee6a4SAndroid Build Coastguard Worker
13*bb4ee6a4SAndroid Build Coastguard Worker use protobuf_codegen::Codegen;
14*bb4ee6a4SAndroid Build Coastguard Worker
15*bb4ee6a4SAndroid Build Coastguard Worker /// Builds a set of Rust protos based on the provided proto files. The individual protos will be
16*bb4ee6a4SAndroid Build Coastguard Worker /// dumped into `out_dir` (will be created if needed), along with a file that wraps them
17*bb4ee6a4SAndroid Build Coastguard Worker /// `out_dir/generated.rs`. The wrapper file can then be included using a pattern like:
18*bb4ee6a4SAndroid Build Coastguard Worker /// ```ignore
19*bb4ee6a4SAndroid Build Coastguard Worker /// pub mod protos {
20*bb4ee6a4SAndroid Build Coastguard Worker /// // Suppose the `out_dir` supplied to `build_protos` was
21*bb4ee6a4SAndroid Build Coastguard Worker /// // format!("{}/my_crate_protos", env!("OUT_DIR"))
22*bb4ee6a4SAndroid Build Coastguard Worker /// include!(concat!(env!("OUT_DIR"), "/my_crate_protos/generated.rs"));
23*bb4ee6a4SAndroid Build Coastguard Worker /// }
24*bb4ee6a4SAndroid Build Coastguard Worker /// // Protos are available at protos::proto_file_name.
25*bb4ee6a4SAndroid Build Coastguard Worker /// ```
build_protos(out_dir: &PathBuf, proto_paths: &[PathBuf])26*bb4ee6a4SAndroid Build Coastguard Worker pub fn build_protos(out_dir: &PathBuf, proto_paths: &[PathBuf]) {
27*bb4ee6a4SAndroid Build Coastguard Worker build_protos_explicit(
28*bb4ee6a4SAndroid Build Coastguard Worker out_dir,
29*bb4ee6a4SAndroid Build Coastguard Worker proto_paths,
30*bb4ee6a4SAndroid Build Coastguard Worker proto_paths,
31*bb4ee6a4SAndroid Build Coastguard Worker to_includes(proto_paths).as_slice(),
32*bb4ee6a4SAndroid Build Coastguard Worker )
33*bb4ee6a4SAndroid Build Coastguard Worker }
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Worker /// Allows for more control than build_protos (useful when the proto build is more complex).
build_protos_explicit( out_dir: &PathBuf, proto_paths: &[PathBuf], rebuild_if_changed_paths: &[PathBuf], includes: &[PathBuf], )36*bb4ee6a4SAndroid Build Coastguard Worker pub fn build_protos_explicit(
37*bb4ee6a4SAndroid Build Coastguard Worker out_dir: &PathBuf,
38*bb4ee6a4SAndroid Build Coastguard Worker proto_paths: &[PathBuf],
39*bb4ee6a4SAndroid Build Coastguard Worker rebuild_if_changed_paths: &[PathBuf],
40*bb4ee6a4SAndroid Build Coastguard Worker includes: &[PathBuf],
41*bb4ee6a4SAndroid Build Coastguard Worker ) {
42*bb4ee6a4SAndroid Build Coastguard Worker for file in rebuild_if_changed_paths {
43*bb4ee6a4SAndroid Build Coastguard Worker // Triggers rebuild if the file has newer mtime.
44*bb4ee6a4SAndroid Build Coastguard Worker println!(
45*bb4ee6a4SAndroid Build Coastguard Worker "cargo:rerun-if-changed={}",
46*bb4ee6a4SAndroid Build Coastguard Worker file.to_str().expect("proto path must be UTF-8")
47*bb4ee6a4SAndroid Build Coastguard Worker );
48*bb4ee6a4SAndroid Build Coastguard Worker }
49*bb4ee6a4SAndroid Build Coastguard Worker fs::create_dir_all(out_dir).unwrap();
50*bb4ee6a4SAndroid Build Coastguard Worker if !proto_paths.is_empty() {
51*bb4ee6a4SAndroid Build Coastguard Worker gen_protos(out_dir, proto_paths, includes);
52*bb4ee6a4SAndroid Build Coastguard Worker }
53*bb4ee6a4SAndroid Build Coastguard Worker create_gen_file(out_dir, proto_paths);
54*bb4ee6a4SAndroid Build Coastguard Worker }
55*bb4ee6a4SAndroid Build Coastguard Worker
56*bb4ee6a4SAndroid Build Coastguard Worker /// Given a list of proto files, extract the set of include directories needed to pass to the proto
57*bb4ee6a4SAndroid Build Coastguard Worker /// compiler.
to_includes(proto_paths: &[PathBuf]) -> Vec<PathBuf>58*bb4ee6a4SAndroid Build Coastguard Worker fn to_includes(proto_paths: &[PathBuf]) -> Vec<PathBuf> {
59*bb4ee6a4SAndroid Build Coastguard Worker let mut include_paths = HashSet::new();
60*bb4ee6a4SAndroid Build Coastguard Worker
61*bb4ee6a4SAndroid Build Coastguard Worker for proto in proto_paths {
62*bb4ee6a4SAndroid Build Coastguard Worker include_paths.insert(
63*bb4ee6a4SAndroid Build Coastguard Worker proto
64*bb4ee6a4SAndroid Build Coastguard Worker .parent()
65*bb4ee6a4SAndroid Build Coastguard Worker .expect("protos must be files in a directory")
66*bb4ee6a4SAndroid Build Coastguard Worker .to_owned(),
67*bb4ee6a4SAndroid Build Coastguard Worker );
68*bb4ee6a4SAndroid Build Coastguard Worker }
69*bb4ee6a4SAndroid Build Coastguard Worker
70*bb4ee6a4SAndroid Build Coastguard Worker include_paths.drain().collect::<Vec<PathBuf>>()
71*bb4ee6a4SAndroid Build Coastguard Worker }
72*bb4ee6a4SAndroid Build Coastguard Worker
gen_protos(out_dir: &PathBuf, proto_paths: &[PathBuf], includes: &[PathBuf])73*bb4ee6a4SAndroid Build Coastguard Worker fn gen_protos(out_dir: &PathBuf, proto_paths: &[PathBuf], includes: &[PathBuf]) {
74*bb4ee6a4SAndroid Build Coastguard Worker Codegen::new()
75*bb4ee6a4SAndroid Build Coastguard Worker .out_dir(out_dir)
76*bb4ee6a4SAndroid Build Coastguard Worker .inputs(proto_paths)
77*bb4ee6a4SAndroid Build Coastguard Worker .includes(includes)
78*bb4ee6a4SAndroid Build Coastguard Worker .run()
79*bb4ee6a4SAndroid Build Coastguard Worker .expect("failed to compile Rust protos");
80*bb4ee6a4SAndroid Build Coastguard Worker }
81*bb4ee6a4SAndroid Build Coastguard Worker
create_gen_file(out_dir: &PathBuf, proto_files: &[PathBuf])82*bb4ee6a4SAndroid Build Coastguard Worker fn create_gen_file(out_dir: &PathBuf, proto_files: &[PathBuf]) {
83*bb4ee6a4SAndroid Build Coastguard Worker let generated = PathBuf::from(&out_dir).join("generated.rs");
84*bb4ee6a4SAndroid Build Coastguard Worker let out = File::create(generated).expect("Failed to create generated file.");
85*bb4ee6a4SAndroid Build Coastguard Worker
86*bb4ee6a4SAndroid Build Coastguard Worker for proto_path in proto_files {
87*bb4ee6a4SAndroid Build Coastguard Worker let file_stem = proto_path.file_stem().unwrap().to_str().unwrap();
88*bb4ee6a4SAndroid Build Coastguard Worker let out_dir = out_dir
89*bb4ee6a4SAndroid Build Coastguard Worker .to_str()
90*bb4ee6a4SAndroid Build Coastguard Worker .expect("path must be UTF-8")
91*bb4ee6a4SAndroid Build Coastguard Worker .replace('\\', "/");
92*bb4ee6a4SAndroid Build Coastguard Worker writeln!(&out, "#[path = \"{}/{}.rs\"]", out_dir, file_stem)
93*bb4ee6a4SAndroid Build Coastguard Worker .expect("failed to write to generated.");
94*bb4ee6a4SAndroid Build Coastguard Worker writeln!(&out, "pub mod {};", file_stem).expect("failed to write to generated.");
95*bb4ee6a4SAndroid Build Coastguard Worker }
96*bb4ee6a4SAndroid Build Coastguard Worker }
97