diff options
Diffstat (limited to 'gen/build')
-rw-r--r-- | gen/build/Cargo.toml | 19 | ||||
-rw-r--r-- | gen/build/src/deps.rs | 8 | ||||
-rw-r--r-- | gen/build/src/intern.rs | 6 | ||||
-rw-r--r-- | gen/build/src/lib.rs | 26 | ||||
-rw-r--r-- | gen/build/src/out.rs | 229 | ||||
-rw-r--r-- | gen/build/src/paths.rs | 23 | ||||
-rw-r--r-- | gen/build/src/vec.rs | 6 |
7 files changed, 257 insertions, 60 deletions
diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 9b74255b..ee20e2b7 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "cxx-build" -version = "1.0.93" +version = "1.0.119" authors = ["David Tolnay <dtolnay@gmail.com>"] categories = ["development-tools::build-utils", "development-tools::ffi"] description = "C++ code generator for integrating `cxx` crate into a Cargo build." documentation = "https://docs.rs/cxx-build" -edition = "2018" +edition = "2021" exclude = ["build.rs"] homepage = "https://cxx.rs" keywords = ["ffi", "build-dependencies"] @@ -19,21 +19,22 @@ parallel = ["cc/parallel"] experimental-async-fn = [] [dependencies] -cc = "1.0.49" +cc = "1.0.83" codespan-reporting = "0.11.1" -once_cell = "1.9" -proc-macro2 = { version = "1.0.39", default-features = false, features = ["span-locations"] } -quote = { version = "1.0", default-features = false } -scratch = "1.0" -syn = { version = "2.0.1", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } +once_cell = "1.18" +proc-macro2 = { version = "1.0.74", default-features = false, features = ["span-locations"] } +quote = { version = "1.0.35", default-features = false } +scratch = "1.0.5" +syn = { version = "2.0.46", default-features = false, features = ["clone-impls", "full", "parsing", "printing"] } [dev-dependencies] cxx = { version = "1.0", path = "../.." } cxx-gen = { version = "0.7", path = "../lib" } -pkg-config = "0.3" +pkg-config = "0.3.27" [lib] doc-scrape-examples = false [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--generate-link-to-definition"] diff --git a/gen/build/src/deps.rs b/gen/build/src/deps.rs index fb80072c..36f2066a 100644 --- a/gen/build/src/deps.rs +++ b/gen/build/src/deps.rs @@ -4,19 +4,19 @@ use std::ffi::OsString; use std::path::PathBuf; #[derive(Default)] -pub struct Crate { +pub(crate) struct Crate { pub include_prefix: Option<PathBuf>, pub links: Option<OsString>, pub header_dirs: Vec<HeaderDir>, } -pub struct HeaderDir { +pub(crate) struct HeaderDir { pub exported: bool, pub path: PathBuf, } impl Crate { - pub fn print_to_cargo(&self) { + pub(crate) fn print_to_cargo(&self) { if let Some(include_prefix) = &self.include_prefix { println!( "cargo:CXXBRIDGE_PREFIX={}", @@ -38,7 +38,7 @@ impl Crate { } } -pub fn direct_dependencies() -> Vec<Crate> { +pub(crate) fn direct_dependencies() -> Vec<Crate> { let mut crates: BTreeMap<String, Crate> = BTreeMap::new(); let mut exported_header_dirs: BTreeMap<String, Vec<(usize, PathBuf)>> = BTreeMap::new(); diff --git a/gen/build/src/intern.rs b/gen/build/src/intern.rs index c8b57d89..753e3f31 100644 --- a/gen/build/src/intern.rs +++ b/gen/build/src/intern.rs @@ -3,15 +3,15 @@ use once_cell::sync::OnceCell; use std::sync::{Mutex, PoisonError}; #[derive(Copy, Clone, Default)] -pub struct InternedString(&'static str); +pub(crate) struct InternedString(&'static str); impl InternedString { - pub fn str(self) -> &'static str { + pub(crate) fn str(self) -> &'static str { self.0 } } -pub fn intern(s: &str) -> InternedString { +pub(crate) fn intern(s: &str) -> InternedString { static INTERN: OnceCell<Mutex<Set<&'static str>>> = OnceCell::new(); let mut set = INTERN diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index b6b843a6..4fcb9459 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -45,17 +45,17 @@ //! $ cxxbridge src/main.rs > path/to/mybridge.cc //! ``` -#![doc(html_root_url = "https://docs.rs/cxx-build/1.0.93")] +#![doc(html_root_url = "https://docs.rs/cxx-build/1.0.119")] #![allow( clippy::cast_sign_loss, clippy::default_trait_access, clippy::derive_partial_eq_without_eq, clippy::doc_markdown, - clippy::drop_copy, clippy::enum_glob_use, clippy::explicit_auto_deref, clippy::if_same_then_else, clippy::inherent_to_string, + clippy::into_iter_without_iter, clippy::items_after_statements, clippy::match_bool, clippy::match_on_vec_items, @@ -65,7 +65,6 @@ clippy::needless_pass_by_value, clippy::new_without_default, clippy::nonminimal_bool, - clippy::option_if_let_else, clippy::or_fun_call, clippy::redundant_else, clippy::shadow_unrelated, @@ -73,12 +72,13 @@ clippy::similar_names, clippy::single_match_else, clippy::struct_excessive_bools, + clippy::struct_field_names, clippy::too_many_arguments, clippy::too_many_lines, clippy::toplevel_ref_arg, + clippy::unconditional_recursion, // clippy bug: https://github.com/rust-lang/rust-clippy/issues/12133 + clippy::uninlined_format_args, clippy::upper_case_acronyms, - // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983 - clippy::wrong_self_convention )] mod cargo; @@ -369,7 +369,7 @@ fn make_crate_dir(prj: &Project) -> PathBuf { let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); let ref link = crate_dir.join(&prj.include_prefix); let ref manifest_dir = prj.manifest_dir; - if out::symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) { + if out::relative_symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) { let cachedir_tag = "\ Signature: 8a477f597d28d172789f06886806bc55\n\ # This file is a cache directory tag created by cxx.\n\ @@ -386,11 +386,11 @@ fn make_include_dir(prj: &Project) -> Result<PathBuf> { let cxx_h = include_dir.join("rust").join("cxx.h"); let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h"); if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") { - out::symlink_file(original, cxx_h)?; - out::symlink_file(original, shared_cxx_h)?; + out::absolute_symlink_file(original, cxx_h)?; + out::absolute_symlink_file(original, shared_cxx_h)?; } else { out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?; - out::symlink_file(shared_cxx_h, cxx_h)?; + out::relative_symlink_file(shared_cxx_h, cxx_h)?; } Ok(include_dir) } @@ -414,7 +414,7 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> out::write(header_path, &generated.header)?; let ref link_path = include_dir.join(rel_path); - let _ = out::symlink_file(header_path, link_path); + let _ = out::relative_symlink_file(header_path, link_path); let ref rel_path_cc = rel_path.with_appended_extension(".cc"); let ref implementation_path = sources_dir.join(rel_path_cc); @@ -423,8 +423,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h); let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc); - let _ = out::symlink_file(header_path, shared_h); - let _ = out::symlink_file(implementation_path, shared_cc); + let _ = out::relative_symlink_file(header_path, shared_h); + let _ = out::relative_symlink_file(implementation_path, shared_cc); Ok(()) } @@ -455,7 +455,7 @@ fn best_effort_copy_headers(src: &Path, dst: &Path, max_depth: usize) { Ok(file_type) if file_type.is_file() => { let src = entry.path(); match src.extension().and_then(OsStr::to_str) { - Some("h") | Some("hh") | Some("hpp") => {} + Some("h" | "hh" | "hpp") => {} _ => continue, } if !dst_created && fs::create_dir_all(dst).is_err() { diff --git a/gen/build/src/out.rs b/gen/build/src/out.rs index a52aab25..0095666f 100644 --- a/gen/build/src/out.rs +++ b/gen/build/src/out.rs @@ -1,8 +1,8 @@ use crate::error::{Error, Result}; use crate::gen::fs; use crate::paths; -use std::io; -use std::path::Path; +use std::path::{Component, Path, PathBuf}; +use std::{env, io}; pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> { let path = path.as_ref(); @@ -29,19 +29,61 @@ pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> { } } -pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> { +pub(crate) fn relative_symlink_file( + original: impl AsRef<Path>, + link: impl AsRef<Path>, +) -> Result<()> { let original = original.as_ref(); let link = link.as_ref(); - let mut create_dir_error = None; + let parent_directory_error = prepare_parent_directory_for_symlink(link).err(); + let relativized = best_effort_relativize_symlink(original, link); + + symlink_file(&relativized, original, link, parent_directory_error) +} + +pub(crate) fn absolute_symlink_file( + original: impl AsRef<Path>, + link: impl AsRef<Path>, +) -> Result<()> { + let original = original.as_ref(); + let link = link.as_ref(); + + let parent_directory_error = prepare_parent_directory_for_symlink(link).err(); + + symlink_file(original, original, link, parent_directory_error) +} + +pub(crate) fn relative_symlink_dir( + original: impl AsRef<Path>, + link: impl AsRef<Path>, +) -> Result<()> { + let original = original.as_ref(); + let link = link.as_ref(); + + let parent_directory_error = prepare_parent_directory_for_symlink(link).err(); + let relativized = best_effort_relativize_symlink(original, link); + + symlink_dir(&relativized, link, parent_directory_error) +} + +fn prepare_parent_directory_for_symlink(link: &Path) -> fs::Result<()> { if fs::exists(link) { best_effort_remove(link); + Ok(()) } else { let parent = link.parent().unwrap(); - create_dir_error = fs::create_dir_all(parent).err(); + fs::create_dir_all(parent) } +} - match paths::symlink_or_copy(original, link) { +fn symlink_file( + path_for_symlink: &Path, + path_for_copy: &Path, + link: &Path, + parent_directory_error: Option<fs::Error>, +) -> Result<()> { + match paths::symlink_or_copy(path_for_symlink, path_for_copy, link) { // As long as symlink_or_copy succeeded, ignore any create_dir_all error. Ok(()) => Ok(()), Err(err) => { @@ -57,29 +99,22 @@ pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) - } else { // If create_dir_all and symlink_or_copy both failed, prefer the // first error. - Err(Error::Fs(create_dir_error.unwrap_or(err))) + Err(Error::Fs(parent_directory_error.unwrap_or(err))) } } } } -pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> { - let original = original.as_ref(); - let link = link.as_ref(); - - let mut create_dir_error = None; - if fs::exists(link) { - best_effort_remove(link); - } else { - let parent = link.parent().unwrap(); - create_dir_error = fs::create_dir_all(parent).err(); - } - - match fs::symlink_dir(original, link) { +fn symlink_dir( + path_for_symlink: &Path, + link: &Path, + parent_directory_error: Option<fs::Error>, +) -> Result<()> { + match fs::symlink_dir(path_for_symlink, link) { // As long as symlink_dir succeeded, ignore any create_dir_all error. Ok(()) => Ok(()), // If create_dir_all and symlink_dir both failed, prefer the first error. - Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), + Err(err) => Err(Error::Fs(parent_directory_error.unwrap_or(err))), } } @@ -117,3 +152,155 @@ fn best_effort_remove(path: &Path) { } } } + +fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> PathBuf { + let original = original.as_ref(); + let link = link.as_ref(); + + let relative_path = match abstractly_relativize_symlink(original, link) { + Some(relative_path) => relative_path, + None => return original.to_path_buf(), + }; + + // Sometimes "a/b/../c" refers to a different canonical location than "a/c". + // This can happen if 'b' is a symlink. The '..' canonicalizes to the parent + // directory of the symlink's target, not back to 'a'. In cxx-build's case + // someone could be using `--target-dir` with a location containing such + // symlinks. + if let Ok(original_canonical) = original.canonicalize() { + if let Ok(relative_canonical) = link.parent().unwrap().join(&relative_path).canonicalize() { + if original_canonical == relative_canonical { + return relative_path; + } + } + } + + original.to_path_buf() +} + +fn abstractly_relativize_symlink( + original: impl AsRef<Path>, + link: impl AsRef<Path>, +) -> Option<PathBuf> { + let original = original.as_ref(); + let link = link.as_ref(); + + // Relativization only makes sense if there is a semantically meaningful + // base directory shared between the two paths. + // + // For example /Volumes/code/library/src/lib.rs + // and /Volumes/code/library/target/path/to/something.a + // have a meaningful shared base of /Volumes/code/library. The target and + // source directory only likely ever get relocated as one unit. + // + // On the other hand, /Volumes/code/library/src/lib.rs + // and /Volumes/shared_target + // do not, since upon moving library to a different location it should + // continue referring to the original location of that shared Cargo target + // directory. + let likely_no_semantic_prefix = env::var_os("CARGO_TARGET_DIR").is_some(); + + if likely_no_semantic_prefix + || original.is_relative() + || link.is_relative() + || path_contains_intermediate_components(original) + || path_contains_intermediate_components(link) + { + return None; + } + + let (common_prefix, rest_of_original, rest_of_link) = split_after_common_prefix(original, link); + + if common_prefix == Path::new("") { + return None; + } + + let mut rest_of_link = rest_of_link.components(); + rest_of_link + .next_back() + .expect("original can't be a subdirectory of link"); + + let mut path_to_common_prefix = PathBuf::new(); + while rest_of_link.next_back().is_some() { + path_to_common_prefix.push(Component::ParentDir); + } + + Some(path_to_common_prefix.join(rest_of_original)) +} + +fn path_contains_intermediate_components(path: impl AsRef<Path>) -> bool { + path.as_ref() + .components() + .any(|component| component == Component::ParentDir) +} + +fn split_after_common_prefix<'first, 'second>( + first: &'first Path, + second: &'second Path, +) -> (&'first Path, &'first Path, &'second Path) { + let entire_first = first; + let mut first = first.components(); + let mut second = second.components(); + loop { + let rest_of_first = first.as_path(); + let rest_of_second = second.as_path(); + match (first.next(), second.next()) { + (Some(first_component), Some(second_component)) + if first_component == second_component => {} + _ => { + let mut common_prefix = entire_first; + for _ in rest_of_first.components().rev() { + if let Some(parent) = common_prefix.parent() { + common_prefix = parent; + } else { + common_prefix = Path::new(""); + break; + } + } + return (common_prefix, rest_of_first, rest_of_second); + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::out::abstractly_relativize_symlink; + use std::path::Path; + + #[cfg(not(windows))] + #[test] + fn test_relativize_symlink_unix() { + assert_eq!( + abstractly_relativize_symlink("/foo/bar/baz", "/foo/spam/eggs").as_deref(), + Some(Path::new("../bar/baz")), + ); + assert_eq!( + abstractly_relativize_symlink("/foo/bar/../baz", "/foo/spam/eggs"), + None, + ); + assert_eq!( + abstractly_relativize_symlink("/foo/bar/baz", "/foo/spam/./eggs").as_deref(), + Some(Path::new("../bar/baz")), + ); + } + + #[cfg(windows)] + #[test] + fn test_relativize_symlink_windows() { + use std::path::PathBuf; + + let windows_target = PathBuf::from_iter(["c:\\", "windows", "foo"]); + let windows_link = PathBuf::from_iter(["c:\\", "users", "link"]); + let windows_different_volume_link = PathBuf::from_iter(["d:\\", "users", "link"]); + + assert_eq!( + abstractly_relativize_symlink(&windows_target, windows_link).as_deref(), + Some(Path::new("..\\windows\\foo")), + ); + assert_eq!( + abstractly_relativize_symlink(&windows_target, windows_different_volume_link), + None, + ); + } +} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index c514a570..53445dee 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -40,28 +40,37 @@ impl PathExt for Path { } #[cfg(unix)] -pub(crate) use self::fs::symlink_file as symlink_or_copy; +pub(crate) fn symlink_or_copy( + path_for_symlink: impl AsRef<Path>, + _path_for_copy: impl AsRef<Path>, + link: impl AsRef<Path>, +) -> fs::Result<()> { + fs::symlink_file(path_for_symlink, link) +} #[cfg(windows)] pub(crate) fn symlink_or_copy( - original: impl AsRef<Path>, + path_for_symlink: impl AsRef<Path>, + path_for_copy: impl AsRef<Path>, link: impl AsRef<Path>, ) -> fs::Result<()> { // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. - let original = original.as_ref(); + let path_for_symlink = path_for_symlink.as_ref(); let link = link.as_ref(); - if fs::symlink_file(original, link).is_err() { - fs::copy(original, link)?; + if fs::symlink_file(path_for_symlink, link).is_err() { + let path_for_copy = path_for_copy.as_ref(); + fs::copy(path_for_copy, link)?; } Ok(()) } #[cfg(not(any(unix, windows)))] pub(crate) fn symlink_or_copy( - original: impl AsRef<Path>, + _path_for_symlink: impl AsRef<Path>, + path_for_copy: impl AsRef<Path>, copy: impl AsRef<Path>, ) -> fs::Result<()> { - fs::copy(original, copy)?; + fs::copy(path_for_copy, copy)?; Ok(()) } diff --git a/gen/build/src/vec.rs b/gen/build/src/vec.rs index ac9235ec..ccc98955 100644 --- a/gen/build/src/vec.rs +++ b/gen/build/src/vec.rs @@ -1,7 +1,7 @@ use crate::intern::{self, InternedString}; use std::path::Path; -pub trait InternedVec<T> +pub(crate) trait InternedVec<T> where T: ?Sized, { @@ -17,14 +17,14 @@ where } } -pub fn intern<T>(elements: &[&T]) -> Vec<InternedString> +pub(crate) fn intern<T>(elements: &[&T]) -> Vec<InternedString> where T: ?Sized + Element, { elements.iter().copied().map(Element::intern).collect() } -pub trait Element { +pub(crate) trait Element { fn intern(&self) -> InternedString; fn unintern(_: InternedString) -> &'static Self; } |