Skip to content

Commit

Permalink
Possibility to use bindgen & bundled together
Browse files Browse the repository at this point in the history
* Also supports opt-in static linking
  • Loading branch information
Cobrand committed Oct 31, 2017
1 parent 2946b6f commit c1a9f63
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 97 deletions.
4 changes: 4 additions & 0 deletions sdl2-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ optional = true
version = "0.2"
optional = true

[build-dependencies]
cfg-if = "0.1"

[features]

default = []
use-pkgconfig = ["pkg-config"]
use-bindgen = ["bindgen"]
static-link = []
use_mac_framework = []
bundled = ["cmake", "reqwest", "tar", "flate2"]
270 changes: 173 additions & 97 deletions sdl2-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ extern crate flate2;
#[cfg(feature="bundled")]
extern crate reqwest;

#[macro_use]
extern crate cfg_if;

use std::path::{Path, PathBuf};
use std::{io, fs, env};

Expand Down Expand Up @@ -46,142 +49,223 @@ fn download_to<T: io::Write>(url: &str, mut dest: T) {
}
}

#[cfg(feature="bundled")]
fn main() {
let target = env::var("TARGET").expect("Cargo build scripts always have TARGET");
let host = env::var("HOST").expect("Cargo build scripts always have HOST");
let target_os = get_os_from_triple(target.as_str()).unwrap();
#[cfg(feature = "use-pkgconfig")]
#[cfg(feature = "static-link")]
fn get_pkg_config() -> pkg_config::Library {
pkg_config::Config::new()
.statik(false)
.probe("sdl2").unwrap()
}

prepare_bindings(&target, &host);
#[cfg(feature = "use-pkgconfig")]
#[cfg(not(feature = "static-link"))]
fn get_pkg_config() -> pkg_config::Library {
pkg_config::Config::new()
.statik(true)
.probe("sdl2").unwrap()
}

// returns the location of the downloaded source
#[cfg(feature = "bundled")]
fn download_sdl2() -> PathBuf {
let out_dir = env::var("OUT_DIR").unwrap();

let sdl2_archive_name = format!("SDL2-{}.tar.gz", LASTEST_SDL2_VERSION);
let sdl2_archive_url = format!("http://libsdl.org/release/{}", sdl2_archive_name);

let out_dir = env::var("OUT_DIR").unwrap();


let sdl2_archive_path = Path::new(&out_dir).join(sdl2_archive_name);
let sdl2_build_path = Path::new(&out_dir).join(format!("SDL2-{}", LASTEST_SDL2_VERSION));

// avoid re-downloading the archive if it already exists
if !sdl2_archive_path.exists() {
let sdl2_archive = fs::File::create(&sdl2_archive_path).unwrap();
download_to(&sdl2_archive_url, &sdl2_archive);
}

let reader = flate2::read::GzDecoder::new(
fs::File::open(&sdl2_archive_path).unwrap()
).unwrap();
let mut ar = tar::Archive::new(reader);
ar.unpack(&out_dir).unwrap();

let install_path = cmake::Config::new(sdl2_build_path)
.define("SDL_SHARED", "OFF")
.define("SDL_STATIC", "ON")
.build();

println!("cargo:rustc-link-search={}", install_path.join("lib").display());
println!("cargo:rustc-link-lib=static=SDL2main");
println!("cargo:rustc-link-lib=static=SDL2");

// Also linked to any required libraries for each supported platform
if t
arget_os == "windows-msvc" {
println!("cargo:rustc-link-lib=user32");
println!("cargo:rustc-link-lib=gdi32");
println!("cargo:rustc-link-lib=winmm");
println!("cargo:rustc-link-lib=imm32");
println!("cargo:rustc-link-lib=ole32");
println!("cargo:rustc-link-lib=oleaut32");
println!("cargo:rustc-link-lib=version");
println!("cargo:rustc-link-lib=uuid");
println!("cargo:rustc-link-lib=dinput8");
println!("cargo:rustc-link-lib=dxguid");
} else if target_os.contains("linux") {
println!("cargo:rustc-link-lib=sndio");
} else if target_os == "darwin" {
println!("cargo:rustc-link-lib=framework=Cocoa");
println!("cargo:rustc-link-lib=framework=IOKit");
println!("cargo:rustc-link-lib=framework=Carbon");
println!("cargo:rustc-link-lib=framework=ForceFeedback");
println!("cargo:rustc-link-lib=framework=CoreVideo");
println!("cargo:rustc-link-lib=framework=CoreAudio");
println!("cargo:rustc-link-lib=framework=AudioToolbox");
println!("cargo:rustc-link-lib=iconv");
sdl2_build_path
}

// compile a shared or static lib depending on the feature
#[cfg(feature = "bundled")]
fn compile_sdl2(sdl2_build_path: &Path) -> PathBuf {
let install_path = if cfg!(feature = "static-link") {
cmake::Config::new(sdl2_build_path)
.define("SDL_SHARED", "OFF")
.define("SDL_STATIC", "ON")
.build()
} else {
// TODO: Add other platform linker options here.
cmake::Config::new(sdl2_build_path)
.define("SDL_SHARED", "ON")
.define("SDL_STATIC", "OFF")
.build()
};

install_path
}

#[cfg(not(feature = "bundled"))]
fn compute_include_paths() -> Vec<String> {
let mut include_paths: Vec<String> = vec!();

if let Ok(include_path) = env::var("SDL2_INCLUDE_PATH") {
include_paths.push(format!("{}", include_path));
};

#[cfg(feature = "pkg-config")] {
// don't print the "cargo:xxx" directives, we're just trying to get the include paths here
let pkg_config_library = pkg_config::Config::new().print_system_libs(false).probe("sdl2").unwrap();
for path in pkg_config_library.include_paths {
include_paths.push(format!("{}", path.display()));
};
}

include_paths
}

fn link_sdl2(target_os: &str) {
#[cfg(all(feature = "use-pkgconfig", not(feature = "bundled")))] {
// prints the appropriate linking parameters when using pkg-config
// useless when using "bundled"
get_pkg_config();
}

#[cfg(not(feature = "static-link"))] {
if target_os == "ios" {
// iOS requires additional linking to function properly
println!("cargo:rustc-flags=-l framework=AVFoundation");
println!("cargo:rustc-flags=-l framework=AudioToolbox");
println!("cargo:rustc-flags=-l framework=CoreAudio");
println!("cargo:rustc-flags=-l framework=CoreGraphics");
println!("cargo:rustc-flags=-l framework=CoreMotion");
println!("cargo:rustc-flags=-l framework=Foundation");
println!("cargo:rustc-flags=-l framework=GameController");
println!("cargo:rustc-flags=-l framework=OpenGLES");
println!("cargo:rustc-flags=-l framework=QuartzCore");
println!("cargo:rustc-flags=-l framework=UIKit");
}

// pkg-config automatically prints this output when probing,
// however pkg_config isn't used with the feature "bundled"
if cfg!(feature = "bundled") || cfg!(not(feature = "use-pkgconfig")) {
if cfg!(feature = "use_mac_framework") && target_os == "darwin" {
println!("cargo:rustc-flags=-l framework=SDL2");
} else {
println!("cargo:rustc-flags=-l SDL2");
}
}
}

#[cfg(feature = "static-link")] {
if cfg!(feature = "bundled") || cfg!(feature = "use-pkgconfig") == false {
println!("cargo:rustc-link-lib=static=SDL2main");
println!("cargo:rustc-link-lib=static=SDL2");
}

// Also linked to any required libraries for each supported platform
if target_os == "windows-msvc" {
println!("cargo:rustc-link-lib=user32");
println!("cargo:rustc-link-lib=gdi32");
println!("cargo:rustc-link-lib=winmm");
println!("cargo:rustc-link-lib=imm32");
println!("cargo:rustc-link-lib=ole32");
println!("cargo:rustc-link-lib=oleaut32");
println!("cargo:rustc-link-lib=version");
println!("cargo:rustc-link-lib=uuid");
println!("cargo:rustc-link-lib=dinput8");
println!("cargo:rustc-link-lib=dxguid");
} else if target_os.contains("linux") {
println!("cargo:rustc-link-lib=sndio");
} else if target_os == "darwin" {
println!("cargo:rustc-link-lib=framework=Cocoa");
println!("cargo:rustc-link-lib=framework=IOKit");
println!("cargo:rustc-link-lib=framework=Carbon");
println!("cargo:rustc-link-lib=framework=ForceFeedback");
println!("cargo:rustc-link-lib=framework=CoreVideo");
println!("cargo:rustc-link-lib=framework=CoreAudio");
println!("cargo:rustc-link-lib=framework=AudioToolbox");
println!("cargo:rustc-link-lib=iconv");
} else {
// TODO: Add other platform linker options here.
}
}
}

#[cfg(not(feature="bundled"))]
fn main() {
let target = env::var("TARGET").expect("Cargo build scripts always have TARGET");
let host = env::var("HOST").expect("Cargo build scripts always have HOST");
let target_os = get_os_from_triple(target.as_str()).unwrap();

prepare_bindings(&target, &host);

if target_os == "ios" {
println!("cargo:rustc-flags=-l framework=AVFoundation");
println!("cargo:rustc-flags=-l framework=AudioToolbox");
println!("cargo:rustc-flags=-l framework=CoreAudio");
println!("cargo:rustc-flags=-l framework=CoreGraphics");
println!("cargo:rustc-flags=-l framework=CoreMotion");
println!("cargo:rustc-flags=-l framework=Foundation");
println!("cargo:rustc-flags=-l framework=GameController");
println!("cargo:rustc-flags=-l framework=OpenGLES");
println!("cargo:rustc-flags=-l framework=QuartzCore");
println!("cargo:rustc-flags=-l framework=UIKit");
#[cfg(feature = "bundled")] {
let sdl2_source_path = download_sdl2();
let sdl2_compiled_path = compile_sdl2(sdl2_source_path.as_path());

let sdl2_downloaded_include_path = sdl2_source_path.join("include");
let sdl2_compiled_lib_path = sdl2_compiled_path.join("lib");

println!("cargo:rustc-link-search={}", sdl2_compiled_lib_path.display());

#[cfg(feature = "bindgen")] {
let include_paths = vec!(String::from(sdl2_downloaded_include_path.to_str().unwrap()));
generate_bindings(target.as_str(), host.as_str(), include_paths.as_slice())
}
};

#[cfg(all(not(feature = "bundled"), feature = "bindgen"))] {
let include_paths: Vec<String> = compute_include_paths();
generate_bindings(target.as_str(), host.as_str(), include_paths.as_slice())
}
}

#[cfg(not(feature="pkg-config"))]
#[cfg(not(feature="bundled"))]
fn build_pkgconfig() -> bool {
false
}
#[cfg(not(feature = "bindgen"))] {
copy_pregenerated_bindings();
}

#[cfg(feature="pkg-config")]
fn build_pkgconfig() -> bool {
pkg_config::probe_library("sdl2").is_ok()
link_sdl2(target_os);
}

#[cfg(not(feature = "bindgen"))]
fn prepare_bindings(target: &str, host: &str) {
add_explicit_linker_flags(get_os_from_triple(target).unwrap());
fn copy_pregenerated_bindings() {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let crate_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
fs::copy(crate_path.join("pregenerated_bindings.rs"), out_path.join("bindings.rs"))
.expect("Couldn't find pregenerated bindings!");
}

#[cfg(feature = "bindgen")]
fn prepare_bindings(target: &str, host: &str) {
// headers_path is a list of directories where the SDL2 headers are expected
// to be found by bindgen (should point to the include/ directories)
fn generate_bindings<S: AsRef<str> + ::std::fmt::Debug>(target: &str, host: &str, headers_paths: &[S]) {
let target_os = get_os_from_triple(target).unwrap();
let mut bindings = bindgen::Builder::default();

// Set correct target triple when cross-compiling
// Set correct target triple for bindgen when cross-compiling
if target != host {
bindings = bindings.clang_arg("-target");
bindings = bindings.clang_arg(target.clone());
}

if let Ok(include_path) = env::var("SDL2_INCLUDE_PATH") {
bindings = bindings.clang_arg(String::from("-I") + &include_path);
add_explicit_linker_flags(get_os_from_triple(target).unwrap());
} else if build_pkgconfig() {
#[cfg(feature = "pkg-config")]
for path in &pkg_config::find_library("sdl2").unwrap().include_paths {
bindings = bindings.clang_arg(String::from("-I") +
&path.clone().into_os_string().into_string().unwrap());
}
} else {
if headers_paths.len() == 0 {
// if no paths are being provided, fall back to the headers included in this repo
let mut include_path: PathBuf = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
include_path.push(String::from("SDL2-") + SDL2_BUNDLED_VERSION);
include_path.push(format!("SDL2-{}", SDL2_HEADERS_BUNDLED_VERSION));
include_path.push("include");
bindings = bindings.clang_arg(String::from("-I") +
&include_path.into_os_string().into_string().unwrap());
// SDL2 hasn't a default configuration for Linux
if get_os_from_triple(target).unwrap() == "linux" {
bindings = bindings.clang_arg("-DSDL_VIDEO_DRIVER_X11");
bindings = bindings.clang_arg("-DSDL_VIDEO_DRIVER_WAYLAND");
bindings = bindings.clang_arg(format!("-I{}", include_path.display()));
} else {
// if paths are included, use them for bindgen. Bindgen should use the first one.
for headers_path in headers_paths {
bindings = bindings.clang_arg(format!("-I{}", headers_path.as_ref()))
}
add_explicit_linker_flags(get_os_from_triple(target).unwrap());
}

// SDL2 hasn't a default configuration for Linux
if target_os == "linux" {
bindings = bindings.clang_arg("-DSDL_VIDEO_DRIVER_X11");
bindings = bindings.clang_arg("-DSDL_VIDEO_DRIVER_WAYLAND");
}

let bindings = bindings
Expand All @@ -206,12 +290,4 @@ fn prepare_bindings(target: &str, host: &str) {
fn get_os_from_triple(triple: &str) -> Option<&str>
{
triple.split("-").nth(2)
}

fn add_explicit_linker_flags(target_os: &str) {
if cfg!(feature = "use_mac_framework") && target_os == "darwin" {
println!("cargo:rustc-flags=-l framework=SDL2");
} else {
println!("cargo:rustc-flags=-l SDL2");
}
}
}

0 comments on commit c1a9f63

Please sign in to comment.