You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

build.rs 5.4 kB

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