Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cpp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@
//!
//! Behind the scene, it uses the `cc` crate.
//!
//! `cpp_build` tells cargo to rerun the build script when one of the Rust source files it
//! parses changes. It doesn't track C++ headers included from `cpp!` macros, or files added
//! with `cpp_build::Config::file`, so print `cargo:rerun-if-changed` for those yourself:
//!
//! ```no_run
//! fn main() {
//! cpp_build::build("src/lib.rs");
//! println!("cargo:rerun-if-changed=src/header.h");
//! }
//! ```
//!
//! ## Using external libraries
//!
//! Most likely you will want to link against external libraries. You need to tell cpp_build
Expand Down
27 changes: 22 additions & 5 deletions cpp_build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,8 @@ Failed to create output object directory."#,
/// `cc::Build` object.
pub struct Config {
cc: cc::Build,
std_flag_set: bool, // true if the -std flag was specified
std_flag_set: bool, // true if the -std flag was specified
cargo_metadata: bool, // false if cargo metadata output was disabled
}

impl Default for Config {
Expand All @@ -411,10 +412,14 @@ impl Default for Config {
/// Convert from a preconfigured [`cc::Build`] object to [`Config`].
/// Note that this will ensure C++ is enabled and add the proper include
/// directories to work with `cpp`.
///
/// If the [`cc::Build`] has `cargo_metadata(false)`, also call
/// [`Config::cargo_metadata`] with `false`. cpp_build can't read that setting
/// from `cc`, so otherwise it still prints `cargo:rerun-if-changed` lines.
impl From<cc::Build> for Config {
fn from(mut cc: cc::Build) -> Self {
cc.cpp(true).include(cargo_manifest_dir());
Self { cc, std_flag_set: false }
Self { cc, std_flag_set: false, cargo_metadata: true }
}
}

Expand Down Expand Up @@ -593,9 +598,10 @@ impl Config {
}

/// Define whether metadata should be emitted for cargo allowing it to
/// automatically link the binary. Defaults to `true`.
/// automatically link the binary, and to rerun the build script when a
/// parsed Rust source file changes. Defaults to `true`.
pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Self {
// XXX: Use this to control the cargo metadata which rust-cpp produces
self.cargo_metadata = cargo_metadata;
self.cc.cargo_metadata(cargo_metadata);
self
}
Expand Down Expand Up @@ -639,7 +645,18 @@ impl Config {

// Parse the crate
let mut visitor = parser::Parser::default();
if let Err(err) = visitor.parse_crate(crate_root.as_ref().to_owned()) {
let result = visitor.parse_crate(crate_root.as_ref().to_owned());

// `cc` prints `rerun-if-env-changed` lines, and any `rerun-if` line stops Cargo from
// rerunning the build script when a file in the package changes. So tell Cargo about
// every parsed file, otherwise new or changed `cpp!` macros are not extracted.
if self.cargo_metadata {
for path in &visitor.source_files {
println!("cargo:rerun-if-changed={}", path.display());
}
}

if let Err(err) = result {
warnln!(
r#"-- rust-cpp parse error --
There was an error parsing the crate for the rust-cpp build script:
Expand Down
43 changes: 42 additions & 1 deletion cpp_build/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,45 @@ fn test_cursor_advance() -> Result<(), LexError> {
Ok(())
}

#[test]
fn test_source_files() {
// A crate with every module layout that the parser follows, and a module that is disabled
// by a feature, whose file doesn't exist.
let lib = r#"
mod adjacent;
mod dir;
mod inline {
mod nested;
}
#[path = "renamed.rs"]
mod with_path;
#[cfg(feature = "cpp_build_test_disabled")]
mod disabled;
"#;
let files = [
("lib.rs", lib),
("adjacent.rs", "mod child;"),
("adjacent/child.rs", ""),
("dir/mod.rs", ""),
("inline/nested.rs", ""),
("renamed.rs", ""),
];
let root = std::env::temp_dir().join(format!("cpp_build_source_files_{}", std::process::id()));
for (file, source) in files {
let path = root.join(file);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, source).unwrap();
}

let mut parser = Parser::default();
let result = parser.parse_crate(root.join("lib.rs"));
std::fs::remove_dir_all(&root).unwrap();
result.unwrap();

let expected: Vec<PathBuf> = files.iter().map(|(file, _)| root.join(file)).collect();
assert_eq!(parser.source_files, expected);
}

fn line_directive(path: &Path, cur: Cursor) -> String {
let mut line =
format!("#line {} \"{}\"\n", cur.line + 1, path.to_string_lossy().replace('\\', "\\\\"));
Expand All @@ -311,7 +350,8 @@ pub struct Parser {
pub classes: Vec<Class>,
pub snippets: String,
pub callbacks_count: u32,
current_path: PathBuf, // The current file being parsed
pub source_files: Vec<PathBuf>, // Every file that was read while parsing the crate
current_path: PathBuf, // The current file being parsed
mod_dir: PathBuf,
mod_error: Option<Error>, // An error occuring while visiting the modules
}
Expand All @@ -323,6 +363,7 @@ impl Parser {
}

fn parse_mod(&mut self, mod_path: PathBuf, submod_dir: PathBuf) -> Result<(), Error> {
self.source_files.push(mod_path.clone());
let mut s = String::new();
let mut f = File::open(&mod_path).map_err(|_| Error::ParseCannotOpenFile {
src_path: mod_path.to_str().unwrap().to_owned(),
Expand Down