Merge branch 'build/windows-rawdylib' into 'master'
libvlc-sys: import libvlc via raw-dylib on Windows See merge request videolan/vlc-rs!19merge-requests/19/merge
commit
dd0b720c75
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Collapse generated bindings on gitlabs MRs
|
||||||
|
libvlc-sys/bindings.rs gitlab-generated
|
||||||
|
|
@ -2,22 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d"
|
|
||||||
dependencies = [
|
|
||||||
"find-msvc-tools",
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "find-msvc-tools"
|
|
||||||
version = "0.1.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.189"
|
version = "0.2.189"
|
||||||
|
|
@ -30,7 +14,6 @@ version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vswhom",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -39,12 +22,6 @@ version = "0.3.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vlc-rs"
|
name = "vlc-rs"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
@ -52,23 +29,3 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"libvlc-sys",
|
"libvlc-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vswhom"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"vswhom-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vswhom-sys"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
|
||||||
19
README.md
19
README.md
|
|
@ -54,22 +54,11 @@ Other examples are in the examples directory.
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
To build `vlc-rs`, you must either build VLC from source or grab one of the pre-built packages from [videolan.org](https://www.videolan.org/vlc/download-windows.html).
|
vlc-rs uses libvlc's SDK and it is a required dependency that must be available at runtime.
|
||||||
|
For that, you must either build VLC from source or grab one of the pre-built packages from [videolan.org](https://www.videolan.org/vlc/download-windows.html).
|
||||||
|
|
||||||
If you're building for `x86_64`, then you should grab the download labelled "Installer for 64bit version".
|
Once you've downloaded your chosen package, you should extract it some place such that its path contains no spaces, and add that directory to your `PATH` so `libvlc.dll` is found at startup.
|
||||||
That installer is actually a self-extracting ZIP archive, so we can extract the contents without installing VLC itself.
|
For distribution of an executable program, you should probably copy over the necessary DLLs, as well as the `plugins` directory.
|
||||||
|
|
||||||
If you're building for `x86`, then you should either download labelled "7zip package" or the one labelled "Zip package".
|
|
||||||
|
|
||||||
Once you've downloaded your chosen package, you should extract it some place such that its path contains no spaces.
|
|
||||||
To point `vlc-rs` at your VLC package, you should set an appropriate environment variable:
|
|
||||||
|
|
||||||
- `VLC_LIB_DIR`: Directory of the VLC pacakge, any architecture
|
|
||||||
- `VLC_LIB_DIR_X86` : Directory of the VLC pacakge, `x86`-only
|
|
||||||
- `VLC_LIB_DIR_X86_64` : Directory of the VLC pacakge, `x86_64`-only
|
|
||||||
|
|
||||||
You should also add the package to your `PATH` variable if you intend to run the program.
|
|
||||||
For distribution of an executable program, you should probably copy over the neccessary DLLs, as well as the `plugins` directory.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,3 @@ libc = "0.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pkg-config = "0.3"
|
pkg-config = "0.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
|
||||||
vswhom = "0.1.0"
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,7 @@
|
||||||
//! Build script for `libvlc-sys`, locates and links libvlc.
|
//! Build script for `libvlc-sys`, locates and links libvlc.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
/// Minimum supported libvlc version.
|
/// Minimum supported libvlc version.
|
||||||
const MIN_LIBVLC_VERSION: &str = "3.0.0";
|
const MIN_LIBVLC_VERSION: &str = "3.0.0";
|
||||||
|
|
||||||
|
|
@ -10,126 +12,21 @@ fn probe_libvlc() -> Result<pkg_config::Library, pkg_config::Error> {
|
||||||
.probe("libvlc")
|
.probe("libvlc")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
fn main() {
|
||||||
mod windows {
|
// vsnprintf is inlined by the UCRT headers, so MSVC needs this to resolve
|
||||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
// the symbol src/lib.rs declares. https://stackoverflow.com/a/34230122
|
||||||
compile_error!("Only x86 and x86_64 are supported at the moment. Adding support for other architectures should be trivial.");
|
if env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default() == "msvc" {
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use vswhom::VsFindResult;
|
|
||||||
|
|
||||||
pub fn link_vlc() {
|
|
||||||
let vlc_path = vlc_path();
|
|
||||||
|
|
||||||
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
||||||
|
|
||||||
let vs = VsFindResult::search().expect("Could not locate Visual Studio");
|
|
||||||
let vs_exe_path = PathBuf::from(
|
|
||||||
vs.vs_exe_path
|
|
||||||
.expect("Could not retrieve executable path for Visual Studio"),
|
|
||||||
);
|
|
||||||
|
|
||||||
generate_lib_from_dll(&out_dir, &vs_exe_path, &vlc_path);
|
|
||||||
println!("cargo:rustc-link-search=native={}", out_dir.display());
|
|
||||||
// NOTE: Without this directive, linking fails with:
|
|
||||||
// ```
|
|
||||||
// error LNK2019: unresolved external symbol vsnprintf referenced in function _{MangledSymbolName}
|
|
||||||
// msvcrt.lib(vsnprintf.obj) : error LNK2001: unresolved external symbol vsnprintf
|
|
||||||
// msvcrt.lib(vsnprintf.obj) : error LNK2001: unresolved external symbol _vsnprintf
|
|
||||||
// ```
|
|
||||||
// https://stackoverflow.com/a/34230122
|
|
||||||
println!("cargo:rustc-link-lib=dylib=legacy_stdio_definitions");
|
println!("cargo:rustc-link-lib=dylib=legacy_stdio_definitions");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_lib_from_dll(out_dir: &Path, vs_exe_path: &Path, vlc_path: &Path) {
|
// The bindings import from libvlc.dll directly via `raw-dylib`, so
|
||||||
// https://wiki.videolan.org/GenerateLibFromDll/
|
// Windows needs neither an import library nor a search path,
|
||||||
|
// only the DLL at runtime.
|
||||||
let vs_dumpbin = vs_exe_path.join("dumpbin.exe");
|
if env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
|
||||||
let vs_lib = vs_exe_path.join("lib.exe");
|
return;
|
||||||
let vlc_def_path = out_dir.join("libvlc.def");
|
|
||||||
let vlc_import_lib = out_dir.join("vlc.lib");
|
|
||||||
|
|
||||||
let libvlc = vlc_path.join("libvlc.dll");
|
|
||||||
let exports = Command::new(vs_dumpbin)
|
|
||||||
.current_dir(out_dir)
|
|
||||||
.arg("/EXPORTS")
|
|
||||||
.arg(libvlc.display().to_string().trim_end_matches(r"\"))
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
let exports = String::from_utf8(exports.stdout).unwrap();
|
|
||||||
|
|
||||||
let mut vlc_def = String::from("EXPORTS\n");
|
|
||||||
for line in exports.lines() {
|
|
||||||
if let Some(line) = line.get(26..) {
|
|
||||||
if line.starts_with("libvlc_") {
|
|
||||||
vlc_def.push_str(line);
|
|
||||||
vlc_def.push_str("\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs::write(&vlc_def_path, vlc_def.into_bytes()).unwrap();
|
|
||||||
|
|
||||||
// FIXME: Handle paths with spaces in them.
|
|
||||||
Command::new(vs_lib)
|
|
||||||
.current_dir(out_dir)
|
|
||||||
.arg("/NOLOGO")
|
|
||||||
.args(&[
|
|
||||||
format!(
|
|
||||||
r#"/DEF:{}"#,
|
|
||||||
vlc_def_path.display().to_string().trim_end_matches(r"\")
|
|
||||||
),
|
|
||||||
format!(
|
|
||||||
r#"/OUT:{}"#,
|
|
||||||
vlc_import_lib.display().to_string().trim_end_matches(r"\")
|
|
||||||
),
|
|
||||||
format!(
|
|
||||||
"/MACHINE:{}",
|
|
||||||
match target_arch().as_str() {
|
|
||||||
"x86" => "x86",
|
|
||||||
"x86_64" => "x64",
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.status()
|
|
||||||
.expect("Failed to run lib.exe")
|
|
||||||
.success()
|
|
||||||
.then_some(())
|
|
||||||
.expect("lib.exe failed to generate the vlc import library");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vlc_path() -> PathBuf {
|
|
||||||
#[allow(unused_assignments)]
|
|
||||||
let arch_path: Option<OsString> = match target_arch().as_str() {
|
|
||||||
"x86" => env::var_os("VLC_LIB_DIR_X86"),
|
|
||||||
"x86_64" => env::var_os("VLC_LIB_DIR_X86_64"),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
arch_path
|
|
||||||
.or_else(|| env::var_os("VLC_LIB_DIR"))
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.expect("VLC_LIB_DIR not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn target_arch() -> String {
|
|
||||||
env::var("CARGO_CFG_TARGET_ARCH").unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// On success pkg-config has already emitted the link directives; only the
|
|
||||||
// failure path needs handling.
|
|
||||||
if let Err(err) = probe_libvlc() {
|
if let Err(err) = probe_libvlc() {
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
windows::link_vlc();
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
panic!("libvlc (>= {}) not found: {:?}", MIN_LIBVLC_VERSION, err);
|
panic!("libvlc (>= {}) not found: {:?}", MIN_LIBVLC_VERSION, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,14 @@ pub mod valist;
|
||||||
// The bindings are a committed source file, regenerated out of band with
|
// The bindings are a committed source file, regenerated out of band with
|
||||||
// `cargo xtask bindgen`.
|
// `cargo xtask bindgen`.
|
||||||
include!("../bindings.rs");
|
include!("../bindings.rs");
|
||||||
|
|
||||||
|
// `libc` does not expose vsnprintf and libvlc advises to use it to handle logs in the log
|
||||||
|
// callbacks. Expose it for convenience.
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub fn vsnprintf(
|
||||||
|
s: *mut libc::c_char,
|
||||||
|
n: usize,
|
||||||
|
fmt: *const libc::c_char,
|
||||||
|
ap: VaList,
|
||||||
|
) -> libc::c_int;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ unsafe extern "C" fn logging_cb(
|
||||||
let f: &Box<dyn Fn(LogLevel, Log, Cow<str>) + Send + 'static> = ::std::mem::transmute(data);
|
let f: &Box<dyn Fn(LogLevel, Log, Cow<str>) + Send + 'static> = ::std::mem::transmute(data);
|
||||||
let mut buf: [c_char; BUF_SIZE] = [0; BUF_SIZE];
|
let mut buf: [c_char; BUF_SIZE] = [0; BUF_SIZE];
|
||||||
|
|
||||||
sys::vsnprintf(buf.as_mut_ptr(), BUF_SIZE.try_into().unwrap(), fmt, args);
|
sys::vsnprintf(buf.as_mut_ptr(), BUF_SIZE, fmt, args);
|
||||||
|
|
||||||
f((level as u32).into(), Log{ptr: ctx}, from_cstr_ref(buf.as_ptr()).unwrap());
|
f((level as u32).into(), Log{ptr: ctx}, from_cstr_ref(buf.as_ptr()).unwrap());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,22 @@ fn generate_bindings() {
|
||||||
.ctypes_prefix("libc")
|
.ctypes_prefix("libc")
|
||||||
// Allowlist every (lib)vlc symbol.
|
// Allowlist every (lib)vlc symbol.
|
||||||
.allowlist_item("(lib|LIB)?(vlc|VLC)_.*")
|
.allowlist_item("(lib|LIB)?(vlc|VLC)_.*")
|
||||||
// Required by the Windows `legacy_stdio_definitions` link workaround
|
// Avoid leaking unecessary glibc/platform specifics.
|
||||||
// (see libvlc-sys/build.rs).
|
.blocklist_type("_IO_.*")
|
||||||
.allowlist_function("vsnprintf")
|
.blocklist_type("_iobuf")
|
||||||
|
.blocklist_type("FILE")
|
||||||
|
.blocklist_type("__off.*")
|
||||||
|
.blocklist_type("__uint64_t")
|
||||||
|
.raw_line("pub use libc::FILE;")
|
||||||
// Block the whole va_list family and rewrite the parameters bindings to our own `VaList`,
|
// Block the whole va_list family and rewrite the parameters bindings to our own `VaList`,
|
||||||
// which is ABI-correct on every target.
|
// which is ABI-correct on every target.
|
||||||
.blocklist_type(".*va_list.*")
|
.blocklist_type(".*va_list.*")
|
||||||
.raw_line("pub use crate::valist::VaList;");
|
.raw_line("pub use crate::valist::VaList;")
|
||||||
|
// These bindings are committed once and compiled everywhere, so the layout assertions would
|
||||||
|
// only ever encode the machine that ran bindgen.
|
||||||
|
.layout_tests(false)
|
||||||
|
// Emit a single extern block rather than one per function.
|
||||||
|
.merge_extern_blocks(true);
|
||||||
|
|
||||||
for path in &library.include_paths {
|
for path in &library.include_paths {
|
||||||
bindings = bindings.clang_arg(format!("-I{}", path.display()));
|
bindings = bindings.clang_arg(format!("-I{}", path.display()));
|
||||||
|
|
@ -68,6 +77,13 @@ fn generate_bindings() {
|
||||||
"a va_list spelling we do not know about survived"
|
"a va_list spelling we do not know about survived"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// On Windows rustc can synthesise the imports straight from the DLL, so no
|
||||||
|
// import library is needed. bindgen cannot emit the attribute itself.
|
||||||
|
let generated = generated.replace(
|
||||||
|
"unsafe extern \"C\" {",
|
||||||
|
"#[cfg_attr(windows, link(name = \"libvlc\", kind = \"raw-dylib\"))]\nunsafe extern \"C\" {",
|
||||||
|
);
|
||||||
|
|
||||||
std::fs::write(&output, generated).expect("couldn't write bindings");
|
std::fs::write(&output, generated).expect("couldn't write bindings");
|
||||||
println!("wrote {}", output.display());
|
println!("wrote {}", output.display());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue