blob: 9e8475a3026629fc6f44de64ecf9be6ea2b92d9b [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")
19//! .flag("-std=c++11")
20//! .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;
61use crate::gen::Opt;
62use anyhow::anyhow;
63use std::fs;
64use 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]
75pub fn bridge(rust_source_file: impl AsRef<Path>) -> cc::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")
86/// .flag("-std=c++11")
87/// .compile("cxxbridge-demo");
88/// ```
89pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> cc::Build {
90 let mut build = paths::cc_build();
91 for path in rust_source_files {
92 if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) {
David Tolnayf8ed0732020-04-29 12:34:47 -070093 let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err));
94 process::exit(1);
95 }
96 }
David Tolnay6e808332020-05-07 19:43:56 -070097 build
David Tolnayf8ed0732020-04-29 12:34:47 -070098}
99
David Tolnay6e808332020-05-07 19:43:56 -0700100fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> {
David Tolnayf8ed0732020-04-29 12:34:47 -0700101 let header = gen::do_generate_header(rust_source_file, Opt::default());
102 let header_path = paths::out_with_extension(rust_source_file, ".h")?;
103 fs::create_dir_all(header_path.parent().unwrap())?;
104 fs::write(&header_path, header)?;
105 paths::symlink_header(&header_path, rust_source_file);
106
107 let bridge = gen::do_generate_bridge(rust_source_file, Opt::default());
108 let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?;
109 fs::write(&bridge_path, bridge)?;
David Tolnayf8ed0732020-04-29 12:34:47 -0700110 build.file(&bridge_path);
111
112 let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h");
113 let _ = fs::create_dir_all(cxx_h.parent().unwrap());
114 let _ = fs::remove_file(cxx_h);
115 let _ = fs::write(cxx_h, gen::include::HEADER);
116
David Tolnay6e808332020-05-07 19:43:56 -0700117 Ok(())
David Tolnayf8ed0732020-04-29 12:34:47 -0700118}