1use std::env;
2use std::ffi::OsStr;
3use std::fs::create_dir_all;
4use std::path::{Path, PathBuf};
5use std::process::Command;
6
7use cargo_metadata::MetadataCommand;
8use walkdir::WalkDir;
10
11fn manifest_dir() -> PathBuf {
12 PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
13 .canonicalize()
14 .expect("Failed to canonicalize CARGO_MANIFEST_DIR")
15}
16
17fn out_dir() -> PathBuf {
18 PathBuf::from(env::var("OUT_DIR").unwrap())
19 .canonicalize()
20 .expect("Failed to canonicalize OUT_DIR")
21}
22
23pub fn build() {
24 if env::var("CARGO_FEATURE_FLATBUFFERS").ok().is_some() {
26 build_flatbuffers();
27 }
28
29 if env::var("CARGO_FEATURE_PROTO").ok().is_some() {
31 build_proto();
32 }
33}
34
35pub fn build_proto() {
36 let proto_dir = manifest_dir().join("proto");
37 let proto_files = walk_files(&proto_dir, "proto");
38 let proto_out = out_dir().join("proto");
39
40 create_dir_all(&proto_out).expect("Failed to create proto output directory");
41
42 let metadata = MetadataCommand::new()
45 .manifest_path(manifest_dir().join("Cargo.toml"))
46 .no_deps()
47 .exec()
48 .unwrap();
49 let proto_feature = "proto".to_string();
50 let pkg_name = env::var("CARGO_PKG_NAME").unwrap();
51 let proto_includes = metadata
52 .packages
53 .iter()
54 .filter(|&pkg| {
55 pkg.features.contains_key(&proto_feature)
56 || pkg.name == pkg_name
57 })
58 .map(|pkg| {
59 println!("cargo:warning=using proto files from {:?}", pkg.name);
60 pkg.manifest_path.parent().unwrap().join("proto")
61 })
62 .collect::<Vec<_>>();
63
64 prost_build::Config::new()
65 .out_dir(&proto_out)
66 .compile_protos(&proto_files, proto_includes.as_slice())
67 .expect("Failed to compile protos");
68}
69
70pub fn build_flatbuffers() {
71 let flatbuffers_dir = manifest_dir().join("flatbuffers");
72 let metadata = MetadataCommand::new()
73 .manifest_path(manifest_dir().join("Cargo.toml"))
74 .no_deps()
75 .exec()
76 .unwrap();
77 let fbs_feature = "flatbuffers".to_string();
78 let pkg_name = env::var("CARGO_PKG_NAME").unwrap();
79 let fbs_includes = metadata
80 .packages
81 .iter()
82 .filter(|&pkg| {
83 pkg.features.contains_key(&fbs_feature) || pkg.name == pkg_name
85 })
86 .map(|pkg| {
87 println!("cargo:warning=using fbs files from {:?}", pkg.name);
88 pkg.manifest_path
89 .parent()
90 .unwrap()
91 .join("flatbuffers")
92 .into_std_path_buf()
93 })
94 .collect::<Vec<_>>();
95
96 let fbs_files = walk_files(&flatbuffers_dir, "fbs");
97
98 let include_args: Vec<String> = fbs_includes
99 .iter()
100 .flat_map(|path| vec!["-I".to_string(), path.to_str().unwrap().to_string()])
101 .collect();
102
103 println!("cargo:warning=flatc {:?}", include_args);
104
105 check_call(
106 Command::new("flatc")
107 .arg("--rust")
108 .arg("--filename-suffix")
109 .arg("")
110 .args(include_args)
111 .arg("--include-prefix")
112 .arg("flatbuffers::deps")
113 .arg("-o")
114 .arg(out_dir().join("flatbuffers"))
115 .args(fbs_files),
116 )
117}
118
119fn walk_files(dir: &Path, ext: &str) -> Vec<PathBuf> {
121 WalkDir::new(dir)
122 .into_iter()
123 .filter_map(|e| e.ok())
124 .filter(|e| e.path().extension() == Some(OsStr::new(ext)))
125 .map(|e| {
126 rerun_if_changed(e.path());
127 e.path().to_path_buf()
128 })
129 .collect()
130}
131
132fn rerun_if_changed(path: &Path) {
133 println!(
134 "cargo:rerun-if-changed={}",
135 path.canonicalize()
136 .unwrap_or_else(|_| panic!("failed to canonicalize {}", path.to_str().unwrap()))
137 .to_str()
138 .unwrap()
139 );
140}
141
142fn check_call(command: &mut Command) {
143 let name = command.get_program().to_str().unwrap().to_string();
144 let Ok(status) = command.status() else {
145 panic!("Failed to launch {}", &name)
146 };
147 if !status.success() {
148 panic!("{} failed with status {}", &name, status.code().unwrap());
149 }
150}