use std::env; use std::ffi::OsStr; use std::ffi::OsString; use std::fs; use std::io; use std::path::{Path, PathBuf}; use std::process::Command; use cc; trait CommandExt { fn execute(&mut self) -> io::Result<()>; } impl CommandExt for Command { fn execute(&mut self) -> io::Result<()> { let status = self.status()?; if status.success() { Ok(()) } else { Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\ \t{:?}\n\ did not run successfully.", self))) } } } fn build_lua(tooling: &cc::Tool, source: &Path, build: &Path) -> io::Result<()> { let platform = match env::var("TARGET").unwrap().split('-').nth(2).unwrap() { "windows" => "mingw", "darwin" => "macosx", "linux" => "linux", "freebsd" => "freebsd", "dragonfly" => "bsd", _ => "generic", }; let cc = tooling.path(); let mut cflags = OsString::new(); for arg in tooling.args() { cflags.push(arg); cflags.push(" "); } let makefile = source.join("Makefile"); let make = OsString::from(format!("make -e -f {:?}", makefile.to_string_lossy().replace("\\", "/"))); let mut command = Command::new("make"); for &(ref key, ref val) in tooling.env() { command.env(key, val); } command.current_dir(build) .env("VPATH", source.to_string_lossy().replace("\\", "/")) .env("MAKE", make) .env("CC", cc) .env("MYCFLAGS", cflags) .arg("-e") .arg("-f").arg(makefile) .arg(platform) .execute() } fn verify_msvc_environment() { let found_cl_exe = Command::new("cl.exe").arg("/help").output().is_ok(); let found_lib_exe = Command::new("lib.exe").arg("/help").output().is_ok(); if !found_cl_exe || !found_lib_exe { panic!("cl.exe and lib.exe must be on your %PATH% to compile Lua for MSVC.\n\ Please install this crate through the Visual Studio Native Tools Command Line."); } } fn build_lua_msvc(source: &Path, build: &Path) -> io::Result<()> { verify_msvc_environment(); let build_str = build.as_os_str().to_str().unwrap(); let mut compile_cmd = Command::new("cl.exe"); compile_cmd.current_dir(&source); for file_res in fs::read_dir(source).unwrap() { let dir_entry = file_res.unwrap(); let file_name = dir_entry.file_name().into_string().unwrap(); if file_name.ends_with(".c") && file_name != "luac.c" { compile_cmd.arg(file_name); } } compile_cmd.arg("/c") .arg("/MP") .arg(format!("/Fo{}\\", &build_str)) .arg("/nologo"); compile_cmd.execute().unwrap(); let mut lib_cmd = Command::new("lib.exe"); lib_cmd.current_dir(&build); for file_res in fs::read_dir(build).unwrap() { let dir_entry = file_res.unwrap(); let file_name = dir_entry.file_name().into_string().unwrap(); if file_name.ends_with(".obj") { lib_cmd.arg(file_name); } } lib_cmd.arg(format!("/out:{}\\lua.lib", &build_str)) .arg("/NOLOGO"); lib_cmd.execute() } fn prebuild() -> io::Result<()> { let lua_dir: PathBuf = match env::var_os("LUA_LOCAL_SOURCE") { Some(dir) => PathBuf::from(dir), None => { let mut dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); dir.push(OsStr::new("lua/src").to_str().unwrap()); dir } }; let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let mut config = cc::Build::new(); let msvc = env::var("TARGET").unwrap().split('-').last().unwrap() == "msvc"; println!("cargo:rustc-link-lib=static=lua"); if !msvc && lua_dir.join("liblua.a").exists() { println!("cargo:rustc-link-search=native={}", &lua_dir.display()); } else if msvc { if !build_dir.join("lua.lib").exists() { build_lua_msvc(&lua_dir, &build_dir)?; } println!("cargo:rustc-link-search=native={}", &build_dir.display()); } else { if !build_dir.join("liblua.a").exists() { let tooling = config.get_compiler(); fs::create_dir_all(&build_dir)?; build_lua(&tooling, &lua_dir, &build_dir)?; } println!("cargo:rustc-link-search=native={}", &build_dir.display()); } if !build_dir.join("glue.rs").exists() { let glue = build_dir.join("glue"); config.include(&lua_dir).get_compiler().to_command() .arg("-I").arg(&lua_dir) .arg("src/glue/glue.c") .arg("-o").arg(&glue) .execute()?; Command::new(glue) .arg(build_dir.join("glue.rs")) .execute()?; } Ok(()) } fn main() { match prebuild() { Err(e) => panic!("Error: {}", e), Ok(()) => (), } }