Auto merge of #14754 - upsuper:skip-bindgen, r=emilio

Skip invoking bindgen if no header changes

This can avoid doing bindgen when build script is called for updating other files, e.g. properties.

This uses a global modified time, so there is a chance that some of the files which can be skipped but not skipped. But given that we do all three files in parallel, that would unlikely affect the actual runtime.

Using lots of `Mutex` could be an issue, but it doesn't seem to be in practice. Since only one thread would hold the lock of `ADDED_PATHS`, there is never a competitor for the lock of `LAST_MODIFIED`.

r? @emilio

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14754)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-12-28 04:51:53 -08:00 committed by GitHub
commit 9e0d269353

View file

@ -4,10 +4,15 @@
mod common { mod common {
use std::env; use std::env;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::SystemTime;
lazy_static! { lazy_static! {
pub static ref OUTDIR_PATH: PathBuf = PathBuf::from(env::var("OUT_DIR").unwrap()).join("gecko"); pub static ref OUTDIR_PATH: PathBuf = PathBuf::from(env::var("OUT_DIR").unwrap()).join("gecko");
pub static ref LAST_MODIFIED: Mutex<SystemTime> =
Mutex::new(get_modified_time(&env::current_exe().unwrap())
.expect("Failed to get modified time of executable"));
} }
pub const STRUCTS_DEBUG_FILE: &'static str = "structs_debug.rs"; pub const STRUCTS_DEBUG_FILE: &'static str = "structs_debug.rs";
@ -26,16 +31,21 @@ mod common {
BuildType::Release => STRUCTS_RELEASE_FILE BuildType::Release => STRUCTS_RELEASE_FILE
} }
} }
pub fn get_modified_time(file: &Path) -> Option<SystemTime> {
file.metadata().and_then(|m| m.modified()).ok()
}
} }
#[cfg(feature = "bindgen")] #[cfg(feature = "bindgen")]
mod bindings { mod bindings {
use libbindgen::{Builder, CodegenConfig}; use libbindgen::{Builder, CodegenConfig};
use regex::Regex; use regex::Regex;
use std::cmp;
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::{BufWriter, Read, Write}; use std::io::{Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Mutex; use std::sync::Mutex;
use super::common::*; use super::common::*;
@ -59,9 +69,13 @@ mod bindings {
fn search_include(name: &str) -> Option<PathBuf> { fn search_include(name: &str) -> Option<PathBuf> {
for path in SEARCH_PATHS.iter() { for path in SEARCH_PATHS.iter() {
let file = path.join(name); let file = path.join(name);
if file.is_file() { if !file.is_file() {
return Some(file); continue;
} }
let modified = get_modified_time(&file).unwrap();
let mut last_modified = LAST_MODIFIED.lock().unwrap();
*last_modified = cmp::max(modified, *last_modified);
return Some(file);
} }
None None
} }
@ -178,10 +192,25 @@ mod bindings {
} }
} }
fn write_binding_file(builder: Builder, file: &str) { struct Fixup {
let bindings = builder.generate().expect("Unable to generate bindings"); pat: String,
let binding_file = File::create(&OUTDIR_PATH.join(file)).unwrap(); rep: String
bindings.write(Box::new(BufWriter::new(binding_file))).expect("Unable to write output"); }
fn write_binding_file(builder: Builder, file: &str, fixups: &[Fixup]) {
let out_file = OUTDIR_PATH.join(file);
if let Some(modified) = get_modified_time(&out_file) {
// Don't generate the file if nothing it depends on was modified.
let last_modified = LAST_MODIFIED.lock().unwrap();
if *last_modified <= modified {
return;
}
}
let mut result = builder.generate().expect("Unable to generate bindings").to_string();
for fixup in fixups.iter() {
result = Regex::new(&format!(r"\b{}\b", fixup.pat)).unwrap().replace_all(&result, fixup.rep.as_str());
}
File::create(&out_file).unwrap().write_all(&result.into_bytes()).expect("Unable to write output");
} }
pub fn generate_structs(build_type: BuildType) { pub fn generate_structs(build_type: BuildType) {
@ -390,10 +419,6 @@ mod bindings {
servo: "AtomicRefCell<ElementData>", servo: "AtomicRefCell<ElementData>",
} }
]; ];
struct Fixup {
pat: String,
rep: String
}
let mut fixups = vec![ let mut fixups = vec![
Fixup { Fixup {
pat: "root::nsString".into(), pat: "root::nsString".into(),
@ -419,12 +444,7 @@ mod bindings {
rep: format!("::gecko_bindings::structs::{}", gecko_name) rep: format!("::gecko_bindings::structs::{}", gecko_name)
}); });
} }
let mut result = builder.generate().expect("Unable to generate bindings").to_string(); write_binding_file(builder, structs_file(build_type), &fixups);
for fixup in fixups.iter() {
result = Regex::new(&format!(r"\b{}\b", fixup.pat)).unwrap().replace_all(&result, fixup.rep.as_str());
}
File::create(&OUTDIR_PATH.join(structs_file(build_type))).unwrap()
.write_all(&result.into_bytes()).unwrap();
} }
pub fn generate_bindings() { pub fn generate_bindings() {
@ -585,7 +605,7 @@ mod bindings {
// type with zero_size_type. If we ever introduce immutable borrow types // type with zero_size_type. If we ever introduce immutable borrow types
// which _do_ need to be opaque, we'll need a separate mode. // which _do_ need to be opaque, we'll need a separate mode.
} }
write_binding_file(builder, BINDINGS_FILE); write_binding_file(builder, BINDINGS_FILE, &Vec::new());
} }
} }