Merge branch 'windows-build' into 'master'

Improve linking on Windows

See merge request mfkl/vlc-rs!1
merge-requests/7/merge
Martin Finkel 2021-07-21 06:25:32 +00:00
commit 21c4cb25f5
3 changed files with 143 additions and 0 deletions

View File

@ -16,3 +16,6 @@ crate-type = ["rlib"]
[dependencies]
libc = "0.2"
[target.'cfg(target_os = "windows")'.build-dependencies]
vswhom = "0.1.0"

View File

@ -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)

113
build.rs 100644
View File

@ -0,0 +1,113 @@
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=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");
}
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<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()
}
}