use anyhow::Result;
use std::env::var;
use std::fs::{canonicalize, File};
use std::io::{copy, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use zip::ZipArchive;
fn download_archive_windows(out_dir: &Path) -> Result<()> {
if out_dir.join("libfftw3.dll").exists() && out_dir.join("libfftw3f.dll").exists() {
return Ok(());
}
let archive = out_dir.join("fftw_windows.zip");
if !archive.exists() {
let mut conn = ftp::FtpStream::connect("ftp.fftw.org:21")?;
conn.login("anonymous", "anonymous")?;
conn.cwd("pub/fftw")?;
let buf = conn.simple_retr("fftw-3.3.5-dll64.zip")?.into_inner();
let mut f = File::create(&archive)?;
f.write(&buf)?;
}
let f = File::open(&archive)?;
let mut zip = ZipArchive::new(f)?;
let target = var("TARGET").unwrap();
for name in &["fftw3-3", "fftw3f-3"] {
for ext in &["dll", "def"] {
let filename = format!("lib{}.{}", name, ext);
let mut zf = zip.by_name(&filename)?;
let mut f = File::create(out_dir.join(filename))?;
copy(&mut zf, &mut f)?;
}
run(cc::windows_registry::find_tool(&target, "lib.exe")
.unwrap()
.to_command()
.arg("/MACHINE:X64")
.arg(format!("/DEF:lib{}.def", name))
.arg(format!("/OUT:lib{}.lib", name))
.current_dir(out_dir))
}
Ok(())
}
fn build_unix(out_dir: &Path) {
let src_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap()).join("fftw-3.3.8");
let out_src_dir = out_dir.join("src");
fs_extra::dir::copy(
src_dir,
&out_src_dir,
&fs_extra::dir::CopyOptions {
overwrite: true,
skip_exist: false,
buffer_size: 64000,
copy_inside: true,
depth: 0,
content_only: false,
},
)
.unwrap();
if !out_dir.join("lib/libfftw3.a").exists() {
build_fftw(&[], &out_src_dir, &out_dir);
}
if !out_dir.join("lib/libfftw3f.a").exists() {
build_fftw(&["--enable-single"], &out_src_dir, &out_dir);
}
}
fn build_fftw(flags: &[&str], src_dir: &Path, out_dir: &Path) {
run(
Command::new(canonicalize(src_dir.join("configure")).unwrap())
.arg("--with-pic")
.arg("--enable-static")
.arg("--disable-doc")
.arg(format!("--prefix={}", out_dir.display()))
.args(flags)
.current_dir(&src_dir),
);
run(Command::new("make")
.arg(format!("-j{}", var("NUM_JOBS").unwrap()))
.current_dir(&src_dir));
run(Command::new("make").arg("install").current_dir(&src_dir));
}
fn run(command: &mut Command) {
println!("Running: {:?}", command);
match command.status() {
Ok(status) => {
if !status.success() {
panic!("`{:?}` failed: {}", command, status);
}
}
Err(error) => {
panic!("failed to execute `{:?}`: {}", command, error);
}
}
}
fn main() {
let out_dir = PathBuf::from(var("OUT_DIR").unwrap());
if cfg!(target_os = "windows") {
download_archive_windows(&out_dir).unwrap();
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rustc-link-lib=libfftw3-3");
println!("cargo:rustc-link-lib=libfftw3f-3");
} else {
build_unix(&out_dir);
println!("cargo:rustc-link-search={}", out_dir.join("lib").display());
println!("cargo:rustc-link-lib=static=fftw3");
println!("cargo:rustc-link-lib=static=fftw3f");
}
}