From 37e72976d6faa9c3c6b10f32d562caf42cafc963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Tue, 13 Apr 2021 20:22:58 +0200 Subject: [PATCH] Add support for building on Windows --- Cargo.toml | 3 ++ README.md | 27 +++++++++++++ build.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index faf9be5..4267262 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ crate-type = ["rlib"] [dependencies] libc = "0.2" + +[target.'cfg(target_os = "windows")'.build-dependencies] +vswhom = "0.1.0" diff --git a/README.md b/README.md index 3188204..31f2f7c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # vlc-rs [![Build Status](https://travis-ci.org/garkimasera/vlc-rs.svg?branch=master)](https://travis-ci.org/garkimasera/vlc-rs) + Rust bindings for libVLC media framework. ## Status + Many missing functions and wrappers. ## Use + Please add the following dependencies to your Cargo.toml. ```Toml @@ -20,7 +23,9 @@ git = "https://github.com/garkimasera/vlc-rs.git" ``` ## Example + Play for 10 seconds from a media file. + ```Rust extern crate vlc; use vlc::{Instance, Media, MediaPlayer}; @@ -45,5 +50,27 @@ fn main() { Other examples are in the examples directory. +## Building + +### 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). + +If you're building for `x86_64`, then you should grab the download labelled "Installer for 64bit version". +That installer is actually a self-extracting ZIP archive, so we can extract the contents without installing VLC itself. + +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 + MIT (Examples are licensed under CC0) diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2a28e00 --- /dev/null +++ b/build.rs @@ -0,0 +1,114 @@ +fn main() { + #[cfg(target_os = "windows")] + windows::link_vlc(); +} + +#[cfg(target_os = "windows")] +mod windows { + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + compile_error!("Only x86 and x86_64 are supported at the moment. Adding support for other architectures should be trivial."); + + 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=all={}", out_dir.display()); + // NOTE: 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 + // ``` + // without this. + // https://stackoverflow.com/a/34230122 + println!("cargo:rustc-link-lib=dylib=legacy_stdio_definitions"); + } + + fn generate_lib_from_dll(out_dir: &Path, vs_exe_path: &Path, vlc_path: &Path) { + // https://wiki.videolan.org/GenerateLibFromDll/ + + let vs_dumpbin = vs_exe_path.join("dumpbin.exe"); + let vs_lib = vs_exe_path.join("lib.exe"); + 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!(), + } + ), + ]) + .spawn() + .unwrap(); + } + + fn vlc_path() -> PathBuf { + #[allow(unused_assignments)] + let arch_path: Option = 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() + } +}