Regenerate QEMU binding stubs only for newer versions of the nightly compiler (#2177)

* Regenerate binding stubs only for newer versions of the nightly compiler.

* fmt

* clippy
This commit is contained in:
Romain Malmain 2024-05-15 01:42:59 +02:00 committed by GitHub
parent 19087f3dab
commit b127f0579d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 102 additions and 20 deletions

View File

@ -37,3 +37,4 @@ pkg-config = "0.3.26"
cc = "1.0"
regex = "1"
rustversion = "1.0"
rustc_version = "0.4"

View File

@ -95,7 +95,7 @@ pub fn generate(
) -> Result<Bindings, BindgenError> {
let wrapper_h = build_dir.join("wrapper.h");
store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes());
store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes(), None, None, false);
let bindings = bindgen::Builder::default()
.derive_debug(true)

View File

@ -6,12 +6,14 @@ use std::{
env, fs,
fs::File,
hash::Hasher,
io::{Read, Seek, SeekFrom, Write},
io::{BufRead, BufReader, Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
process::Command,
ptr::addr_of_mut,
};
use regex::Regex;
use rustc_version::Version;
use which::which;
mod bindings;
@ -243,26 +245,37 @@ fn include_path(build_dir: &Path, path: &str) -> String {
}
}
pub fn store_generated_content_if_different(file_to_update: &PathBuf, fresh_content: &[u8]) {
/// If `fresh_content` != `content_file_to_update` (the file is read directly if `content_file_to_update` is None), update the file. prefix is not considered for comparison.
/// If a prefix is given, it will be added as the first line of the file.
pub fn store_generated_content_if_different(
file_to_update: &PathBuf,
fresh_content: &[u8],
content_file_to_update: Option<Vec<u8>>,
first_line_prefix: Option<&str>,
force_regeneration: bool,
) {
let mut must_rewrite_file = true;
// Check if equivalent file already exists without relying on filesystem timestamp.
let mut file_to_check =
if let Ok(mut wrapper_file) = File::options().read(true).write(true).open(file_to_update) {
let mut existing_file_content = Vec::with_capacity(fresh_content.len());
wrapper_file
.read_to_end(existing_file_content.as_mut())
.unwrap();
let existing_file_content = content_file_to_update.unwrap_or_else(|| {
let mut content = Vec::with_capacity(fresh_content.len());
wrapper_file.read_to_end(content.as_mut()).unwrap();
content
});
let mut existing_wrapper_hasher = hash_map::DefaultHasher::new();
existing_wrapper_hasher.write(existing_file_content.as_ref());
if !force_regeneration {
let mut existing_wrapper_hasher = hash_map::DefaultHasher::new();
existing_wrapper_hasher.write(existing_file_content.as_ref());
let mut wrapper_h_hasher = hash_map::DefaultHasher::new();
wrapper_h_hasher.write(fresh_content);
let mut wrapper_h_hasher = hash_map::DefaultHasher::new();
wrapper_h_hasher.write(fresh_content);
// Check if wrappers are the same
if existing_wrapper_hasher.finish() == wrapper_h_hasher.finish() {
must_rewrite_file = false;
// Check if wrappers are the same
if existing_wrapper_hasher.finish() == wrapper_h_hasher.finish() {
must_rewrite_file = false;
}
}
// Reset file cursor if it's going to be rewritten
@ -280,6 +293,10 @@ pub fn store_generated_content_if_different(file_to_update: &PathBuf, fresh_cont
};
if must_rewrite_file {
if let Some(prefix) = first_line_prefix {
writeln!(&file_to_check, "{prefix}").expect("Could not write prefix");
}
file_to_check
.write_all(fresh_content)
.unwrap_or_else(|_| panic!("Unable to write in {}", file_to_update.display()));
@ -293,12 +310,74 @@ pub fn maybe_generate_stub_bindings(
bindings_file: &PathBuf,
) {
if cpu_target == "x86_64" && emulation_mode == "usermode" {
store_generated_content_if_different(
stub_bindings_file,
fs::read(bindings_file)
.expect("Could not read generated bindings file")
.as_slice(),
);
let current_rustc_version =
rustc_version::version().expect("Could not get current rustc version");
let semver_re = Regex::new(r"/\* (.*) \*/").unwrap();
// We only try to store the stub if the current rustc version is strictly bigger than the one used to generate
// the versioned stub.
let (try_generate, force_regeneration, stub_content): (bool, bool, Option<Vec<u8>>) =
if let Ok(stub_file) = File::open(stub_bindings_file) {
let mut stub_rdr = BufReader::new(stub_file);
let mut first_line = String::new();
let mut stub_content = Vec::<u8>::new();
assert!(
stub_rdr
.read_line(&mut first_line)
.expect("Could not read first line")
> 0,
"Error while reading first line."
);
if let Some((_, [version_str])) = semver_re
.captures_iter(&first_line)
.next()
.map(|caps| caps.extract())
{
// The first line matches the regex
if let Ok(version) = Version::parse(version_str) {
// The first line contains a version
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(current_rustc_version > version, false, Some(stub_content))
} else {
stub_rdr.seek(SeekFrom::Start(0)).unwrap();
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(true, true, Some(stub_content))
}
} else {
stub_rdr.seek(SeekFrom::Start(0)).unwrap();
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(true, true, Some(stub_content))
}
} else {
// No stub file stored
(true, true, None)
};
let header = format!("/* {current_rustc_version} */");
if try_generate {
store_generated_content_if_different(
stub_bindings_file,
fs::read(bindings_file)
.expect("Could not read generated bindings file")
.as_slice(),
stub_content,
Some(header.as_str()),
force_regeneration,
);
}
}
}

View File

@ -1,3 +1,4 @@
/* 1.80.0-nightly */
/* automatically generated by rust-bindgen 0.69.4 */
#[repr(C)]

View File

@ -1,3 +1,4 @@
/* 1.80.0-nightly */
/* automatically generated by rust-bindgen 0.69.4 */
pub const _STDINT_H: u32 = 1;