133d722a9Sopenharmony_ci//! The CXX code generator for constructing and compiling C++ code. 233d722a9Sopenharmony_ci//! 333d722a9Sopenharmony_ci//! This is intended to be used from Cargo build scripts to execute CXX's 433d722a9Sopenharmony_ci//! C++ code generator, set up any additional compiler flags depending on 533d722a9Sopenharmony_ci//! the use case, and make the C++ compiler invocation. 633d722a9Sopenharmony_ci//! 733d722a9Sopenharmony_ci//! <br> 833d722a9Sopenharmony_ci//! 933d722a9Sopenharmony_ci//! # Example 1033d722a9Sopenharmony_ci//! 1133d722a9Sopenharmony_ci//! Example of a canonical Cargo build script that builds a CXX bridge: 1233d722a9Sopenharmony_ci//! 1333d722a9Sopenharmony_ci//! ```no_run 1433d722a9Sopenharmony_ci//! // build.rs 1533d722a9Sopenharmony_ci//! 1633d722a9Sopenharmony_ci//! fn main() { 1733d722a9Sopenharmony_ci//! cxx_build::bridge("src/main.rs") 1833d722a9Sopenharmony_ci//! .file("src/demo.cc") 1933d722a9Sopenharmony_ci//! .flag_if_supported("-std=c++11") 2033d722a9Sopenharmony_ci//! .compile("cxxbridge-demo"); 2133d722a9Sopenharmony_ci//! 2233d722a9Sopenharmony_ci//! println!("cargo:rerun-if-changed=src/main.rs"); 2333d722a9Sopenharmony_ci//! println!("cargo:rerun-if-changed=src/demo.cc"); 2433d722a9Sopenharmony_ci//! println!("cargo:rerun-if-changed=include/demo.h"); 2533d722a9Sopenharmony_ci//! } 2633d722a9Sopenharmony_ci//! ``` 2733d722a9Sopenharmony_ci//! 2833d722a9Sopenharmony_ci//! A runnable working setup with this build script is shown in the *demo* 2933d722a9Sopenharmony_ci//! directory of [https://github.com/dtolnay/cxx]. 3033d722a9Sopenharmony_ci//! 3133d722a9Sopenharmony_ci//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx 3233d722a9Sopenharmony_ci//! 3333d722a9Sopenharmony_ci//! <br> 3433d722a9Sopenharmony_ci//! 3533d722a9Sopenharmony_ci//! # Alternatives 3633d722a9Sopenharmony_ci//! 3733d722a9Sopenharmony_ci//! For use in non-Cargo builds like Bazel or Buck, CXX provides an 3833d722a9Sopenharmony_ci//! alternate way of invoking the C++ code generator as a standalone command 3933d722a9Sopenharmony_ci//! line tool. The tool is packaged as the `cxxbridge-cmd` crate. 4033d722a9Sopenharmony_ci//! 4133d722a9Sopenharmony_ci//! ```bash 4233d722a9Sopenharmony_ci//! $ cargo install cxxbridge-cmd # or build it from the repo 4333d722a9Sopenharmony_ci//! 4433d722a9Sopenharmony_ci//! $ cxxbridge src/main.rs --header > path/to/mybridge.h 4533d722a9Sopenharmony_ci//! $ cxxbridge src/main.rs > path/to/mybridge.cc 4633d722a9Sopenharmony_ci//! ``` 4733d722a9Sopenharmony_ci 4833d722a9Sopenharmony_ci#![doc(html_root_url = "https://docs.rs/cxx-build/1.0.97")] 4933d722a9Sopenharmony_ci#![allow( 5033d722a9Sopenharmony_ci clippy::cast_sign_loss, 5133d722a9Sopenharmony_ci clippy::default_trait_access, 5233d722a9Sopenharmony_ci clippy::derive_partial_eq_without_eq, 5333d722a9Sopenharmony_ci clippy::doc_markdown, 5433d722a9Sopenharmony_ci clippy::drop_copy, 5533d722a9Sopenharmony_ci clippy::enum_glob_use, 5633d722a9Sopenharmony_ci clippy::explicit_auto_deref, 5733d722a9Sopenharmony_ci clippy::if_same_then_else, 5833d722a9Sopenharmony_ci clippy::inherent_to_string, 5933d722a9Sopenharmony_ci clippy::items_after_statements, 6033d722a9Sopenharmony_ci clippy::match_bool, 6133d722a9Sopenharmony_ci clippy::match_on_vec_items, 6233d722a9Sopenharmony_ci clippy::match_same_arms, 6333d722a9Sopenharmony_ci clippy::module_name_repetitions, 6433d722a9Sopenharmony_ci clippy::needless_doctest_main, 6533d722a9Sopenharmony_ci clippy::needless_pass_by_value, 6633d722a9Sopenharmony_ci clippy::new_without_default, 6733d722a9Sopenharmony_ci clippy::nonminimal_bool, 6833d722a9Sopenharmony_ci clippy::option_if_let_else, 6933d722a9Sopenharmony_ci clippy::or_fun_call, 7033d722a9Sopenharmony_ci clippy::redundant_else, 7133d722a9Sopenharmony_ci clippy::shadow_unrelated, 7233d722a9Sopenharmony_ci clippy::significant_drop_in_scrutinee, 7333d722a9Sopenharmony_ci clippy::similar_names, 7433d722a9Sopenharmony_ci clippy::single_match_else, 7533d722a9Sopenharmony_ci clippy::struct_excessive_bools, 7633d722a9Sopenharmony_ci clippy::too_many_arguments, 7733d722a9Sopenharmony_ci clippy::too_many_lines, 7833d722a9Sopenharmony_ci clippy::toplevel_ref_arg, 7933d722a9Sopenharmony_ci clippy::upper_case_acronyms, 8033d722a9Sopenharmony_ci // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983 8133d722a9Sopenharmony_ci clippy::wrong_self_convention 8233d722a9Sopenharmony_ci)] 8333d722a9Sopenharmony_ci 8433d722a9Sopenharmony_cimod cargo; 8533d722a9Sopenharmony_cimod cfg; 8633d722a9Sopenharmony_cimod deps; 8733d722a9Sopenharmony_cimod error; 8833d722a9Sopenharmony_cimod gen; 8933d722a9Sopenharmony_cimod intern; 9033d722a9Sopenharmony_cimod out; 9133d722a9Sopenharmony_cimod paths; 9233d722a9Sopenharmony_cimod syntax; 9333d722a9Sopenharmony_cimod target; 9433d722a9Sopenharmony_cimod vec; 9533d722a9Sopenharmony_ci 9633d722a9Sopenharmony_ciuse crate::cargo::CargoEnvCfgEvaluator; 9733d722a9Sopenharmony_ciuse crate::deps::{Crate, HeaderDir}; 9833d722a9Sopenharmony_ciuse crate::error::{Error, Result}; 9933d722a9Sopenharmony_ciuse crate::gen::error::report; 10033d722a9Sopenharmony_ciuse crate::gen::Opt; 10133d722a9Sopenharmony_ciuse crate::paths::PathExt; 10233d722a9Sopenharmony_ciuse crate::syntax::map::{Entry, UnorderedMap}; 10333d722a9Sopenharmony_ciuse crate::target::TargetDir; 10433d722a9Sopenharmony_ciuse cc::Build; 10533d722a9Sopenharmony_ciuse std::collections::BTreeSet; 10633d722a9Sopenharmony_ciuse std::env; 10733d722a9Sopenharmony_ciuse std::ffi::{OsStr, OsString}; 10833d722a9Sopenharmony_ciuse std::io::{self, Write}; 10933d722a9Sopenharmony_ciuse std::iter; 11033d722a9Sopenharmony_ciuse std::path::{Path, PathBuf}; 11133d722a9Sopenharmony_ciuse std::process; 11233d722a9Sopenharmony_ci 11333d722a9Sopenharmony_cipub use crate::cfg::{Cfg, CFG}; 11433d722a9Sopenharmony_ci 11533d722a9Sopenharmony_ci/// This returns a [`cc::Build`] on which you should continue to set up any 11633d722a9Sopenharmony_ci/// additional source files or compiler flags, and lastly call its [`compile`] 11733d722a9Sopenharmony_ci/// method to execute the C++ build. 11833d722a9Sopenharmony_ci/// 11933d722a9Sopenharmony_ci/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile 12033d722a9Sopenharmony_ci#[must_use] 12133d722a9Sopenharmony_cipub fn bridge(rust_source_file: impl AsRef<Path>) -> Build { 12233d722a9Sopenharmony_ci bridges(iter::once(rust_source_file)) 12333d722a9Sopenharmony_ci} 12433d722a9Sopenharmony_ci 12533d722a9Sopenharmony_ci/// `cxx_build::bridge` but for when more than one file contains a 12633d722a9Sopenharmony_ci/// #\[cxx::bridge\] module. 12733d722a9Sopenharmony_ci/// 12833d722a9Sopenharmony_ci/// ```no_run 12933d722a9Sopenharmony_ci/// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; 13033d722a9Sopenharmony_ci/// cxx_build::bridges(source_files) 13133d722a9Sopenharmony_ci/// .file("src/demo.cc") 13233d722a9Sopenharmony_ci/// .flag_if_supported("-std=c++11") 13333d722a9Sopenharmony_ci/// .compile("cxxbridge-demo"); 13433d722a9Sopenharmony_ci/// ``` 13533d722a9Sopenharmony_ci#[must_use] 13633d722a9Sopenharmony_cipub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> Build { 13733d722a9Sopenharmony_ci let ref mut rust_source_files = rust_source_files.into_iter(); 13833d722a9Sopenharmony_ci build(rust_source_files).unwrap_or_else(|err| { 13933d722a9Sopenharmony_ci let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err)); 14033d722a9Sopenharmony_ci process::exit(1); 14133d722a9Sopenharmony_ci }) 14233d722a9Sopenharmony_ci} 14333d722a9Sopenharmony_ci 14433d722a9Sopenharmony_cistruct Project { 14533d722a9Sopenharmony_ci include_prefix: PathBuf, 14633d722a9Sopenharmony_ci manifest_dir: PathBuf, 14733d722a9Sopenharmony_ci // The `links = "..."` value from Cargo.toml. 14833d722a9Sopenharmony_ci links_attribute: Option<OsString>, 14933d722a9Sopenharmony_ci // Output directory as received from Cargo. 15033d722a9Sopenharmony_ci out_dir: PathBuf, 15133d722a9Sopenharmony_ci // Directory into which to symlink all generated code. 15233d722a9Sopenharmony_ci // 15333d722a9Sopenharmony_ci // This is *not* used for an #include path, only as a debugging convenience. 15433d722a9Sopenharmony_ci // Normally available at target/cxxbridge/ if we are able to know where the 15533d722a9Sopenharmony_ci // target dir is, otherwise under a common scratch dir. 15633d722a9Sopenharmony_ci // 15733d722a9Sopenharmony_ci // The reason this isn't the #include dir is that we do not want builds to 15833d722a9Sopenharmony_ci // have access to headers from arbitrary other parts of the dependency 15933d722a9Sopenharmony_ci // graph. Using a global directory for all builds would be both a race 16033d722a9Sopenharmony_ci // condition depending on what order Cargo randomly executes the build 16133d722a9Sopenharmony_ci // scripts, as well as semantically undesirable for builds not to have to 16233d722a9Sopenharmony_ci // declare their real dependencies. 16333d722a9Sopenharmony_ci shared_dir: PathBuf, 16433d722a9Sopenharmony_ci} 16533d722a9Sopenharmony_ci 16633d722a9Sopenharmony_ciimpl Project { 16733d722a9Sopenharmony_ci fn init() -> Result<Self> { 16833d722a9Sopenharmony_ci let include_prefix = Path::new(CFG.include_prefix); 16933d722a9Sopenharmony_ci assert!(include_prefix.is_relative()); 17033d722a9Sopenharmony_ci let include_prefix = include_prefix.components().collect(); 17133d722a9Sopenharmony_ci 17233d722a9Sopenharmony_ci let links_attribute = env::var_os("CARGO_MANIFEST_LINKS"); 17333d722a9Sopenharmony_ci 17433d722a9Sopenharmony_ci let manifest_dir = paths::manifest_dir()?; 17533d722a9Sopenharmony_ci let out_dir = paths::out_dir()?; 17633d722a9Sopenharmony_ci 17733d722a9Sopenharmony_ci let shared_dir = match target::find_target_dir(&out_dir) { 17833d722a9Sopenharmony_ci TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), 17933d722a9Sopenharmony_ci TargetDir::Unknown => scratch::path("cxxbridge"), 18033d722a9Sopenharmony_ci }; 18133d722a9Sopenharmony_ci 18233d722a9Sopenharmony_ci Ok(Project { 18333d722a9Sopenharmony_ci include_prefix, 18433d722a9Sopenharmony_ci manifest_dir, 18533d722a9Sopenharmony_ci links_attribute, 18633d722a9Sopenharmony_ci out_dir, 18733d722a9Sopenharmony_ci shared_dir, 18833d722a9Sopenharmony_ci }) 18933d722a9Sopenharmony_ci } 19033d722a9Sopenharmony_ci} 19133d722a9Sopenharmony_ci 19233d722a9Sopenharmony_ci// We lay out the OUT_DIR as follows. Everything is namespaced under a cxxbridge 19333d722a9Sopenharmony_ci// subdirectory to avoid stomping on other things that the caller's build script 19433d722a9Sopenharmony_ci// might be doing inside OUT_DIR. 19533d722a9Sopenharmony_ci// 19633d722a9Sopenharmony_ci// $OUT_DIR/ 19733d722a9Sopenharmony_ci// cxxbridge/ 19833d722a9Sopenharmony_ci// crate/ 19933d722a9Sopenharmony_ci// $CARGO_PKG_NAME -> $CARGO_MANIFEST_DIR 20033d722a9Sopenharmony_ci// include/ 20133d722a9Sopenharmony_ci// rust/ 20233d722a9Sopenharmony_ci// cxx.h 20333d722a9Sopenharmony_ci// $CARGO_PKG_NAME/ 20433d722a9Sopenharmony_ci// .../ 20533d722a9Sopenharmony_ci// lib.rs.h 20633d722a9Sopenharmony_ci// sources/ 20733d722a9Sopenharmony_ci// $CARGO_PKG_NAME/ 20833d722a9Sopenharmony_ci// .../ 20933d722a9Sopenharmony_ci// lib.rs.cc 21033d722a9Sopenharmony_ci// 21133d722a9Sopenharmony_ci// The crate/ and include/ directories are placed on the #include path for the 21233d722a9Sopenharmony_ci// current build as well as for downstream builds that have a direct dependency 21333d722a9Sopenharmony_ci// on the current crate. 21433d722a9Sopenharmony_cifn build(rust_source_files: &mut dyn Iterator<Item = impl AsRef<Path>>) -> Result<Build> { 21533d722a9Sopenharmony_ci let ref prj = Project::init()?; 21633d722a9Sopenharmony_ci validate_cfg(prj)?; 21733d722a9Sopenharmony_ci let this_crate = make_this_crate(prj)?; 21833d722a9Sopenharmony_ci 21933d722a9Sopenharmony_ci let mut build = Build::new(); 22033d722a9Sopenharmony_ci build.cpp(true); 22133d722a9Sopenharmony_ci build.cpp_link_stdlib(None); // linked via link-cplusplus crate 22233d722a9Sopenharmony_ci 22333d722a9Sopenharmony_ci for path in rust_source_files { 22433d722a9Sopenharmony_ci generate_bridge(prj, &mut build, path.as_ref())?; 22533d722a9Sopenharmony_ci } 22633d722a9Sopenharmony_ci 22733d722a9Sopenharmony_ci this_crate.print_to_cargo(); 22833d722a9Sopenharmony_ci eprintln!("\nCXX include path:"); 22933d722a9Sopenharmony_ci for header_dir in this_crate.header_dirs { 23033d722a9Sopenharmony_ci build.include(&header_dir.path); 23133d722a9Sopenharmony_ci if header_dir.exported { 23233d722a9Sopenharmony_ci eprintln!(" {}", header_dir.path.display()); 23333d722a9Sopenharmony_ci } else { 23433d722a9Sopenharmony_ci eprintln!(" {} (private)", header_dir.path.display()); 23533d722a9Sopenharmony_ci } 23633d722a9Sopenharmony_ci } 23733d722a9Sopenharmony_ci 23833d722a9Sopenharmony_ci Ok(build) 23933d722a9Sopenharmony_ci} 24033d722a9Sopenharmony_ci 24133d722a9Sopenharmony_cifn validate_cfg(prj: &Project) -> Result<()> { 24233d722a9Sopenharmony_ci for exported_dir in &CFG.exported_header_dirs { 24333d722a9Sopenharmony_ci if !exported_dir.is_absolute() { 24433d722a9Sopenharmony_ci return Err(Error::ExportedDirNotAbsolute(exported_dir)); 24533d722a9Sopenharmony_ci } 24633d722a9Sopenharmony_ci } 24733d722a9Sopenharmony_ci 24833d722a9Sopenharmony_ci for prefix in &CFG.exported_header_prefixes { 24933d722a9Sopenharmony_ci if prefix.is_empty() { 25033d722a9Sopenharmony_ci return Err(Error::ExportedEmptyPrefix); 25133d722a9Sopenharmony_ci } 25233d722a9Sopenharmony_ci } 25333d722a9Sopenharmony_ci 25433d722a9Sopenharmony_ci if prj.links_attribute.is_none() { 25533d722a9Sopenharmony_ci if !CFG.exported_header_dirs.is_empty() { 25633d722a9Sopenharmony_ci return Err(Error::ExportedDirsWithoutLinks); 25733d722a9Sopenharmony_ci } 25833d722a9Sopenharmony_ci if !CFG.exported_header_prefixes.is_empty() { 25933d722a9Sopenharmony_ci return Err(Error::ExportedPrefixesWithoutLinks); 26033d722a9Sopenharmony_ci } 26133d722a9Sopenharmony_ci if !CFG.exported_header_links.is_empty() { 26233d722a9Sopenharmony_ci return Err(Error::ExportedLinksWithoutLinks); 26333d722a9Sopenharmony_ci } 26433d722a9Sopenharmony_ci } 26533d722a9Sopenharmony_ci 26633d722a9Sopenharmony_ci Ok(()) 26733d722a9Sopenharmony_ci} 26833d722a9Sopenharmony_ci 26933d722a9Sopenharmony_cifn make_this_crate(prj: &Project) -> Result<Crate> { 27033d722a9Sopenharmony_ci let crate_dir = make_crate_dir(prj); 27133d722a9Sopenharmony_ci let include_dir = make_include_dir(prj)?; 27233d722a9Sopenharmony_ci 27333d722a9Sopenharmony_ci let mut this_crate = Crate { 27433d722a9Sopenharmony_ci include_prefix: Some(prj.include_prefix.clone()), 27533d722a9Sopenharmony_ci links: prj.links_attribute.clone(), 27633d722a9Sopenharmony_ci header_dirs: Vec::new(), 27733d722a9Sopenharmony_ci }; 27833d722a9Sopenharmony_ci 27933d722a9Sopenharmony_ci // The generated code directory (include_dir) is placed in front of 28033d722a9Sopenharmony_ci // crate_dir on the include line so that `#include "path/to/file.rs"` from 28133d722a9Sopenharmony_ci // C++ "magically" works and refers to the API generated from that Rust 28233d722a9Sopenharmony_ci // source file. 28333d722a9Sopenharmony_ci this_crate.header_dirs.push(HeaderDir { 28433d722a9Sopenharmony_ci exported: true, 28533d722a9Sopenharmony_ci path: include_dir, 28633d722a9Sopenharmony_ci }); 28733d722a9Sopenharmony_ci 28833d722a9Sopenharmony_ci this_crate.header_dirs.push(HeaderDir { 28933d722a9Sopenharmony_ci exported: true, 29033d722a9Sopenharmony_ci path: crate_dir, 29133d722a9Sopenharmony_ci }); 29233d722a9Sopenharmony_ci 29333d722a9Sopenharmony_ci for exported_dir in &CFG.exported_header_dirs { 29433d722a9Sopenharmony_ci this_crate.header_dirs.push(HeaderDir { 29533d722a9Sopenharmony_ci exported: true, 29633d722a9Sopenharmony_ci path: PathBuf::from(exported_dir), 29733d722a9Sopenharmony_ci }); 29833d722a9Sopenharmony_ci } 29933d722a9Sopenharmony_ci 30033d722a9Sopenharmony_ci let mut header_dirs_index = UnorderedMap::new(); 30133d722a9Sopenharmony_ci let mut used_header_links = BTreeSet::new(); 30233d722a9Sopenharmony_ci let mut used_header_prefixes = BTreeSet::new(); 30333d722a9Sopenharmony_ci for krate in deps::direct_dependencies() { 30433d722a9Sopenharmony_ci let mut is_link_exported = || match &krate.links { 30533d722a9Sopenharmony_ci None => false, 30633d722a9Sopenharmony_ci Some(links_attribute) => CFG.exported_header_links.iter().any(|&exported| { 30733d722a9Sopenharmony_ci let matches = links_attribute == exported; 30833d722a9Sopenharmony_ci if matches { 30933d722a9Sopenharmony_ci used_header_links.insert(exported); 31033d722a9Sopenharmony_ci } 31133d722a9Sopenharmony_ci matches 31233d722a9Sopenharmony_ci }), 31333d722a9Sopenharmony_ci }; 31433d722a9Sopenharmony_ci 31533d722a9Sopenharmony_ci let mut is_prefix_exported = || match &krate.include_prefix { 31633d722a9Sopenharmony_ci None => false, 31733d722a9Sopenharmony_ci Some(include_prefix) => CFG.exported_header_prefixes.iter().any(|&exported| { 31833d722a9Sopenharmony_ci let matches = include_prefix.starts_with(exported); 31933d722a9Sopenharmony_ci if matches { 32033d722a9Sopenharmony_ci used_header_prefixes.insert(exported); 32133d722a9Sopenharmony_ci } 32233d722a9Sopenharmony_ci matches 32333d722a9Sopenharmony_ci }), 32433d722a9Sopenharmony_ci }; 32533d722a9Sopenharmony_ci 32633d722a9Sopenharmony_ci let exported = is_link_exported() || is_prefix_exported(); 32733d722a9Sopenharmony_ci 32833d722a9Sopenharmony_ci for dir in krate.header_dirs { 32933d722a9Sopenharmony_ci // Deduplicate dirs reachable via multiple transitive dependencies. 33033d722a9Sopenharmony_ci match header_dirs_index.entry(dir.path.clone()) { 33133d722a9Sopenharmony_ci Entry::Vacant(entry) => { 33233d722a9Sopenharmony_ci entry.insert(this_crate.header_dirs.len()); 33333d722a9Sopenharmony_ci this_crate.header_dirs.push(HeaderDir { 33433d722a9Sopenharmony_ci exported, 33533d722a9Sopenharmony_ci path: dir.path, 33633d722a9Sopenharmony_ci }); 33733d722a9Sopenharmony_ci } 33833d722a9Sopenharmony_ci Entry::Occupied(entry) => { 33933d722a9Sopenharmony_ci let index = *entry.get(); 34033d722a9Sopenharmony_ci this_crate.header_dirs[index].exported |= exported; 34133d722a9Sopenharmony_ci } 34233d722a9Sopenharmony_ci } 34333d722a9Sopenharmony_ci } 34433d722a9Sopenharmony_ci } 34533d722a9Sopenharmony_ci 34633d722a9Sopenharmony_ci if let Some(unused) = CFG 34733d722a9Sopenharmony_ci .exported_header_links 34833d722a9Sopenharmony_ci .iter() 34933d722a9Sopenharmony_ci .find(|&exported| !used_header_links.contains(exported)) 35033d722a9Sopenharmony_ci { 35133d722a9Sopenharmony_ci return Err(Error::UnusedExportedLinks(unused)); 35233d722a9Sopenharmony_ci } 35333d722a9Sopenharmony_ci 35433d722a9Sopenharmony_ci if let Some(unused) = CFG 35533d722a9Sopenharmony_ci .exported_header_prefixes 35633d722a9Sopenharmony_ci .iter() 35733d722a9Sopenharmony_ci .find(|&exported| !used_header_prefixes.contains(exported)) 35833d722a9Sopenharmony_ci { 35933d722a9Sopenharmony_ci return Err(Error::UnusedExportedPrefix(unused)); 36033d722a9Sopenharmony_ci } 36133d722a9Sopenharmony_ci 36233d722a9Sopenharmony_ci Ok(this_crate) 36333d722a9Sopenharmony_ci} 36433d722a9Sopenharmony_ci 36533d722a9Sopenharmony_cifn make_crate_dir(prj: &Project) -> PathBuf { 36633d722a9Sopenharmony_ci if prj.include_prefix.as_os_str().is_empty() { 36733d722a9Sopenharmony_ci return prj.manifest_dir.clone(); 36833d722a9Sopenharmony_ci } 36933d722a9Sopenharmony_ci let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); 37033d722a9Sopenharmony_ci let ref link = crate_dir.join(&prj.include_prefix); 37133d722a9Sopenharmony_ci let ref manifest_dir = prj.manifest_dir; 37233d722a9Sopenharmony_ci if out::symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) { 37333d722a9Sopenharmony_ci let cachedir_tag = "\ 37433d722a9Sopenharmony_ci Signature: 8a477f597d28d172789f06886806bc55\n\ 37533d722a9Sopenharmony_ci # This file is a cache directory tag created by cxx.\n\ 37633d722a9Sopenharmony_ci # For information about cache directory tags see https://bford.info/cachedir/\n"; 37733d722a9Sopenharmony_ci let _ = out::write(crate_dir.join("CACHEDIR.TAG"), cachedir_tag.as_bytes()); 37833d722a9Sopenharmony_ci let max_depth = 6; 37933d722a9Sopenharmony_ci best_effort_copy_headers(manifest_dir, link, max_depth); 38033d722a9Sopenharmony_ci } 38133d722a9Sopenharmony_ci crate_dir 38233d722a9Sopenharmony_ci} 38333d722a9Sopenharmony_ci 38433d722a9Sopenharmony_cifn make_include_dir(prj: &Project) -> Result<PathBuf> { 38533d722a9Sopenharmony_ci let include_dir = prj.out_dir.join("cxxbridge").join("include"); 38633d722a9Sopenharmony_ci let cxx_h = include_dir.join("rust").join("cxx.h"); 38733d722a9Sopenharmony_ci let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h"); 38833d722a9Sopenharmony_ci if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") { 38933d722a9Sopenharmony_ci out::symlink_file(original, cxx_h)?; 39033d722a9Sopenharmony_ci out::symlink_file(original, shared_cxx_h)?; 39133d722a9Sopenharmony_ci } else { 39233d722a9Sopenharmony_ci out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?; 39333d722a9Sopenharmony_ci out::symlink_file(shared_cxx_h, cxx_h)?; 39433d722a9Sopenharmony_ci } 39533d722a9Sopenharmony_ci Ok(include_dir) 39633d722a9Sopenharmony_ci} 39733d722a9Sopenharmony_ci 39833d722a9Sopenharmony_cifn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { 39933d722a9Sopenharmony_ci let opt = Opt { 40033d722a9Sopenharmony_ci allow_dot_includes: false, 40133d722a9Sopenharmony_ci cfg_evaluator: Box::new(CargoEnvCfgEvaluator), 40233d722a9Sopenharmony_ci doxygen: CFG.doxygen, 40333d722a9Sopenharmony_ci ..Opt::default() 40433d722a9Sopenharmony_ci }; 40533d722a9Sopenharmony_ci let generated = gen::generate_from_path(rust_source_file, &opt); 40633d722a9Sopenharmony_ci let ref rel_path = paths::local_relative_path(rust_source_file); 40733d722a9Sopenharmony_ci 40833d722a9Sopenharmony_ci let cxxbridge = prj.out_dir.join("cxxbridge"); 40933d722a9Sopenharmony_ci let include_dir = cxxbridge.join("include").join(&prj.include_prefix); 41033d722a9Sopenharmony_ci let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix); 41133d722a9Sopenharmony_ci 41233d722a9Sopenharmony_ci let ref rel_path_h = rel_path.with_appended_extension(".h"); 41333d722a9Sopenharmony_ci let ref header_path = include_dir.join(rel_path_h); 41433d722a9Sopenharmony_ci out::write(header_path, &generated.header)?; 41533d722a9Sopenharmony_ci 41633d722a9Sopenharmony_ci let ref link_path = include_dir.join(rel_path); 41733d722a9Sopenharmony_ci let _ = out::symlink_file(header_path, link_path); 41833d722a9Sopenharmony_ci 41933d722a9Sopenharmony_ci let ref rel_path_cc = rel_path.with_appended_extension(".cc"); 42033d722a9Sopenharmony_ci let ref implementation_path = sources_dir.join(rel_path_cc); 42133d722a9Sopenharmony_ci out::write(implementation_path, &generated.implementation)?; 42233d722a9Sopenharmony_ci build.file(implementation_path); 42333d722a9Sopenharmony_ci 42433d722a9Sopenharmony_ci let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h); 42533d722a9Sopenharmony_ci let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc); 42633d722a9Sopenharmony_ci let _ = out::symlink_file(header_path, shared_h); 42733d722a9Sopenharmony_ci let _ = out::symlink_file(implementation_path, shared_cc); 42833d722a9Sopenharmony_ci Ok(()) 42933d722a9Sopenharmony_ci} 43033d722a9Sopenharmony_ci 43133d722a9Sopenharmony_cifn best_effort_copy_headers(src: &Path, dst: &Path, max_depth: usize) { 43233d722a9Sopenharmony_ci // Not using crate::gen::fs because we aren't reporting the errors. 43333d722a9Sopenharmony_ci use std::fs; 43433d722a9Sopenharmony_ci 43533d722a9Sopenharmony_ci let mut dst_created = false; 43633d722a9Sopenharmony_ci let mut entries = match fs::read_dir(src) { 43733d722a9Sopenharmony_ci Ok(entries) => entries, 43833d722a9Sopenharmony_ci Err(_) => return, 43933d722a9Sopenharmony_ci }; 44033d722a9Sopenharmony_ci 44133d722a9Sopenharmony_ci while let Some(Ok(entry)) = entries.next() { 44233d722a9Sopenharmony_ci let file_name = entry.file_name(); 44333d722a9Sopenharmony_ci if file_name.to_string_lossy().starts_with('.') { 44433d722a9Sopenharmony_ci continue; 44533d722a9Sopenharmony_ci } 44633d722a9Sopenharmony_ci match entry.file_type() { 44733d722a9Sopenharmony_ci Ok(file_type) if file_type.is_dir() && max_depth > 0 => { 44833d722a9Sopenharmony_ci let src = entry.path(); 44933d722a9Sopenharmony_ci if src.join("Cargo.toml").exists() || src.join("CACHEDIR.TAG").exists() { 45033d722a9Sopenharmony_ci continue; 45133d722a9Sopenharmony_ci } 45233d722a9Sopenharmony_ci let dst = dst.join(file_name); 45333d722a9Sopenharmony_ci best_effort_copy_headers(&src, &dst, max_depth - 1); 45433d722a9Sopenharmony_ci } 45533d722a9Sopenharmony_ci Ok(file_type) if file_type.is_file() => { 45633d722a9Sopenharmony_ci let src = entry.path(); 45733d722a9Sopenharmony_ci match src.extension().and_then(OsStr::to_str) { 45833d722a9Sopenharmony_ci Some("h") | Some("hh") | Some("hpp") => {} 45933d722a9Sopenharmony_ci _ => continue, 46033d722a9Sopenharmony_ci } 46133d722a9Sopenharmony_ci if !dst_created && fs::create_dir_all(dst).is_err() { 46233d722a9Sopenharmony_ci return; 46333d722a9Sopenharmony_ci } 46433d722a9Sopenharmony_ci dst_created = true; 46533d722a9Sopenharmony_ci let dst = dst.join(file_name); 46633d722a9Sopenharmony_ci let _ = fs::remove_file(&dst); 46733d722a9Sopenharmony_ci let _ = fs::copy(src, dst); 46833d722a9Sopenharmony_ci } 46933d722a9Sopenharmony_ci _ => {} 47033d722a9Sopenharmony_ci } 47133d722a9Sopenharmony_ci } 47233d722a9Sopenharmony_ci} 47333d722a9Sopenharmony_ci 47433d722a9Sopenharmony_cifn env_os(key: impl AsRef<OsStr>) -> Result<OsString> { 47533d722a9Sopenharmony_ci let key = key.as_ref(); 47633d722a9Sopenharmony_ci env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned())) 47733d722a9Sopenharmony_ci} 478