From a502e2132cb947a0c7dc2828a9dc2c613eb967ec Mon Sep 17 00:00:00 2001 From: Conrad Kramer Date: Mon, 5 Jun 2023 03:51:41 -0400 Subject: [PATCH] Only run build script when necessary The build script should only run if one of the generated files is changed. --- tun/build.rs | 147 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 37 deletions(-) diff --git a/tun/build.rs b/tun/build.rs index dd3ea28..5569cc4 100644 --- a/tun/build.rs +++ b/tun/build.rs @@ -1,41 +1,97 @@ +#[cfg(not(windows))] +fn main() { + println!("cargo:rerun-if-changed=build.rs"); +} + #[cfg(windows)] #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { - use std::io::{Cursor, Read}; + let out_dir = std::env::var("OUT_DIR")?; + let path = std::path::PathBuf::from(out_dir); + generate(&path).await?; - let buf = reqwest::get("https://www.wintun.net/builds/wintun-0.14.1.zip") - .await? - .bytes() - .await?; + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} - ssri::IntegrityChecker::new("sha256-B8JWGF1u42UuCfpVwLZz4mJLVl4CxLkJHHnKfS8k71E=".parse()?) - .chain(&buf) +#[cfg(windows)] +async fn generate(out_dir: &std::path::Path) -> anyhow::Result<()> { + use std::{fs::File, io::Write}; + + use anyhow::Context; + + let bindings_path = out_dir.join("wintun.rs"); + let binary_path = out_dir.join("wintun.dll"); + println!("cargo:rerun-if-changed={}", bindings_path.to_str().unwrap()); + println!("cargo:rerun-if-changed={}", binary_path.to_str().unwrap()); + + if let (Ok(..), Ok(..)) = (File::open(&bindings_path), File::open(&binary_path)) { + return Ok(()); + }; + + let archive = download(out_dir) + .await + .context("Failed to download wintun")?; + + let (bindings, binary) = parse(archive).context("Failed to parse wintun archive")?; + + bindings + .write_to_file(bindings_path) + .context("Failed to write bindings")?; + + let mut file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .open(binary_path) + .context("Failed to write binary")?; + file.write_all(&binary)?; + + Ok(()) +} + +#[cfg(windows)] +async fn download(directory: &std::path::Path) -> anyhow::Result { + use std::{io::Write, str::FromStr}; + + let path = directory.join(WINTUN_FILENAME); + let mut file = match std::fs::OpenOptions::new().read(true).open(&path) { + Ok(existing) => return Ok(existing), + Err(_e) => std::fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(path)?, + }; + + let mut url = reqwest::Url::from_str("https://www.wintun.net/builds")?; + url.path_segments_mut().unwrap().push(WINTUN_FILENAME); + + let body = reqwest::get(url).await?.bytes().await?; + + ssri::IntegrityChecker::new(WINTUN_INTEGRITY.parse()?) + .chain(&body) .result()?; - let mut archive = zip::ZipArchive::new(Cursor::new(buf))?; + file.set_len(0)?; + file.write_all(&body)?; - let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?); + Ok(file) +} + +#[cfg(windows)] +fn parse(file: std::fs::File) -> anyhow::Result<(bindgen::Bindings, Vec)> { + use anyhow::Context; + use std::io::Read; + + let reader = std::io::BufReader::new(file); + let mut archive = zip::ZipArchive::new(reader)?; let mut header = String::new(); archive .by_name("wintun/include/wintun.h")? .read_to_string(&mut header)?; - header.push_str( - "WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter; - WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter; - WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID; - WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion; - WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver; - WINTUN_SET_LOGGER_FUNC WintunSetLogger; - WINTUN_START_SESSION_FUNC WintunStartSession; - WINTUN_END_SESSION_FUNC WintunEndSession; - WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter; - WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent; - WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket; - WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket; - WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket; - WINTUN_SEND_PACKET_FUNC WintunSendPacket;", - ); + header.push_str(WINTUN_BINDINGS_PREAMBLE); + let bindings = bindgen::Builder::default() .header_contents("wintun.h", &header) .allowlist_function("Wintun.*") @@ -43,12 +99,12 @@ async fn main() -> anyhow::Result<()> { .dynamic_library_name("wintun") .dynamic_link_require_all(true) .generate() + .context("Failed to generate bindings from wintun archive") .unwrap(); - bindings.write_to_file(out_dir.join("wintun.rs"))?; - let mut library = Vec::new(); + let mut binary = Vec::new(); let target = std::env::var("TARGET")?; - let arch = match target.split("-").next() { + let arch = match target.split('-').next() { Some("i686") => "x86", Some("x86_64") => "amd64", Some("aarch64") => "arm64", @@ -58,15 +114,32 @@ async fn main() -> anyhow::Result<()> { }; archive .by_name(&format!("wintun/bin/{}/wintun.dll", arch))? - .read_to_end(&mut library)?; - std::fs::write(out_dir.join("wintun.dll"), library)?; + .read_to_end(&mut binary) + .context("Failed to read binary from wintun archive")?; - println!("cargo:rerun-if-changed=build.rs"); - - Ok(()) + Ok((bindings, binary)) } -#[cfg(not(windows))] -fn main() { - println!("cargo:rerun-if-changed=build.rs"); -} +#[cfg(windows)] +const WINTUN_FILENAME: &str = "wintun-0.14.1.zip"; + +#[cfg(windows)] +const WINTUN_INTEGRITY: &str = "sha256-B8JWGF1u42UuCfpVwLZz4mJLVl4CxLkJHHnKfS8k71E="; + +#[cfg(windows)] +const WINTUN_BINDINGS_PREAMBLE: &str = r#" +WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter; +WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter; +WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID; +WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion; +WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver; +WINTUN_SET_LOGGER_FUNC WintunSetLogger; +WINTUN_START_SESSION_FUNC WintunStartSession; +WINTUN_END_SESSION_FUNC WintunEndSession; +WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter; +WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent; +WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket; +WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket; +WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket; +WINTUN_SEND_PACKET_FUNC WintunSendPacket; +"#;