Skip to content

Commit

Permalink
feat(neon-build): Add builder for customizing index.node location
Browse files Browse the repository at this point in the history
  • Loading branch information
kjvalencik committed Dec 7, 2020
1 parent ea6e96b commit 48a2e88
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 21 deletions.
4 changes: 4 additions & 0 deletions crates/neon-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ mod legacy;
#[cfg(not(feature = "neon-sys"))]
mod napi;

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

/// Custom build scripts for [Neon][neon] modules.
/// Must be called from `main.rs` in a Cargo [build script][build-script].
///
Expand All @@ -13,6 +16,7 @@ mod napi;
///
/// ```rust
/// // build.rs
/// # #[allow(clippy::needless_doctest_main)]
/// fn main() {
/// neon_build::setup();
/// }
Expand Down
170 changes: 150 additions & 20 deletions crates/neon-build/src/napi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use std::path::PathBuf;
use std::path::{Path, PathBuf};

fn out_dir() -> PathBuf {
std::env::var_os("OUT_DIR")
fn manifest_dir() -> PathBuf {
std::env::var_os("CARGO_MANIFEST_DIR")
.map(PathBuf::from)
.expect("Expected OUT_DIR environment variable")
.expect("Expected CARGO_MANIFEST_DIR environment variable")
}

fn output_file() -> PathBuf {
std::env::var_os("CARGO_MANIFEST_DIR")
fn out_dir() -> PathBuf {
std::env::var_os("OUT_DIR")
.map(PathBuf::from)
.expect("Expected CARGO_MANIFEST_DIR environment variable")
.join("index.node")
.expect("Expected OUT_DIR environment variable")
}

fn is_env(env_var: &str, value: &str) -> bool {
Expand All @@ -19,25 +18,156 @@ fn is_env(env_var: &str, value: &str) -> bool {
.unwrap_or(false)
}

fn setup_unix() {
fn setup_unix(output_file: PathBuf) {
println!("cargo:rustc-cdylib-link-arg=-o");
println!("cargo:rustc-cdylib-link-arg={}", output_file().display());
println!("cargo:rustc-cdylib-link-arg={}", output_file.display());
}

fn setup_windows() {
let pdb_file = out_dir().join("index.pdb");
fn setup_windows(output_file: PathBuf) {
let pdb_file = output_file
.file_name()
.map(|file| out_dir().join(Path::new(file).with_extension("pdb")))
.expect("Expected a neon output file name");

println!("cargo:rustc-cdylib-link-arg=/OUT:{}", output_file().display());
println!("cargo:rustc-cdylib-link-arg=/OUT:{}", output_file.display());
println!("cargo:rustc-cdylib-link-arg=/PDB:{}", pdb_file.display());
}

pub(crate) fn setup() {
let is_windows = is_env("CARGO_CFG_TARGET_OS", "windows");
let is_gnu = is_env("CARGO_CFG_TARGET_ENV", "gnu");
/// `Builder` acts as a builder for initializing a Neon build script
///
/// A default setup builder is provided as [`neon_build::setup()`](crate::setup()).
///
/// # Example
///
/// Output the neon module at `lib/native.node`
///
/// ```
/// # #[allow(clippy::needless_doctest_main)]
/// fn main() {
/// neon_build::Builder::new()
/// .output_dir("lib")
/// .output_dir("native.node")
/// .setup();
/// }
#[derive(Debug, Default)]
pub struct Builder {
output_dir: Option<PathBuf>,
output_file: Option<PathBuf>,
}

impl Builder {
/// Create a new Neon build script builder
pub fn new() -> Self {
Default::default()
}

/// Sets the output directory for the native library.
/// Defaults to the cargo manifest directory. If not absolute, paths will
/// be relative to the cargo manifest directory.
pub fn output_dir(&mut self, output_dir: impl AsRef<Path>) -> &mut Self {
self.output_dir = Some(output_dir.as_ref().to_path_buf());
self
}

/// Sets the name of the native library. Defaults to `index.node`. If not
/// absolute, paths will be relative to the [`Builder::output_dir`].
///
/// **Note**: Node.js requires that native libraries have the `.node`
/// extension to be loaded by `require`.
pub fn output_file(&mut self, output_file: impl AsRef<Path>) -> &mut Self {
self.output_file = Some(output_file.as_ref().to_path_buf());
self
}

/// Setup the Cargo build process for Neon. Should be called once from
/// `fn main` in a cargo build script.
pub fn setup(&self) {
let output_file = self.absolute_output_file();
let is_windows = is_env("CARGO_CFG_TARGET_OS", "windows");
let is_gnu = is_env("CARGO_CFG_TARGET_ENV", "gnu");

if is_windows && !is_gnu {
setup_windows(output_file);
} else {
setup_unix(output_file);
}
}

if is_windows && !is_gnu {
setup_windows();
} else {
setup_unix();
fn absolute_output_file(&self) -> PathBuf {
let output_file = self.output_file.clone()
.unwrap_or_else(|| PathBuf::from("index.node"));

// Don't prepend `output_dir` if `output_file` is absolute
if output_file.is_absolute() {
return output_file;
}

let output_dir = if let Some(output_dir) = self.output_dir.clone() {
// If `output_dir` is absolute, use it, otherwise
// append it to `manifest_dir()`
if output_dir.is_absolute() {
output_dir
} else {
manifest_dir().join(output_dir)
}
} else {
// Default to `manifest_dir()`
manifest_dir()
};

output_dir.join(output_file)
}
}

pub(crate) fn setup() {
Builder::new().setup()
}

#[test]
fn test_absolute_output_file_defaults() {
let expected = manifest_dir().join("index.node");
let actual = Builder::new().absolute_output_file();

assert_eq!(actual, expected);
}

#[test]
fn test_absolute_output_file_absolute_file() {
let expected = PathBuf::from("/tmp/hello.node");
let actual = Builder::new()
.output_dir("/tmp/ignore/me")
.output_file("/tmp/hello.node")
.absolute_output_file();

assert_eq!(actual, expected);
}

#[test]
fn test_absolute_output_file_absolute_dir() {
let expected = PathBuf::from("/tmp/index.node");
let actual = Builder::new()
.output_dir("/tmp")
.absolute_output_file();

assert_eq!(actual, expected);
}

#[test]
fn test_absolute_output_file_relative_dir() {
let expected = manifest_dir().join("lib").join("index.node");
let actual = Builder::new()
.output_dir("lib")
.absolute_output_file();

assert_eq!(actual, expected);
}

#[test]
fn test_absolute_output_file_relative_file() {
let expected = manifest_dir().join("lib.node");
let actual = Builder::new()
.output_file("lib.node")
.absolute_output_file();

assert_eq!(actual, expected);
}
3 changes: 2 additions & 1 deletion test/napi/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
extern crate neon_build;

fn main() {
neon_build::setup(); // must be called in build.rs
neon_build::Builder::new().output_dir("lib").output_file("bar.node").setup();
//neon_build::setup(); // must be called in build.rs

// add project-specific build logic here...
}

0 comments on commit 48a2e88

Please sign in to comment.