Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

neon-build refactor to make use of dynamic loading #647

Merged
merged 8 commits into from
Dec 9, 2020
Merged
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ smallvec = "1.4.2"
neon-runtime = { version = "=0.5.3", path = "crates/neon-runtime" }
neon-macros = { version = "=0.5.3", path = "crates/neon-macros", optional = true }

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["minwindef", "libloaderapi", "ntdef"] }

[features]
default = ["legacy-runtime"]

Expand Down Expand Up @@ -68,7 +65,7 @@ members = [
"crates/neon-runtime",
"crates/neon-sys",
"test/static",
"test/electron/native",
"test/electron",
"test/dynamic/native",
"test/napi/native"
"test/napi"
]
8 changes: 1 addition & 7 deletions crates/neon-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ authors = ["Dave Herman <[email protected]>"]
description = "Build logic required for Neon projects."
repository = "https://github.com/neon-bindings/neon"
license = "MIT/Apache-2.0"
edition = "2018"
build = "build.rs"

[dependencies]
neon-sys = { version = "=0.5.3", path = "../neon-sys", optional = true }
cfg-if = "0.1.9"

[build-dependencies]
cfg-if = "0.1.9"

[target.'cfg(windows)'.dependencies]
ureq = { version = "1.3.0", default-features = false, features = ["native-tls"] }
44 changes: 19 additions & 25 deletions crates/neon-build/build.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
extern crate cfg_if;
#[cfg(all(windows, feature = "neon-sys"))]
fn main() {
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

use cfg_if::cfg_if;

cfg_if! {
if #[cfg(all(windows, feature = "neon-sys"))] {
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

// Extract linker metadata from neon-sys and save it in a text file.
// The neon-build lib.rs will textually include them into constants.
fn save(var: &str, filename: &str) {
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(filename);
let mut buffer = File::create(path).unwrap();
write!(buffer, "{}", env::var(var).unwrap()).unwrap();
}

fn main() {
save("DEP_NEON_NODE_ROOT_DIR", "node_root_dir");
save("DEP_NEON_NODE_ARCH", "node_arch");
save("DEP_NEON_NODE_LIB_FILE", "node_lib_file");
}
} else {
fn main() { }
// Extract linker metadata from neon-sys and save it in a text file.
// The neon-build lib.rs will textually include them into constants.
fn save(var: &str, filename: &str) {
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(filename);
let mut buffer = File::create(path).unwrap();
write!(buffer, "{}", env::var(var).unwrap()).unwrap();
}

save("DEP_NEON_NODE_ROOT_DIR", "node_root_dir");
save("DEP_NEON_NODE_ARCH", "node_arch");
save("DEP_NEON_NODE_LIB_FILE", "node_lib_file");
}

#[cfg(any(not(windows), not(feature = "neon-sys")))]
fn main() {}
1 change: 1 addition & 0 deletions crates/neon-build/src/legacy/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub(crate) fn setup() {}
4 changes: 4 additions & 0 deletions crates/neon-build/src/legacy/macos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub(crate) fn setup() {
println!("cargo:rustc-cdylib-link-arg=-undefined");
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
}
14 changes: 14 additions & 0 deletions crates/neon-build/src/legacy/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[cfg(all(not(windows), not(target_os = "macos")))]
mod linux;
#[cfg(all(not(windows), not(target_os = "macos")))]
pub(crate) use linux::*;

#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub(crate) use macos::*;

#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub(crate) use windows::*;
36 changes: 36 additions & 0 deletions crates/neon-build/src/legacy/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::env::var;
use std::path::Path;

const NODE_ROOT_DIR: &'static str = include_str!(concat!(env!("OUT_DIR"), "\\node_root_dir"));
const NODE_ARCH: &'static str = include_str!(concat!(env!("OUT_DIR"), "\\node_arch"));
const NODE_LIB_FILE: &'static str = include_str!(concat!(env!("OUT_DIR"), "\\node_lib_file"));

/// Set up the build environment by setting Cargo configuration variables.
pub(crate) fn setup() {
let debug = var("DEBUG").ok().map_or(false, |s| s == "true");
let configuration = if debug { "Debug" } else { "Release" };
let node_lib_file_path = Path::new(NODE_LIB_FILE);
let mut node_lib_path = Path::new(NODE_ROOT_DIR).to_path_buf();
node_lib_path.push(NODE_ARCH);
println!(
"cargo:rustc-link-search={}\\{}",
NODE_ROOT_DIR, configuration
);
println!(
"cargo:rustc-link-search=native={}",
&node_lib_path.display()
);
println!(
"cargo:rustc-link-lib={}",
&node_lib_file_path.file_stem().unwrap().to_str().unwrap()
);

// Link `win_delay_load_hook.obj` for windows electron
let node_runtime_env = "npm_config_runtime";
println!("cargo:rerun-if-env-changed={}", node_runtime_env);
if var(node_runtime_env).map(|s| s == "electron") == Ok(true) {
println!("cargo:rustc-cdylib-link-arg=win_delay_load_hook.obj");
println!("cargo:rustc-cdylib-link-arg=delayimp.lib");
println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe");
}
}
149 changes: 31 additions & 118 deletions crates/neon-build/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,119 +1,32 @@
extern crate cfg_if;
#[cfg(all(windows, not(feature = "neon-sys")))]
extern crate ureq;

use cfg_if::cfg_if;

cfg_if! {
if #[cfg(all(windows, feature = "neon-sys"))] {
use std::env::var;
use std::path::Path;

const NODE_ROOT_DIR: &'static str = include_str!(concat!(env!("OUT_DIR"), "\\node_root_dir"));
const NODE_ARCH: &'static str = include_str!(concat!(env!("OUT_DIR"), "\\node_arch"));
const NODE_LIB_FILE: &'static str = include_str!(concat!(env!("OUT_DIR"), "\\node_lib_file"));

/// Set up the build environment by setting Cargo configuration variables.
pub fn setup() {
let debug = var("DEBUG").ok().map_or(false, |s| s == "true");
let configuration = if debug { "Debug" } else { "Release" };
let node_lib_file_path = Path::new(NODE_LIB_FILE);
let mut node_lib_path = Path::new(NODE_ROOT_DIR).to_path_buf();
node_lib_path.push(NODE_ARCH);
println!("cargo:rustc-link-search={}\\{}", NODE_ROOT_DIR, configuration);
println!("cargo:rustc-link-search=native={}", &node_lib_path.display());
println!("cargo:rustc-link-lib={}", &node_lib_file_path.file_stem().unwrap().to_str().unwrap());

// Link `win_delay_load_hook.obj` for windows electron
let node_runtime_env = "npm_config_runtime";
println!("cargo:rerun-if-env-changed={}", node_runtime_env);
if var(node_runtime_env).map(|s| s == "electron") == Ok(true) {
println!("cargo:rustc-cdylib-link-arg=win_delay_load_hook.obj");
println!("cargo:rustc-cdylib-link-arg=delayimp.lib");
println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe");
}
}
} else if #[cfg(windows)] {
// ^ automatically not neon-sys
use std::io::{Error, ErrorKind, Write, Read, Result};
use std::process::Command;
use std::path::Path;

fn node_version() -> Result<String> {
let output = Command::new("node").arg("-v").output()?;
if !output.status.success() {
let _ = std::io::stderr().write_all(&output.stderr);
panic!("Could not download node.lib. There is likely more information from stderr above.");
}
let stdout = String::from_utf8(output.stdout).map_err(|error| {
Error::new(ErrorKind::InvalidData, error)
})?;
// npm_config_target should not contain a leading "v"
Ok(stdout.trim().trim_start_matches('v').to_string())
}

fn download_node_lib(version: &str, arch: &str) -> Result<Vec<u8>> {
// Assume we're building for node if a disturl is not specified.
let dist_url = std::env::var("NPM_CONFIG_DISTURL").unwrap_or("https://nodejs.org/dist".into());
let mut request = ureq::get(&format!(
"{dist_url}/v{version}/win-{arch}/node.lib",
dist_url = dist_url,
version = version,
arch = arch,
));
let response = request.call();
let mut bytes = vec![];
response.into_reader().read_to_end(&mut bytes)?;
Ok(bytes)
}

/// Set up the build environment by setting Cargo configuration variables.
pub fn setup() {
// If the user specified a node.lib path, we do not need to download
if let Some(node_lib_path) = std::env::var_os("NEON_NODE_LIB") {
let node_lib_path = Path::new(&node_lib_path);
// Clearing the file name returns the root+directory name
let dir = node_lib_path.with_file_name("");
let basename = node_lib_path.file_stem().expect("Could not parse lib name from NEON_NODE_LIB. Does the path include the full file name?");

println!("cargo:rustc-link-search=native={}", dir.display());
// `basename` is an OsStr, we can output it anyway by re-wrapping it in a Path
// Both `dir` and `basename` will be mangled (contain replacement characters) if
// they are not UTF-8 paths. If we don't mangle them though, Cargo will: so
// non-UTF-8 paths are simply not supported.
println!("cargo:rustc-link-lib={}", Path::new(basename).display());
return;
}

let version = std::env::var("npm_config_target")
.or_else(|_| node_version())
.expect("Could not determine Node.js version");
let arch = if std::env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86" {
"x86"
} else {
"x64"
};

let node_lib_store_path = format!(r"{}/node-{}.lib", env!("OUT_DIR"), arch);

// Download node.lib if it does not exist
if let Err(_) = std::fs::metadata(&node_lib_store_path) {
let node_lib = download_node_lib(&version, arch).expect("Could not download `node.lib`");
std::fs::write(&node_lib_store_path, &node_lib).expect("Could not save `node.lib`");
}

println!("cargo:rustc-link-search=native={}", env!("OUT_DIR"));
println!("cargo:rustc-link-lib=node-{}", arch);
println!("cargo:rustc-cdylib-link-arg=delayimp.lib");
println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe");
}
} 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=-undefined");
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
}
} else {
pub fn setup() { }
}
#[cfg(feature = "neon-sys")]
mod legacy;
#[cfg(not(feature = "neon-sys"))]
mod napi;

#[cfg(not(feature = "neon-sys"))]
pub use napi::Setup;

/// Custom build scripts for [Neon][neon] modules.
/// Must be called from `main.rs` in a Cargo [build script][build-script].
///
/// ```toml
/// [package]
/// build = "build.rs"
/// ```
///
/// ```rust
/// // build.rs
/// # #[allow(clippy::needless_doctest_main)]
/// fn main() {
/// neon_build::setup();
/// }
/// ```
///
/// [neon]: https://docs.rs/neon
/// [build-script]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
pub fn setup() {
#[cfg(feature = "neon-sys")]
legacy::setup();
#[cfg(not(feature = "neon-sys"))]
napi::setup();
}
Loading