1mod win_delay_load_hook;
2
3#[cfg(windows)]
4mod win {
5 use super::win_delay_load_hook;
6 use std::env::var;
7 use std::fs::{File, remove_file};
8 use http_req::request;
9 use std::{
10 process::Command,
11 env::temp_dir,
12 io,
13 path::{PathBuf},
14 fs::{read_dir, create_dir, DirEntry},
15 time::{SystemTime, UNIX_EPOCH},
16 };
17
18 macro_rules! cargo_warn {
19 ($($tokens: tt)*) => {
20 println!("cargo:warning={}", format!($($tokens)*))
21 }
22 }
23
24 fn print_folder(path: &PathBuf) {
25 match ls(path) {
26 Ok(list) => {
27 list.iter()
28 .for_each(|entry| cargo_warn!("{:?}", entry.path()));
29 }
30 Err(err) => cargo_warn!("Fail read {path:?}: {err}"),
31 }
32 }
33
34 fn ls(path: &PathBuf) -> Result<Vec<DirEntry>, io::Error> {
35 let mut list = vec![];
36 for entry in read_dir(path)? {
37 list.push(entry?);
38 }
39 Ok(list)
40 }
41
42 fn tmp_folder_name() -> String {
43 format!(
44 "node_bindgen_build_{}",
45 SystemTime::now()
46 .duration_since(UNIX_EPOCH)
47 .expect("Time went backwards")
48 .as_nanos()
49 )
50 }
51
52 fn find_file_ends_with(path: &PathBuf, filename: &str) -> Option<String> {
53 let list = ls(path).expect("Get list of files from temp folder");
54 list.iter()
55 .find(|entry| {
56 if let Ok(mt) = entry.metadata() {
57 if mt.is_file() {
58 return entry.file_name().to_string_lossy().ends_with(filename);
59 }
60 }
61 false
62 })
63 .map(|entry| entry.file_name().to_string_lossy().to_string())
64 }
65
66 pub fn configure() {
67 let node_full_version =
69 String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout)
70 .unwrap()
71 .trim_end()
72 .to_string();
73
74 let tmp_dir = temp_dir().join(tmp_folder_name());
75 if !tmp_dir.exists() {
76 create_dir(&tmp_dir).expect("Folder {tmp_dir:?} would be created");
77 }
78 let temp_lib = tmp_dir.join(format!("node-{}.lib", node_full_version));
79
80 if !temp_lib.exists() {
81 let lib_file_download_url = format!(
82 "https://nodejs.org/dist/{}/win-x64/node.lib",
83 node_full_version
84 );
85
86 cargo_warn!(
87 "downloading nodejs: {} to: {:#?}",
88 lib_file_download_url,
89 temp_lib
90 );
91
92 let mut node_lib_file = File::create(&temp_lib).unwrap();
93 if let Err(err) = request::get(&lib_file_download_url, &mut node_lib_file) {
94 if temp_lib.exists() {
95 if let Err(err) = remove_file(&temp_lib) {
96 cargo_warn!("Fail to remove {:#?} due error: {}", temp_lib, err);
97 }
98 }
99 panic!("Download node.lib file failed with: {}", err);
100 };
101 }
102
103 println!(
104 "cargo:rustc-link-lib={}",
105 &temp_lib.file_stem().unwrap().to_str().unwrap()
106 );
107 println!("cargo:rustc-link-search={}", tmp_dir.to_str().unwrap());
108
109 let node_runtime_env = "npm_config_runtime";
111 println!("cargo:rerun-if-env-changed={}", node_runtime_env);
112
113 if var(node_runtime_env).map(|s| s == "electron") == Ok(true) {
114 let mut filename = format!(
116 "{}.o",
117 win_delay_load_hook::build(tmp_dir.clone())
118 .expect("Failed to build win_delay_load_hook")
119 );
120 let full_filename = tmp_dir.join(&filename);
121 if !full_filename.exists() {
123 cargo_warn!("File {full_filename:?} doesn't exist");
124 print_folder(&tmp_dir);
126 cargo_warn!("Looking for file {filename} with some prefix in {tmp_dir:?}");
128 if let Some(prefix_filename) = find_file_ends_with(&tmp_dir, &filename) {
129 filename = prefix_filename;
130 cargo_warn!("Found object file {filename}");
131 } else {
132 panic!("Fail to find any related object file");
133 }
134 }
135 println!("cargo:rustc-cdylib-link-arg={filename}");
136 println!("cargo:rustc-cdylib-link-arg=delayimp.lib");
137 println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe");
138 println!("cargo:rustc-cdylib-link-arg=/INCLUDE:__pfnDliNotifyHook2");
139 println!("cargo:rustc-cdylib-link-arg=/FORCE:MULTIPLE");
140 }
141 }
142}
143
144#[cfg(windows)]
147pub fn configure() {
148 win::configure();
149}
150
151#[cfg(unix)]
152pub fn configure() {
153 if cfg!(target_os = "macos") {
154 println!("cargo:rustc-cdylib-link-arg=-undefined");
156 println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
157 }
158
159 }