blob: e318664bdb1f54945f004733a1d9715adab91978 [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::error::{Error, Result};
2use std::env;
3use std::fs;
4use std::os;
5use std::path::{Path, PathBuf};
6
7fn out_dir() -> Result<PathBuf> {
8 env::var_os("OUT_DIR")
9 .map(PathBuf::from)
10 .ok_or(Error::MissingOutDir)
11}
12
13pub(crate) fn cc_build() -> cc::Build {
14 try_cc_build().unwrap_or_default()
15}
16
17fn try_cc_build() -> Result<cc::Build> {
David Tolnay7db73692019-10-20 14:51:12 -040018 let mut build = cc::Build::new();
David Tolnayc43627a2020-01-28 00:50:25 -080019 build.include(include_dir()?);
20 build.include(target_dir()?.parent().unwrap());
David Tolnay7db73692019-10-20 14:51:12 -040021 Ok(build)
22}
23
24// Symlink the header file into a predictable place. The header generated from
25// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.h.
26pub(crate) fn symlink_header(path: &Path, original: &Path) {
27 let _ = try_symlink_header(path, original);
28}
29
30fn try_symlink_header(path: &Path, original: &Path) -> Result<()> {
31 let suffix = relative_to_parent_of_target_dir(original)?;
David Tolnayc43627a2020-01-28 00:50:25 -080032 let ref dst = include_dir()?.join(suffix);
David Tolnay7db73692019-10-20 14:51:12 -040033
34 fs::create_dir_all(dst.parent().unwrap())?;
David Tolnay0c88e032020-01-28 00:43:51 -080035 let _ = fs::remove_file(dst);
David Tolnay7db73692019-10-20 14:51:12 -040036 #[cfg(unix)]
37 os::unix::fs::symlink(path, dst)?;
38 #[cfg(windows)]
39 os::windows::fs::symlink_file(path, dst)?;
40
41 Ok(())
42}
43
44fn relative_to_parent_of_target_dir(original: &Path) -> Result<PathBuf> {
45 let target_dir = target_dir()?;
David Tolnay015c5e82020-01-26 16:31:48 -080046 let mut outer = target_dir.parent().unwrap();
David Tolnayfa1a2bd2020-02-24 21:58:17 -080047 let original = canonicalize(original)?;
David Tolnay015c5e82020-01-26 16:31:48 -080048 loop {
49 if let Ok(suffix) = original.strip_prefix(outer) {
50 return Ok(suffix.to_owned());
51 }
52 match outer.parent() {
53 Some(parent) => outer = parent,
54 None => return Ok(original.components().skip(1).collect()),
55 }
56 }
David Tolnay7db73692019-10-20 14:51:12 -040057}
58
59pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result<PathBuf> {
60 let mut file_name = path.file_name().unwrap().to_owned();
61 file_name.push(ext);
62
63 let out_dir = out_dir()?;
64 let rel = relative_to_parent_of_target_dir(path)?;
65 Ok(out_dir.join(rel).with_file_name(file_name))
66}
67
David Tolnayc43627a2020-01-28 00:50:25 -080068pub(crate) fn include_dir() -> Result<PathBuf> {
69 let target_dir = target_dir()?;
70 Ok(target_dir.join("cxxbridge"))
71}
72
David Tolnay7db73692019-10-20 14:51:12 -040073fn target_dir() -> Result<PathBuf> {
David Tolnayfa1a2bd2020-02-24 21:58:17 -080074 let mut dir = out_dir().and_then(canonicalize)?;
David Tolnay7db73692019-10-20 14:51:12 -040075 loop {
76 if dir.ends_with("target") {
77 return Ok(dir);
78 }
79 if !dir.pop() {
80 return Err(Error::TargetDir);
81 }
82 }
83}
David Tolnayfa1a2bd2020-02-24 21:58:17 -080084
85#[cfg(not(windows))]
86fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
87 Ok(fs::canonicalize(path)?)
88}
89
90#[cfg(windows)]
91fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
92 // Real fs::canonicalize on Windows produces UNC paths which cl.exe is
93 // unable to handle in includes. Use a poor approximation instead.
94 // https://github.com/rust-lang/rust/issues/42869
95 // https://github.com/alexcrichton/cc-rs/issues/169
96 Ok(env::current_dir()?.join(path))
97}