-
Notifications
You must be signed in to change notification settings - Fork 98
/
build.rs
159 lines (138 loc) · 5.3 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use std::path::{Path, PathBuf};
fn main() {
let mut bridge_files = vec![PathBuf::from("src/lib.rs")];
#[cfg(feature = "ros2-bridge")]
bridge_files.push(ros2::generate());
let _build = cxx_build::bridges(&bridge_files);
println!("cargo:rerun-if-changed=src/lib.rs");
// rename header files
let src_dir = target_dir()
.join("cxxbridge")
.join("dora-node-api-cxx")
.join("src");
let target_dir = src_dir.parent().unwrap();
std::fs::copy(src_dir.join("lib.rs.h"), target_dir.join("dora-node-api.h")).unwrap();
std::fs::copy(
src_dir.join("lib.rs.cc"),
target_dir.join("dora-node-api.cc"),
)
.unwrap();
#[cfg(feature = "ros2-bridge")]
ros2::generate_ros2_message_header(bridge_files.last().unwrap());
// to avoid unnecessary `mut` warning
bridge_files.clear();
}
fn target_dir() -> PathBuf {
std::env::var("CARGO_TARGET_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| {
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(3)
.unwrap();
root.join("target")
})
}
#[cfg(feature = "ros2-bridge")]
mod ros2 {
use super::target_dir;
use std::{
io::{BufRead, BufReader},
path::{Component, Path, PathBuf},
};
pub fn generate() -> PathBuf {
use rust_format::Formatter;
let paths = ament_prefix_paths();
let generated = dora_ros2_bridge_msg_gen::gen(paths.as_slice(), true);
let generated_string = rust_format::PrettyPlease::default()
.format_tokens(generated)
.unwrap();
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let target_file = out_dir.join("ros2_bindings.rs");
std::fs::write(&target_file, generated_string).unwrap();
println!(
"cargo:rustc-env=ROS2_BINDINGS_PATH={}",
target_file.display()
);
target_file
}
fn ament_prefix_paths() -> Vec<PathBuf> {
let ament_prefix_path: String = match std::env::var("AMENT_PREFIX_PATH") {
Ok(path) => path,
Err(std::env::VarError::NotPresent) => {
println!("cargo:warning='AMENT_PREFIX_PATH not set'");
String::new()
}
Err(std::env::VarError::NotUnicode(s)) => {
panic!(
"AMENT_PREFIX_PATH is not valid unicode: `{}`",
s.to_string_lossy()
);
}
};
println!("cargo:rerun-if-env-changed=AMENT_PREFIX_PATH");
let paths: Vec<_> = ament_prefix_path.split(':').map(PathBuf::from).collect();
for path in &paths {
println!("cargo:rerun-if-changed={}", path.display());
}
paths
}
pub fn generate_ros2_message_header(source_file: &Path) {
use std::io::Write as _;
let out_dir = source_file.parent().unwrap();
let relative_path = local_relative_path(&source_file)
.ancestors()
.nth(2)
.unwrap()
.join("out");
let header_path = out_dir
.join("cxxbridge")
.join("include")
.join("dora-node-api-cxx")
.join(&relative_path)
.join("ros2_bindings.rs.h");
let code_path = out_dir
.join("cxxbridge")
.join("sources")
.join("dora-node-api-cxx")
.join(&relative_path)
.join("ros2_bindings.rs.cc");
// copy message files to target directory
let target_path = target_dir()
.join("cxxbridge")
.join("dora-node-api-cxx")
.join("dora-ros2-bindings.h");
std::fs::copy(&header_path, &target_path).unwrap();
println!("cargo:rerun-if-changed={}", header_path.display());
let node_header =
std::fs::File::open(target_path.with_file_name("dora-node-api.h")).unwrap();
let mut code_file = std::fs::File::open(&code_path).unwrap();
println!("cargo:rerun-if-changed={}", code_path.display());
let mut code_target_file =
std::fs::File::create(target_path.with_file_name("dora-ros2-bindings.cc")).unwrap();
// copy both the node header and the code file to prevent import errors
let mut header_reader = {
let mut reader = BufReader::new(node_header);
// read first line to skip `#pragma once`, which is not allowed in main files
let mut first_line = String::new();
reader.read_line(&mut first_line).unwrap();
assert_eq!(first_line.trim(), "#pragma once");
reader
};
std::io::copy(&mut header_reader, &mut code_target_file).unwrap();
std::io::copy(&mut code_file, &mut code_target_file).unwrap();
code_target_file.flush().unwrap();
}
// copy from cxx-build source
fn local_relative_path(path: &Path) -> PathBuf {
let mut rel_path = PathBuf::new();
for component in path.components() {
match component {
Component::Prefix(_) | Component::RootDir | Component::CurDir => {}
Component::ParentDir => drop(rel_path.pop()), // noop if empty
Component::Normal(name) => rel_path.push(name),
}
}
rel_path
}
}