Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lsl-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,16 @@ keywords = ["lsl", "ffi"]
categories = ["external-ffi-bindings"]
links = "lsl"

[features]
system = ["dep:pkg-config", "dep:vcpkg"]
bundled = ["dep:cmake"]
bindgen = ["dep:bindgen"]
default = ["system", "bundled", "bindgen"]

[build-dependencies]
cmake = "0.1.44"
cmake = { version = "0.1.44", optional = true }
bindgen = { version = "0.72", optional = true }
pkg-config = { version = "0.3", optional = true }

[target.'cfg(windows)'.build-dependencies]
vcpkg = { version = "0.2", optional = true }
119 changes: 92 additions & 27 deletions lsl-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,80 @@
use std::env;
use std::path::{Path, PathBuf};

fn main() {
// TODO: find out if liblsl already present on system and usable (if so, link to that instead)
// println!("cargo:warning={}", "rebuilding...");
build_liblsl();
let include_dirs = resolve_lsl();

#[cfg(feature = "bindgen")]
{
let header = find_header(&include_dirs, "lsl_c.h").unwrap_or_else(|| {
panic!(
"lsl_c.h not found in any resolved include dir: {include_dirs:?}.\n\
If liblsl is installed in a non-standard location, make sure pkg-config \
can find its .pc file (set PKG_CONFIG_PATH), or on Windows that vcpkg \
knows about the 'lsl' port."
)
});
run_bindgen(&header, &include_dirs);
}

let _ = include_dirs;
}

#[cfg(all(feature = "system", feature = "bundled"))]
fn resolve_lsl() -> Vec<PathBuf> {
try_system().unwrap_or_else(build_liblsl)
}

#[cfg(all(feature = "system", not(feature = "bundled")))]
fn resolve_lsl() -> Vec<PathBuf> {
try_system().unwrap_or_else(|| {
panic!(
"liblsl not found via pkg-config (or vcpkg on Windows). Install liblsl's \
development package, or enable the 'bundled' feature to build it from source."
)
})
}

// Build the liblsl library from source using cmake
fn build_liblsl() {
#[cfg(all(not(feature = "system"), feature = "bundled"))]
fn resolve_lsl() -> Vec<PathBuf> {
build_liblsl()
}

#[cfg(not(any(feature = "system", feature = "bundled")))]
fn resolve_lsl() -> Vec<PathBuf> {
compile_error!("at least one of the 'system' or 'bundled' features must be enabled")
}

#[cfg(feature = "system")]
fn try_system() -> Option<Vec<PathBuf>> {
println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH");

if let Ok(lib) = pkg_config::Config::new().probe("lsl") {
return Some(lib.include_paths);
}

#[cfg(windows)]
if let Ok(lib) = vcpkg::find_package("lsl") {
return Some(lib.include_paths);
}

None
}

fn find_header(dirs: &[PathBuf], name: &str) -> Option<PathBuf> {
dirs.iter().map(|d| d.join(name)).find(|p| p.exists())
}

#[cfg(feature = "bundled")]
fn build_liblsl() -> Vec<PathBuf> {
let target = env::var("TARGET").unwrap();

// build with cmake

let mut cfg = cmake::Config::new("liblsl");
cfg
.define("LSL_NO_FANCY_LIBNAME", "ON")
cfg.define("LSL_NO_FANCY_LIBNAME", "ON")
.define("LSL_BUILD_STATIC", "ON");
if target.contains("msvc") {
// override some C/CXX flags that the cmake crate splices in on Windows
// (these cause the build to fail)...
// * /EHsc sets the correct exception handling mode
// * /GR enables RTTI
// * /MD links in the msvcrt as a DLL instead of statically
let cxx_args = " /nologo /EHsc /MD /GR";
cfg
.define("WIN32", "1")
cfg.define("WIN32", "1")
.define("_WINDOWS", "1")
.define("CMAKE_C_FLAGS", cxx_args)
.define("CMAKE_CXX_FLAGS", cxx_args)
Expand All @@ -34,23 +85,37 @@ fn build_liblsl() {
}
let install_dir = cfg.build();

// emit link directives
let libdir = install_dir.join("lib");
let libname = "lsl";
println!(
"cargo:rustc-link-search=native={}",
libdir.to_str().unwrap()
);
println!("cargo:rustc-link-lib=static={}", libname);

// make sure we also link some additional libs
println!("cargo:rustc-link-search=native={}", libdir.display());
println!("cargo:rustc-link-lib=static=lsl");

if target.contains("linux") {
println!("cargo:rustc-link-lib=dylib=stdc++");
} else if target.contains("windows") {
// TODO: this is a shortcoming in the current cmake file, which should be
// linking in this library (once this is fixed, we should remove this)
println!("cargo:rustc-link-lib=dylib=bcrypt");
} else {
println!("cargo:rustc-link-lib=dylib=c++");
}

vec![install_dir.join("include")]
}

#[cfg(feature = "bindgen")]
fn run_bindgen(header: &Path, include_dirs: &[PathBuf]) {
let mut builder = bindgen::Builder::default()
.header(header.to_str().expect("header path is not valid UTF-8"))
.allowlist_function("^lsl_.*")
.allowlist_var("^lsl_.*")
.allowlist_type("^lsl_.*");

for dir in include_dirs {
builder = builder.clang_arg(format!("-I{}", dir.display()));
}

let bindings = builder.generate().expect("failed to generate liblsl bindings");

let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join("generated.rs");
bindings
.write_to_file(&out)
.expect("failed to write liblsl bindings");
}
12 changes: 12 additions & 0 deletions lsl-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
#[cfg(not(feature = "bindgen"))]
mod generated;

#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
#[cfg(feature = "bindgen")]
mod generated {
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
}

#[cfg(feature = "system")]
#[link(name = "lsl")]
extern "C" {}

pub use generated::*;

#[cfg(test)]
Expand Down