From 7862726e84382ab4575937f3f2120ab541ba4780 Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 17:57:28 -0700 Subject: [PATCH 1/7] Use ureq to download node.lib --- build/Cargo.toml | 3 +++ build/src/lib.rs | 41 +++++++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/build/Cargo.toml b/build/Cargo.toml index d05b0d32..75029464 100644 --- a/build/Cargo.toml +++ b/build/Cargo.toml @@ -11,3 +11,6 @@ version = "0.2.1" [dependencies] cfg-if = "1.0" + +[target.'cfg(windows)'.dependencies] +ureq = { version = "1.5.0", default-features = false, features = [ "native-tls" ] } diff --git a/build/src/lib.rs b/build/src/lib.rs index 11fa666f..47b8e665 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -8,25 +8,34 @@ cfg_if! { if #[cfg(windows)] { use std::fs::{create_dir, metadata, write}; use std::path::PathBuf; + use std::io::Read; - fn download_node_lib() -> Vec { - let script = format!(" - require('https').get('https://nodejs.org/dist/' + process.version + '/win-x64/node.lib', (res) => {{ - res.pipe(process.stdout) - }})"); + fn get_node_version() -> std::io::Result { + let output = Command::new("node").arg("-v").output()?; + let stdout_str = String::from_utf8_lossy(&output.stdout); - Command::new("node") - .arg("-e") - .arg(script) - .output() - .expect("Download node.lib failed") - .stdout + // version should not have a leading "v" or trailing whitespace + Ok(stdout_str.trim().trim_start_matches('v').to_string()) + } + + fn download_node_lib(version: &str) -> Vec { + let url = format!("https://nodejs.org/dist/v{version}/win-x64/node.lib", version = version); + + let response = ureq::get(&url).call(); + if let Some(error) = response.synthetic_error() { + panic!("Failed to download node.lib: {:#?}", error); + } + + let mut reader = response.into_reader(); + let mut bytes = vec![]; + reader.read_to_end(&mut bytes).unwrap(); + + bytes } pub fn setup() { - let node_full_version = - String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout).unwrap(); - let trim_node_full_version = node_full_version.trim_end(); + let node_version = get_node_version().expect("Failed to determine nodejs version"); + let mut node_lib_file_dir = PathBuf::from(String::from_utf8(Command::new("node").arg("-e").arg("console.log(require('os').homedir())").output().unwrap().stdout).unwrap().trim_end().to_owned()); @@ -43,10 +52,10 @@ cfg_if! { let link_search_dir = node_lib_file_dir.clone(); - node_lib_file_dir.push(format!("node-{}.lib", trim_node_full_version)); + node_lib_file_dir.push(format!("node-{}.lib", node_version)); if let Err(_) = metadata(&node_lib_file_dir) { - let node_lib = download_node_lib(); + let node_lib = download_node_lib(&node_version); write(&node_lib_file_dir, &node_lib).expect(&format!("Could not save file to {}", node_lib_file_dir.to_str().unwrap())); } println!( From b062c0fd6e711a8cbde961def5f0d0e6e01ed7a5 Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 18:17:06 -0700 Subject: [PATCH 2/7] Respect NPM_CONFIG_DISTURL env var --- build/src/lib.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/build/src/lib.rs b/build/src/lib.rs index 47b8e665..18447b37 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -18,8 +18,8 @@ cfg_if! { Ok(stdout_str.trim().trim_start_matches('v').to_string()) } - fn download_node_lib(version: &str) -> Vec { - let url = format!("https://nodejs.org/dist/v{version}/win-x64/node.lib", version = version); + fn download_node_lib(dist_url: &str, version: &str) -> Vec { + let url = format!("{dist_url}/v{version}/win-x64/node.lib", dist_url = dist_url, version = version); let response = ureq::get(&url).call(); if let Some(error) = response.synthetic_error() { @@ -34,7 +34,17 @@ cfg_if! { } pub fn setup() { - let node_version = get_node_version().expect("Failed to determine nodejs version"); + // Assume nodejs if not specified. + let dist_url = std::env::var("NPM_CONFIG_DISTURL") + .unwrap_or("https://nodejs.org/dist".into()); + + // Try to get local nodejs version if not specified. + let node_version = std::env::var("NPM_CONFIG_TARGET") + .or_else(|_| get_node_version()) + .expect("Failed to determine nodejs version"); + + println!("cargo:rerun-if-env-changed=NPM_CONFIG_DISTURL"); + println!("cargo:rerun-if-env-changed=NPM_CONFIG_TARGET"); let mut node_lib_file_dir = PathBuf::from(String::from_utf8(Command::new("node").arg("-e").arg("console.log(require('os').homedir())").output().unwrap().stdout).unwrap().trim_end().to_owned()); @@ -55,7 +65,7 @@ cfg_if! { node_lib_file_dir.push(format!("node-{}.lib", node_version)); if let Err(_) = metadata(&node_lib_file_dir) { - let node_lib = download_node_lib(&node_version); + let node_lib = download_node_lib(&dist_url, &node_version); write(&node_lib_file_dir, &node_lib).expect(&format!("Could not save file to {}", node_lib_file_dir.to_str().unwrap())); } println!( From e36e1e1fbb35f80850e672eeba51a9ac0ee954d5 Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 18:24:19 -0700 Subject: [PATCH 3/7] Copy win_delay_load_hook from Neon --- napi/Cargo.toml | 3 ++ napi/src/lib.rs | 2 + napi/src/win_delay_load_hook.rs | 79 +++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 napi/src/win_delay_load_hook.rs diff --git a/napi/Cargo.toml b/napi/Cargo.toml index ab9a8357..0628c8fb 100644 --- a/napi/Cargo.toml +++ b/napi/Cargo.toml @@ -18,6 +18,9 @@ tokio_rt = ["futures", "tokio", "once_cell"] [dependencies] napi-sys = {version = "0.4", path = "../sys"} +[target.'cfg(windows)'.dependencies] +winapi = "0.3.9" + [dependencies.encoding_rs] optional = true version = "0.8" diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 8ce80c4e..442194de 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -110,6 +110,8 @@ mod tokio_rt; #[cfg(all(feature = "libuv", napi4))] mod uv; mod version; +#[cfg(target_os = "windows")] +mod win_delay_load_hook; pub use napi_sys as sys; diff --git a/napi/src/win_delay_load_hook.rs b/napi/src/win_delay_load_hook.rs new file mode 100644 index 00000000..712cbc54 --- /dev/null +++ b/napi/src/win_delay_load_hook.rs @@ -0,0 +1,79 @@ +//! The following directly was copied from [neon][]. +//! +//! Rust port of [win_delay_load_hook.cc][]. +//! +//! When the addon tries to load the "node.exe" DLL module, this module gives it the pointer to the +//! .exe we are running in instead. Typically, that will be the same value. But if the node executable +//! was renamed, you would not otherwise get the correct DLL. +//! +//! [neon]: https://github.com/neon-bindings/neon/blob/5ffa2d282177b63094c46e92b20b8e850d122e65/src/win_delay_load_hook.rs +//! [win_delay_load_hook.cc]: https://github.com/nodejs/node-gyp/blob/e18a61afc1669d4897e6c5c8a6694f4995a0f4d6/src/win_delay_load_hook.cc + +use std::ffi::CStr; +use std::ptr::null_mut; +use winapi::shared::minwindef::{BOOL, DWORD, FARPROC, HMODULE, LPVOID}; +use winapi::shared::ntdef::LPCSTR; +use winapi::um::libloaderapi::GetModuleHandleA; + +// Structures hand-copied from +// https://docs.microsoft.com/en-us/cpp/build/reference/structure-and-constant-definitions + +#[repr(C)] +#[allow(non_snake_case)] +struct DelayLoadProc { + fImportByName: BOOL, + // Technically this is `union{LPCSTR; DWORD;}` but we don't access it anyways. + szProcName: LPCSTR, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct DelayLoadInfo { + /// size of structure + cb: DWORD, + /// raw form of data (everything is there) + /// Officially a pointer to ImgDelayDescr but we don't access it. + pidd: LPVOID, + /// points to address of function to load + ppfn: *mut FARPROC, + /// name of dll + szDll: LPCSTR, + /// name or ordinal of procedure + dlp: DelayLoadProc, + /// the hInstance of the library we have loaded + hmodCur: HMODULE, + /// the actual function that will be called + pfnCur: FARPROC, + /// error received (if an error notification) + dwLastError: DWORD, +} + +#[allow(non_snake_case)] +type PfnDliHook = unsafe extern "C" fn(dliNotify: usize, pdli: *const DelayLoadInfo) -> FARPROC; + +const HOST_BINARIES: &[&[u8]] = &[b"node.exe", b"electron.exe"]; + +unsafe extern "C" fn load_exe_hook(event: usize, info: *const DelayLoadInfo) -> FARPROC { + if event != 0x01 + /* dliNotePreLoadLibrary */ + { + return null_mut(); + } + + let dll_name = CStr::from_ptr((*info).szDll); + if !HOST_BINARIES + .iter() + .any(|&host_name| host_name == dll_name.to_bytes()) + { + return null_mut(); + } + + let exe_handle = GetModuleHandleA(null_mut()); + + // PfnDliHook sometimes has to return a FARPROC, sometimes an HMODULE, but only one + // of them could be specified in the header, so we have to cast our HMODULE to that. + exe_handle as FARPROC +} + +#[no_mangle] +static mut __pfnDliNotifyHook2: *mut PfnDliHook = load_exe_hook as *mut PfnDliHook; From 536445836606ea5f16a6cc91c5b6a49a842769bd Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 18:29:08 -0700 Subject: [PATCH 4/7] Avoid running entire napi_build::setup twice --- build/src/lib.rs | 2 +- napi/build.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build/src/lib.rs b/build/src/lib.rs index 18447b37..29534b4c 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -93,7 +93,7 @@ cfg_if! { } } -fn setup_napi_feature() { +pub fn setup_napi_feature() { let napi_version = String::from_utf8( Command::new("node") .args(&["-e", "console.log(process.versions.napi)"]) diff --git a/napi/build.rs b/napi/build.rs index 1f866b6a..1c4d57bc 100644 --- a/napi/build.rs +++ b/napi/build.rs @@ -1,5 +1,3 @@ -extern crate napi_build; - fn main() { - napi_build::setup(); + napi_build::setup_napi_feature(); } From 7739cf5dc3e81cd8cc894d5fba63d3bc9ac721b7 Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 20:00:16 -0700 Subject: [PATCH 5/7] Respect CARGO_CFG_TARGET_ARCH env var --- build/src/lib.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/build/src/lib.rs b/build/src/lib.rs index 29534b4c..f24578f3 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -18,8 +18,9 @@ cfg_if! { Ok(stdout_str.trim().trim_start_matches('v').to_string()) } - fn download_node_lib(dist_url: &str, version: &str) -> Vec { - let url = format!("{dist_url}/v{version}/win-x64/node.lib", dist_url = dist_url, version = version); + fn download_node_lib(dist_url: &str, version: &str, arch: &str) -> Vec { + // Assume windows since we know we are building on windows. + let url = format!("{dist_url}/v{version}/win-{arch}/node.lib", dist_url = dist_url, version = version, arch = arch); let response = ureq::get(&url).call(); if let Some(error) = response.synthetic_error() { @@ -43,6 +44,24 @@ cfg_if! { .or_else(|_| get_node_version()) .expect("Failed to determine nodejs version"); + // NPM also gives us an arch var, but let's trust cargo more. + // We translate from cargo's arch env format into npm/gyps's. + // See https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch for rust env values. + // Nodejs appears to follow `process.arch`. + // See https://nodejs.org/docs/latest/api/process.html#process_process_arch for npm env values. + let arch = std::env::var("CARGO_CFG_TARGET_ARCH") + .map(|arch| match arch.as_str() { + "x86" => "x86", // TODO: x86 appears to also be called ia32 in npm_config_arch sometimes. What is the right value? + "x86_64" => "x64", + "mips" => "mips", + "powerpc" => "ppc", + "powerpc64" => "ppc64", + "arm" => "arm", + "aarch64" => "arm64", + arch => panic!("Unknown Architecture: {}", arch), + }) + .expect("Failed to determine target arch"); + println!("cargo:rerun-if-env-changed=NPM_CONFIG_DISTURL"); println!("cargo:rerun-if-env-changed=NPM_CONFIG_TARGET"); @@ -65,7 +84,7 @@ cfg_if! { node_lib_file_dir.push(format!("node-{}.lib", node_version)); if let Err(_) = metadata(&node_lib_file_dir) { - let node_lib = download_node_lib(&dist_url, &node_version); + let node_lib = download_node_lib(&dist_url, &node_version, &arch); write(&node_lib_file_dir, &node_lib).expect(&format!("Could not save file to {}", node_lib_file_dir.to_str().unwrap())); } println!( @@ -73,7 +92,6 @@ cfg_if! { &node_lib_file_dir.file_stem().unwrap().to_str().unwrap() ); println!("cargo:rustc-link-search=native={}", link_search_dir.to_str().unwrap()); - // Link `win_delay_load_hook.obj` for windows electron println!("cargo:rustc-cdylib-link-arg=delayimp.lib"); println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe"); setup_napi_feature(); From e25ef3a3d81bf97c2c5a3f8440acc00f43a4b5e8 Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 20:11:10 -0700 Subject: [PATCH 6/7] Seperate build setup script into seperate files to fix formatting --- build/src/lib.rs | 123 +++++-------------------------------------- build/src/macos.rs | 8 +++ build/src/windows.rs | 116 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 110 deletions(-) create mode 100644 build/src/macos.rs create mode 100644 build/src/windows.rs diff --git a/build/src/lib.rs b/build/src/lib.rs index f24578f3..66a0ac86 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -1,116 +1,19 @@ -extern crate cfg_if; +cfg_if::cfg_if! { + if #[cfg(windows)] { + mod windows; + pub use windows::setup; + } else if #[cfg(target_os = "macos")] { + mod macos; + pub use macos::setup; + } else { + pub fn setup() { + setup_napi_feature(); + } + } +} use std::process::Command; -use cfg_if::cfg_if; - -cfg_if! { - if #[cfg(windows)] { - use std::fs::{create_dir, metadata, write}; - use std::path::PathBuf; - use std::io::Read; - - fn get_node_version() -> std::io::Result { - let output = Command::new("node").arg("-v").output()?; - let stdout_str = String::from_utf8_lossy(&output.stdout); - - // version should not have a leading "v" or trailing whitespace - Ok(stdout_str.trim().trim_start_matches('v').to_string()) - } - - fn download_node_lib(dist_url: &str, version: &str, arch: &str) -> Vec { - // Assume windows since we know we are building on windows. - let url = format!("{dist_url}/v{version}/win-{arch}/node.lib", dist_url = dist_url, version = version, arch = arch); - - let response = ureq::get(&url).call(); - if let Some(error) = response.synthetic_error() { - panic!("Failed to download node.lib: {:#?}", error); - } - - let mut reader = response.into_reader(); - let mut bytes = vec![]; - reader.read_to_end(&mut bytes).unwrap(); - - bytes - } - - pub fn setup() { - // Assume nodejs if not specified. - let dist_url = std::env::var("NPM_CONFIG_DISTURL") - .unwrap_or("https://nodejs.org/dist".into()); - - // Try to get local nodejs version if not specified. - let node_version = std::env::var("NPM_CONFIG_TARGET") - .or_else(|_| get_node_version()) - .expect("Failed to determine nodejs version"); - - // NPM also gives us an arch var, but let's trust cargo more. - // We translate from cargo's arch env format into npm/gyps's. - // See https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch for rust env values. - // Nodejs appears to follow `process.arch`. - // See https://nodejs.org/docs/latest/api/process.html#process_process_arch for npm env values. - let arch = std::env::var("CARGO_CFG_TARGET_ARCH") - .map(|arch| match arch.as_str() { - "x86" => "x86", // TODO: x86 appears to also be called ia32 in npm_config_arch sometimes. What is the right value? - "x86_64" => "x64", - "mips" => "mips", - "powerpc" => "ppc", - "powerpc64" => "ppc64", - "arm" => "arm", - "aarch64" => "arm64", - arch => panic!("Unknown Architecture: {}", arch), - }) - .expect("Failed to determine target arch"); - - println!("cargo:rerun-if-env-changed=NPM_CONFIG_DISTURL"); - println!("cargo:rerun-if-env-changed=NPM_CONFIG_TARGET"); - - let mut node_lib_file_dir = - PathBuf::from(String::from_utf8(Command::new("node").arg("-e").arg("console.log(require('os').homedir())").output().unwrap().stdout).unwrap().trim_end().to_owned()); - - node_lib_file_dir.push(".napi-rs"); - - match create_dir(&node_lib_file_dir) { - Ok(_) => {}, - Err(err) => { - if err.kind() != std::io::ErrorKind::AlreadyExists { - panic!("create {} folder failed: {}", node_lib_file_dir.to_str().unwrap(), err) - } - }, - } - - let link_search_dir = node_lib_file_dir.clone(); - - node_lib_file_dir.push(format!("node-{}.lib", node_version)); - - if let Err(_) = metadata(&node_lib_file_dir) { - let node_lib = download_node_lib(&dist_url, &node_version, &arch); - write(&node_lib_file_dir, &node_lib).expect(&format!("Could not save file to {}", node_lib_file_dir.to_str().unwrap())); - } - println!( - "cargo:rustc-link-lib={}", - &node_lib_file_dir.file_stem().unwrap().to_str().unwrap() - ); - println!("cargo:rustc-link-search=native={}", link_search_dir.to_str().unwrap()); - println!("cargo:rustc-cdylib-link-arg=delayimp.lib"); - println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe"); - setup_napi_feature(); - } - } else if #[cfg(target_os = "macos")] { - /// Set up the build environment by setting Cargo configuration variables. - pub fn setup() { - println!("cargo:rustc-cdylib-link-arg=-Wl"); - println!("cargo:rustc-cdylib-link-arg=-undefined"); - println!("cargo:rustc-cdylib-link-arg=dynamic_lookup"); - setup_napi_feature(); - } - } else { - pub fn setup() { - setup_napi_feature(); - } - } -} - pub fn setup_napi_feature() { let napi_version = String::from_utf8( Command::new("node") diff --git a/build/src/macos.rs b/build/src/macos.rs new file mode 100644 index 00000000..49ecd4c2 --- /dev/null +++ b/build/src/macos.rs @@ -0,0 +1,8 @@ +use crate::*; + +pub fn setup() { + println!("cargo:rustc-cdylib-link-arg=-Wl"); + println!("cargo:rustc-cdylib-link-arg=-undefined"); + println!("cargo:rustc-cdylib-link-arg=dynamic_lookup"); + setup_napi_feature(); +} diff --git a/build/src/windows.rs b/build/src/windows.rs new file mode 100644 index 00000000..1deb581a --- /dev/null +++ b/build/src/windows.rs @@ -0,0 +1,116 @@ +use crate::*; +use std::fs::{create_dir, metadata, write}; +use std::io::Read; +use std::path::PathBuf; + +fn get_node_version() -> std::io::Result { + let output = Command::new("node").arg("-v").output()?; + let stdout_str = String::from_utf8_lossy(&output.stdout); + + // version should not have a leading "v" or trailing whitespace + Ok(stdout_str.trim().trim_start_matches('v').to_string()) +} + +fn download_node_lib(dist_url: &str, version: &str, arch: &str) -> Vec { + // Assume windows since we know we are building on windows. + let url = format!( + "{dist_url}/v{version}/win-{arch}/node.lib", + dist_url = dist_url, + version = version, + arch = arch + ); + + let response = ureq::get(&url).call(); + if let Some(error) = response.synthetic_error() { + panic!("Failed to download node.lib: {:#?}", error); + } + + let mut reader = response.into_reader(); + let mut bytes = vec![]; + reader.read_to_end(&mut bytes).unwrap(); + + bytes +} + +pub fn setup() { + // Assume nodejs if not specified. + let dist_url = std::env::var("NPM_CONFIG_DISTURL").unwrap_or("https://nodejs.org/dist".into()); + + // Try to get local nodejs version if not specified. + let node_version = std::env::var("NPM_CONFIG_TARGET") + .or_else(|_| get_node_version()) + .expect("Failed to determine nodejs version"); + + // NPM also gives us an arch var, but let's trust cargo more. + // We translate from cargo's arch env format into npm/gyps's. + // See https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch for rust env values. + // Nodejs appears to follow `process.arch`. + // See https://nodejs.org/docs/latest/api/process.html#process_process_arch for npm env values. + let arch = std::env::var("CARGO_CFG_TARGET_ARCH") + .map(|arch| match arch.as_str() { + "x86" => "x86", // TODO: x86 appears to also be called ia32 in npm_config_arch sometimes. What is the right value? + "x86_64" => "x64", + "mips" => "mips", + "powerpc" => "ppc", + "powerpc64" => "ppc64", + "arm" => "arm", + "aarch64" => "arm64", + arch => panic!("Unknown Architecture: {}", arch), + }) + .expect("Failed to determine target arch"); + + println!("cargo:rerun-if-env-changed=NPM_CONFIG_DISTURL"); + println!("cargo:rerun-if-env-changed=NPM_CONFIG_TARGET"); + + let mut node_lib_file_dir = PathBuf::from( + String::from_utf8( + Command::new("node") + .arg("-e") + .arg("console.log(require('os').homedir())") + .output() + .unwrap() + .stdout, + ) + .unwrap() + .trim_end() + .to_owned(), + ); + + node_lib_file_dir.push(".napi-rs"); + + match create_dir(&node_lib_file_dir) { + Ok(_) => {} + Err(err) => { + if err.kind() != std::io::ErrorKind::AlreadyExists { + panic!( + "create {} folder failed: {}", + node_lib_file_dir.to_str().unwrap(), + err + ) + } + } + } + + let link_search_dir = node_lib_file_dir.clone(); + + node_lib_file_dir.push(format!("node-{}.lib", node_version)); + + if let Err(_) = metadata(&node_lib_file_dir) { + let node_lib = download_node_lib(&dist_url, &node_version, &arch); + write(&node_lib_file_dir, &node_lib).expect(&format!( + "Could not save file to {}", + node_lib_file_dir.to_str().unwrap() + )); + } + println!( + "cargo:rustc-link-lib={}", + &node_lib_file_dir.file_stem().unwrap().to_str().unwrap() + ); + println!( + "cargo:rustc-link-search=native={}", + link_search_dir.to_str().unwrap() + ); + println!("cargo:rustc-cdylib-link-arg=delayimp.lib"); + println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe"); + setup_napi_feature(); +} From cab8fcc3a6ec1fc514b42cf43f5dfed91755d5a3 Mon Sep 17 00:00:00 2001 From: adumbidiot Date: Fri, 30 Oct 2020 20:58:28 -0700 Subject: [PATCH 7/7] Remove use of global node.lib cache --- build/src/windows.rs | 71 ++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/build/src/windows.rs b/build/src/windows.rs index 1deb581a..d493eea8 100644 --- a/build/src/windows.rs +++ b/build/src/windows.rs @@ -1,5 +1,7 @@ use crate::*; -use std::fs::{create_dir, metadata, write}; +use std::collections::hash_map::DefaultHasher; +use std::fs::{metadata, write}; +use std::hash::{Hash, Hasher}; use std::io::Read; use std::path::PathBuf; @@ -33,8 +35,11 @@ fn download_node_lib(dist_url: &str, version: &str, arch: &str) -> Vec { } pub fn setup() { + let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR is not set"); + // Assume nodejs if not specified. - let dist_url = std::env::var("NPM_CONFIG_DISTURL").unwrap_or("https://nodejs.org/dist".into()); + let dist_url = + std::env::var("NPM_CONFIG_DISTURL").unwrap_or_else(|_| "https://nodejs.org/dist".to_string()); // Try to get local nodejs version if not specified. let node_version = std::env::var("NPM_CONFIG_TARGET") @@ -62,55 +67,45 @@ pub fn setup() { println!("cargo:rerun-if-env-changed=NPM_CONFIG_DISTURL"); println!("cargo:rerun-if-env-changed=NPM_CONFIG_TARGET"); - let mut node_lib_file_dir = PathBuf::from( - String::from_utf8( - Command::new("node") - .arg("-e") - .arg("console.log(require('os').homedir())") - .output() - .unwrap() - .stdout, - ) - .unwrap() - .trim_end() - .to_owned(), + let mut node_lib_file_path = PathBuf::from(out_dir); + let link_search_dir = node_lib_file_path.clone(); + + // Hash the dist_url and store it in the node lib file name. + let dist_url_hash = { + let mut hasher = DefaultHasher::new(); + dist_url.hash(&mut hasher); + hasher.finish() + }; + + // Encode version, arch, and dist_url to detect and reaquire node.lib when these 3 change. + let node_lib_file_name = format!( + "node-{version}-{arch}-{dist_url_hash}.lib", + version = node_version, + arch = arch, + dist_url_hash = dist_url_hash ); + node_lib_file_path.push(&node_lib_file_name); - node_lib_file_dir.push(".napi-rs"); - - match create_dir(&node_lib_file_dir) { - Ok(_) => {} - Err(err) => { - if err.kind() != std::io::ErrorKind::AlreadyExists { - panic!( - "create {} folder failed: {}", - node_lib_file_dir.to_str().unwrap(), - err - ) - } - } - } - - let link_search_dir = node_lib_file_dir.clone(); - - node_lib_file_dir.push(format!("node-{}.lib", node_version)); - - if let Err(_) = metadata(&node_lib_file_dir) { + // If file does not exist, download it. + if metadata(&node_lib_file_path).is_err() { let node_lib = download_node_lib(&dist_url, &node_version, &arch); - write(&node_lib_file_dir, &node_lib).expect(&format!( + + write(&node_lib_file_path, &node_lib).expect(&format!( "Could not save file to {}", - node_lib_file_dir.to_str().unwrap() + node_lib_file_path.to_str().unwrap() )); } + println!( "cargo:rustc-link-lib={}", - &node_lib_file_dir.file_stem().unwrap().to_str().unwrap() + node_lib_file_path.file_stem().unwrap().to_str().unwrap() ); println!( "cargo:rustc-link-search=native={}", - link_search_dir.to_str().unwrap() + link_search_dir.display() ); println!("cargo:rustc-cdylib-link-arg=delayimp.lib"); println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe"); + setup_napi_feature(); }