• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::BTreeMap;
2 use std::env;
3 use std::ffi::OsString;
4 use std::path::PathBuf;
5 
6 #[derive(Default)]
7 pub struct Crate {
8     pub include_prefix: Option<PathBuf>,
9     pub links: Option<OsString>,
10     pub header_dirs: Vec<HeaderDir>,
11 }
12 
13 pub struct HeaderDir {
14     pub exported: bool,
15     pub path: PathBuf,
16 }
17 
18 impl Crate {
print_to_cargo(&self)19     pub fn print_to_cargo(&self) {
20         if let Some(include_prefix) = &self.include_prefix {
21             println!(
22                 "cargo:CXXBRIDGE_PREFIX={}",
23                 include_prefix.to_string_lossy(),
24             );
25         }
26         if let Some(links) = &self.links {
27             println!("cargo:CXXBRIDGE_LINKS={}", links.to_string_lossy());
28         }
29         for (i, header_dir) in self.header_dirs.iter().enumerate() {
30             if header_dir.exported {
31                 println!(
32                     "cargo:CXXBRIDGE_DIR{}={}",
33                     i,
34                     header_dir.path.to_string_lossy(),
35                 );
36             }
37         }
38     }
39 }
40 
direct_dependencies() -> Vec<Crate>41 pub fn direct_dependencies() -> Vec<Crate> {
42     let mut crates: BTreeMap<String, Crate> = BTreeMap::new();
43     let mut exported_header_dirs: BTreeMap<String, Vec<(usize, PathBuf)>> = BTreeMap::new();
44 
45     // Only variables set from a build script of direct dependencies are
46     // observable. That's exactly what we want! Your crate needs to declare a
47     // direct dependency on the other crate in order to be able to #include its
48     // headers.
49     //
50     // Also, they're only observable if the dependency's manifest contains a
51     // `links` key. This is important because Cargo imposes no ordering on the
52     // execution of build scripts without a `links` key. When exposing a
53     // generated header for the current crate to #include, we need to be sure
54     // the dependency's build script has already executed and emitted that
55     // generated header.
56     //
57     // References:
58     //   - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
59     //   - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate
60     for (k, v) in env::vars_os() {
61         let mut k = k.to_string_lossy().into_owned();
62         if !k.starts_with("DEP_") {
63             continue;
64         }
65 
66         if k.ends_with("_CXXBRIDGE_PREFIX") {
67             k.truncate(k.len() - "_CXXBRIDGE_PREFIX".len());
68             crates.entry(k).or_default().include_prefix = Some(PathBuf::from(v));
69             continue;
70         }
71 
72         if k.ends_with("_CXXBRIDGE_LINKS") {
73             k.truncate(k.len() - "_CXXBRIDGE_LINKS".len());
74             crates.entry(k).or_default().links = Some(v);
75             continue;
76         }
77 
78         let without_counter = k.trim_end_matches(|ch: char| ch.is_ascii_digit());
79         let counter_len = k.len() - without_counter.len();
80         if counter_len == 0 || !without_counter.ends_with("_CXXBRIDGE_DIR") {
81             continue;
82         }
83 
84         let sort_key = k[k.len() - counter_len..]
85             .parse::<usize>()
86             .unwrap_or(usize::MAX);
87         k.truncate(k.len() - counter_len - "_CXXBRIDGE_DIR".len());
88         exported_header_dirs
89             .entry(k)
90             .or_default()
91             .push((sort_key, PathBuf::from(v)));
92     }
93 
94     for (k, mut dirs) in exported_header_dirs {
95         dirs.sort_by_key(|(sort_key, _dir)| *sort_key);
96         crates
97             .entry(k)
98             .or_default()
99             .header_dirs
100             .extend(dirs.into_iter().map(|(_sort_key, dir)| HeaderDir {
101                 exported: true,
102                 path: dir,
103             }));
104     }
105 
106     crates.into_iter().map(|entry| entry.1).collect()
107 }
108