1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
//! The CXX code generator for constructing and compiling C++ code. //! //! This is intended to be used from Cargo build scripts to execute CXX's //! C++ code generator, set up any additional compiler flags depending on //! the use case, and make the C++ compiler invocation. //! //! <br> //! //! # Example //! //! Example of a canonical Cargo build script that builds a CXX bridge: //! //! ```no_run //! // build.rs //! //! fn main() { //! cxx_build::bridge("src/main.rs") //! .file("../demo-cxx/demo.cc") //! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); //! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); //! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); //! } //! ``` //! //! A runnable working setup with this build script is shown in the //! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx //! //! <br> //! //! # Alternatives //! //! For use in non-Cargo builds like Bazel or Buck, CXX provides an //! alternate way of invoking the C++ code generator as a standalone command //! line tool. The tool is packaged as the `cxxbridge-cmd` crate. //! //! ```bash //! $ cargo install cxxbridge-cmd # or build it from the repo //! //! $ cxxbridge src/main.rs --header > path/to/mybridge.h //! $ cxxbridge src/main.rs > path/to/mybridge.cc //! ``` #![allow( clippy::inherent_to_string, clippy::needless_doctest_main, clippy::new_without_default, clippy::toplevel_ref_arg )] mod error; mod gen; mod paths; mod syntax; use crate::error::Result; use crate::gen::Opt; use anyhow::anyhow; use std::fs; use std::io::{self, Write}; use std::iter; use std::path::Path; use std::process; /// This returns a [`cc::Build`] on which you should continue to set up any /// additional source files or compiler flags, and lastly call its [`compile`] /// method to execute the C++ build. /// /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile #[must_use] pub fn bridge(rust_source_file: impl AsRef<Path>) -> cc::Build { bridges(iter::once(rust_source_file)) } /// `cxx_build::bridge` but for when more than one file contains a /// #\[cxx::bridge\] module. /// /// ```no_run /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; /// cxx_build::bridges(source_files) /// .file("../demo-cxx/demo.cc") /// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> cc::Build { let mut build = paths::cc_build(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate for path in rust_source_files { if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) { let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); process::exit(1); } } build } fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); let header_path = paths::out_with_extension(rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; fs::write(&header_path, generated.header)?; paths::symlink_header(&header_path, rust_source_file); let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?; fs::write(&implementation_path, generated.implementation)?; build.file(&implementation_path); let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); let _ = fs::create_dir_all(cxx_h.parent().unwrap()); let _ = fs::remove_file(cxx_h); let _ = fs::write(cxx_h, gen::include::HEADER); Ok(()) }