blob: c7d452feba78a312e0cbe60beb8a414393fd437a [file] [log] [blame]
David Tolnayf8ed0732020-04-29 12:34:47 -07001//! The CXX code generator for constructing and compiling C++ code.
2//!
3//! This is intended to be used from Cargo build scripts to execute CXX's
4//! C++ code generator, set up any additional compiler flags depending on
5//! the use case, and make the C++ compiler invocation.
6//!
7//! <br>
8//!
9//! # Example
10//!
11//! Example of a canonical Cargo build script that builds a CXX bridge:
12//!
13//! ```no_run
14//! // build.rs
15//!
16//! fn main() {
17//! cxx_build::bridge("src/main.rs")
18//! .file("../demo-cxx/demo.cc")
Philip Craig7e14e2e2020-05-09 10:42:30 +010019//! .flag_if_supported("-std=c++11")
David Tolnayf8ed0732020-04-29 12:34:47 -070020//! .compile("cxxbridge-demo");
21//!
22//! println!("cargo:rerun-if-changed=src/main.rs");
23//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h");
24//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
25//! }
26//! ```
27//!
28//! A runnable working setup with this build script is shown in the
29//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx].
30//!
31//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx
32//!
33//! <br>
34//!
35//! # Alternatives
36//!
37//! For use in non-Cargo builds like Bazel or Buck, CXX provides an
38//! alternate way of invoking the C++ code generator as a standalone command
39//! line tool. The tool is packaged as the `cxxbridge-cmd` crate.
40//!
41//! ```bash
42//! $ cargo install cxxbridge-cmd # or build it from the repo
43//!
44//! $ cxxbridge src/main.rs --header > path/to/mybridge.h
45//! $ cxxbridge src/main.rs > path/to/mybridge.cc
46//! ```
47
David Tolnaydb450b02020-05-05 10:16:57 -070048#![allow(
49 clippy::inherent_to_string,
50 clippy::needless_doctest_main,
51 clippy::new_without_default,
52 clippy::toplevel_ref_arg
53)]
54
David Tolnayf8ed0732020-04-29 12:34:47 -070055mod error;
56mod gen;
57mod paths;
58mod syntax;
59
60use crate::error::Result;
David Tolnaybb3ba502020-08-30 19:59:41 -070061use crate::gen::error::report;
David Tolnay0d85ccd2020-08-30 20:18:13 -070062use crate::gen::{fs, Opt};
David Tolnayf574c5e2020-08-31 00:22:27 -070063use cc::Build;
David Tolnayf8ed0732020-04-29 12:34:47 -070064use std::io::{self, Write};
David Tolnay6e808332020-05-07 19:43:56 -070065use std::iter;
David Tolnayf8ed0732020-04-29 12:34:47 -070066use std::path::Path;
67use std::process;
68
69/// This returns a [`cc::Build`] on which you should continue to set up any
70/// additional source files or compiler flags, and lastly call its [`compile`]
71/// method to execute the C++ build.
72///
73/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile
74#[must_use]
David Tolnayf574c5e2020-08-31 00:22:27 -070075pub fn bridge(rust_source_file: impl AsRef<Path>) -> Build {
David Tolnay6e808332020-05-07 19:43:56 -070076 bridges(iter::once(rust_source_file))
77}
78
79/// `cxx_build::bridge` but for when more than one file contains a
80/// #\[cxx::bridge\] module.
81///
82/// ```no_run
83/// let source_files = vec!["src/main.rs", "src/path/to/other.rs"];
84/// cxx_build::bridges(source_files)
85/// .file("../demo-cxx/demo.cc")
Philip Craig7e14e2e2020-05-09 10:42:30 +010086/// .flag_if_supported("-std=c++11")
David Tolnay6e808332020-05-07 19:43:56 -070087/// .compile("cxxbridge-demo");
88/// ```
David Tolnayf574c5e2020-08-31 00:22:27 -070089pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> Build {
David Tolnay6e808332020-05-07 19:43:56 -070090 let mut build = paths::cc_build();
David Tolnay110df7d2020-05-08 13:06:04 -070091 build.cpp(true);
92 build.cpp_link_stdlib(None); // linked via link-cplusplus crate
93
David Tolnay6e808332020-05-07 19:43:56 -070094 for path in rust_source_files {
95 if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) {
David Tolnaybb3ba502020-08-30 19:59:41 -070096 let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err));
David Tolnayf8ed0732020-04-29 12:34:47 -070097 process::exit(1);
98 }
99 }
David Tolnay110df7d2020-05-08 13:06:04 -0700100
David Tolnay6e808332020-05-07 19:43:56 -0700101 build
David Tolnayf8ed0732020-04-29 12:34:47 -0700102}
103
David Tolnayf574c5e2020-08-31 00:22:27 -0700104fn try_generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> {
David Tolnaya5cca312020-08-29 23:40:04 -0700105 let opt = Opt::default();
David Tolnay8238d4a2020-08-30 00:34:17 -0700106 let generated = gen::generate_from_path(rust_source_file, &opt);
107
David Tolnayf8ed0732020-04-29 12:34:47 -0700108 let header_path = paths::out_with_extension(rust_source_file, ".h")?;
109 fs::create_dir_all(header_path.parent().unwrap())?;
David Tolnay8238d4a2020-08-30 00:34:17 -0700110 fs::write(&header_path, generated.header)?;
David Tolnayf8ed0732020-04-29 12:34:47 -0700111 paths::symlink_header(&header_path, rust_source_file);
112
David Tolnay8238d4a2020-08-30 00:34:17 -0700113 let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?;
114 fs::write(&implementation_path, generated.implementation)?;
115 build.file(&implementation_path);
David Tolnayf8ed0732020-04-29 12:34:47 -0700116
117 let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h");
118 let _ = fs::create_dir_all(cxx_h.parent().unwrap());
119 let _ = fs::remove_file(cxx_h);
120 let _ = fs::write(cxx_h, gen::include::HEADER);
121
David Tolnay6e808332020-05-07 19:43:56 -0700122 Ok(())
David Tolnayf8ed0732020-04-29 12:34:47 -0700123}