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