diff --git a/README.md b/README.md index 5f8bc93..03f5801 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,14 @@ On Mac OS X, you may need to install manually `portaudio` and `pkg-config` (usin You can build the tests and examples with `cargo test`, and the documentation with `cargo doc`. +# Installation on windows-msvc + +On the windows-msvc, **rust-portaudio-sys** uses the Visual Studio solution file to build portaudio. +So, the following programs must be put through the system `PATH`. + +- **Git**: getting the source code from repository +- **devenv**: updating the solution file in the repository of portaudio +- **MSBuild**: building PortAudio statically + +It is remark that cargo does not read the user-wise path, so you must add these programs to the system path. + diff --git a/rust-portaudio-sys/Cargo.toml b/rust-portaudio-sys/Cargo.toml index 98a8028..77234ce 100644 --- a/rust-portaudio-sys/Cargo.toml +++ b/rust-portaudio-sys/Cargo.toml @@ -17,3 +17,4 @@ crate-type = ["rlib"] [build-dependencies] pkg-config = "0.3.6" +force_remove = "0.1.1" diff --git a/rust-portaudio-sys/build.rs b/rust-portaudio-sys/build.rs index af2eb17..62c5e2e 100644 --- a/rust-portaudio-sys/build.rs +++ b/rust-portaudio-sys/build.rs @@ -29,13 +29,22 @@ use std::process::Command; #[cfg(all(unix, not(target_os = "linux")))] use unix_platform as platform; +#[cfg(not(target_os = "windows"))] +const PORTAUDIO_LIBRARY: &'static str = "libportaudio.a"; +#[cfg(target_os = "windows")] +const PORTAUDIO_LIBRARY: &'static str = "portaudio.lib"; + fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=PORTAUDIO_ONLY_STATIC"); if env::var("PORTAUDIO_ONLY_STATIC").is_err() { // If pkg-config finds a library on the system, we are done - if pkg_config::Config::new().atleast_version("19").find("portaudio-2.0").is_ok() { + if pkg_config::Config::new() + .atleast_version("19") + .find("portaudio-2.0") + .is_ok() + { return; } } @@ -48,7 +57,7 @@ fn build() { let out_dir_str = env::var("OUT_DIR").unwrap(); let out_dir = Path::new(&out_dir_str); - let static_lib = out_dir.join("lib/libportaudio.a"); + let static_lib = out_dir.join("lib/").join(PORTAUDIO_LIBRARY); if let Err(_) = ::std::fs::metadata(static_lib) { platform::download(); platform::build(out_dir); @@ -62,7 +71,7 @@ fn build() { fn err_to_panic(result: Result) -> T { match result { Ok(x) => x, - Err(e) => panic!("{}", e) + Err(e) => panic!("{}", e), } } @@ -76,14 +85,15 @@ fn run(command: &mut Command) { #[allow(dead_code)] mod unix_platform { - use std::process::Command; use std::path::Path; + use std::process::Command; use std::env; use super::{err_to_panic, run}; - pub const PORTAUDIO_URL: &'static str = "http://www.portaudio.com/archives/pa_stable_v19_20140130.tgz"; + pub const PORTAUDIO_URL: &'static str = + "http://www.portaudio.com/archives/pa_stable_v19_20140130.tgz"; pub const PORTAUDIO_TAR: &'static str = "pa_stable_v19_20140130.tgz"; pub const PORTAUDIO_FOLDER: &'static str = "portaudio"; @@ -114,24 +124,28 @@ mod unix_platform { err_to_panic(env::set_current_dir("..")); // cleaning portaudio sources - run(Command::new("rm").arg("-rf") + run(Command::new("rm") + .arg("-rf") .args(&[PORTAUDIO_TAR, PORTAUDIO_FOLDER])); } pub fn print_libs(out_dir: &Path) { let out_str = out_dir.to_str().unwrap(); - println!("cargo:rustc-flags=-L native={}/lib -l static=portaudio", out_str); + println!( + "cargo:rustc-flags=-L native={}/lib -l static=portaudio", + out_str + ); } } #[cfg(target_os = "linux")] mod platform { - use pkg_config; - use std::process::Command; use super::unix_platform; + use pkg_config; use std::path::Path; + use std::process::Command; - use super::{run, err_to_panic}; + use super::{err_to_panic, run}; pub fn download() { run(Command::new("wget").arg(unix_platform::PORTAUDIO_URL)); @@ -145,29 +159,104 @@ mod platform { let portaudio_pc_file = out_dir.join("lib/pkgconfig/portaudio-2.0.pc"); let portaudio_pc_file = portaudio_pc_file.to_str().unwrap(); - err_to_panic(pkg_config::Config::new().statik(true).find(portaudio_pc_file)); + err_to_panic( + pkg_config::Config::new() + .statik(true) + .find(portaudio_pc_file), + ); } } #[cfg(windows)] mod platform { + use std::{env, fs}; use std::path::Path; + use std::process::Command; - const PORTAUDIO_DOWNLOAD_URL: &'static str = "http://www.portaudio.com"; + use super::{err_to_panic, run}; + + const PORTAUDIO_REPOSITORY: &'static str = "https://git.assembla.com/portaudio.git"; + const PORTAUDIO_STABLE_BRANCH: &'static str = "pa_stable_v190600_20161030"; + const PORTAUDIO_FOLDER: &'static str = "portaudio"; + const STEINBERG_URL: &'static str = "http://www.steinberg.net/sdk_downloads/"; + const ASIOSDK_ZIP: &'static str = "asiosdk2.3.zip"; + const ASIOSDK_EXPANDED_FOLDER: &'static str = "asiosdk2.3"; + const ASIOSDK_EXPANDED_ENTITY: &'static str = "asiosdk2.3\\ASIOSDK2.3"; + const ASIOSDK_SPECIFIED_LOCATION: &'static str = "portaudio\\src\\hostapi\\asio\\ASIOSDK"; + const PORTAUDIO_BUILD_DIR: &'static str = "portaudio\\build\\msvc"; + const PORTAUDIO_SOLUTION: &'static str = "portaudio.sln"; + const PORTAUDIO_PROJECT: &'static str = "portaudio.vcxproj"; + const MSBUILD_BUILD_CONFIG: &'static str = + "/p:Configuration=Release;Platform=x64;ConfigurationType=StaticLibrary"; + const MSBUILD_TARGET_DIRECTORY: &'static str = "x64\\Release"; + + /// Gets the source of portaudio from git repository. + /// Windows users use different tar expanding tools, but Git is uniformly consistent. + fn download_portaudio() { + // If the source code from the previous build remains, delete it. + if Path::new(PORTAUDIO_FOLDER).exists() { + err_to_panic(force_remove::force_remove_dir_all(PORTAUDIO_FOLDER)); + } + run(Command::new("git").args(&["clone", PORTAUDIO_REPOSITORY])); + // Checkouts the latest stable branch + err_to_panic(env::set_current_dir(PORTAUDIO_FOLDER)); + run(Command::new("git").args(&["checkout", PORTAUDIO_STABLE_BRANCH])); + // Backs to the project dir + err_to_panic(env::set_current_dir("..")); + } - fn print_lib_url() { - panic!("Don't know how to build portaudio on Windows yet. Sources and build instructions available at: {}", PORTAUDIO_DOWNLOAD_URL); + /// Gets the ASIO SDK from Steinberg's web site. Places the SDK in the specified location. + fn download_asiosdk() { + // Windows' standard wget "bitsadmin" requires the full path to the installation location. + let asiosdk_url = &format!("{}{}", STEINBERG_URL, ASIOSDK_ZIP); + let path = env::current_dir().unwrap(); + let zip_fullpath = &format!("{}\\{}", path.display(), ASIOSDK_ZIP); + run(Command::new("bitsadmin").args(&["/TRANSFER", "htmlget", asiosdk_url, zip_fullpath])); + + // unzip + run(Command::new("powershell").args(&["expand-archive", ASIOSDK_ZIP])); + + // Places the SDK in the specified location, and removes a zip and an empty directory. + err_to_panic(fs::rename( + ASIOSDK_EXPANDED_ENTITY, + ASIOSDK_SPECIFIED_LOCATION, + )); + err_to_panic(fs::remove_file(ASIOSDK_ZIP)); + err_to_panic(fs::remove_dir(ASIOSDK_EXPANDED_FOLDER)); } pub fn download() { - print_lib_url(); + download_portaudio(); + download_asiosdk(); } - pub fn build(_: &Path) { - print_lib_url(); + pub fn build(out_dir: &Path) { + // Backups the current dir + let path = env::current_dir().unwrap(); + + // Builds portaudio! + err_to_panic(env::set_current_dir(PORTAUDIO_BUILD_DIR)); + run(Command::new("devenv").args(&[PORTAUDIO_SOLUTION, "/Upgrade"])); + run(Command::new("msbuild").args(&[PORTAUDIO_PROJECT, MSBUILD_BUILD_CONFIG])); + + // Places the library to the target. + let prefix = &format!("{}\\lib", out_dir.display()); + err_to_panic(fs::create_dir_all(prefix)); + let current_portoudio_path = + &format!("{}\\{}", MSBUILD_TARGET_DIRECTORY, super::PORTAUDIO_LIBRARY); + let portaudio_target_path = + &format!("{}\\lib\\{}", out_dir.display(), super::PORTAUDIO_LIBRARY); + err_to_panic(fs::rename(current_portoudio_path, portaudio_target_path)); + + // Backs to the current dir and removes the portaudio directory. + err_to_panic(env::set_current_dir(path)); + err_to_panic(force_remove::force_remove_dir_all(PORTAUDIO_FOLDER)); } - pub fn print_libs(_: &Path) { - print_lib_url(); + pub fn print_libs(out_dir: &Path) { + println!( + "cargo:rustc-flags=-L native={}/lib -l static=portaudio", + out_dir.display() + ); } }