build.rs
128 lines
| 3.4 KiB
| application/rls-services+xml
|
RustLexer
Gregory Szorc
|
r35587 | // build.rs -- Configure build environment for `hgcli` Rust package. | ||
// | ||||
// Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com> | ||||
// | ||||
// This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | ||||
use std::collections::HashMap; | ||||
use std::env; | ||||
use std::path::Path; | ||||
#[cfg(target_os = "windows")] | ||||
use std::path::PathBuf; | ||||
use std::process::Command; | ||||
struct PythonConfig { | ||||
python: String, | ||||
config: HashMap<String, String>, | ||||
} | ||||
fn get_python_config() -> PythonConfig { | ||||
// The python27-sys crate exports a Cargo variable defining the full | ||||
// path to the interpreter being used. | ||||
let python = env::var("DEP_PYTHON27_PYTHON_INTERPRETER").expect( | ||||
"Missing DEP_PYTHON27_PYTHON_INTERPRETER; bad python27-sys crate?", | ||||
); | ||||
if !Path::new(&python).exists() { | ||||
panic!( | ||||
"Python interpreter {} does not exist; this should never happen", | ||||
python | ||||
); | ||||
} | ||||
// This is a bit hacky but it gets the job done. | ||||
let separator = "SEPARATOR STRING"; | ||||
let script = "import sysconfig; \ | ||||
c = sysconfig.get_config_vars(); \ | ||||
print('SEPARATOR STRING'.join('%s=%s' % i for i in c.items()))"; | ||||
let mut command = Command::new(&python); | ||||
command.arg("-c").arg(script); | ||||
let out = command.output().unwrap(); | ||||
if !out.status.success() { | ||||
panic!( | ||||
"python script failed: {}", | ||||
String::from_utf8_lossy(&out.stderr) | ||||
); | ||||
} | ||||
let stdout = String::from_utf8_lossy(&out.stdout); | ||||
let mut m = HashMap::new(); | ||||
for entry in stdout.split(separator) { | ||||
let mut parts = entry.splitn(2, "="); | ||||
let key = parts.next().unwrap(); | ||||
let value = parts.next().unwrap(); | ||||
m.insert(String::from(key), String::from(value)); | ||||
} | ||||
PythonConfig { | ||||
python: python, | ||||
config: m, | ||||
} | ||||
} | ||||
#[cfg(not(target_os = "windows"))] | ||||
fn have_shared(config: &PythonConfig) -> bool { | ||||
match config.config.get("Py_ENABLE_SHARED") { | ||||
Some(value) => value == "1", | ||||
None => false, | ||||
} | ||||
} | ||||
#[cfg(target_os = "windows")] | ||||
fn have_shared(config: &PythonConfig) -> bool { | ||||
// python27.dll should exist next to python2.7.exe. | ||||
let mut dll = PathBuf::from(&config.python); | ||||
dll.pop(); | ||||
dll.push("python27.dll"); | ||||
return dll.exists(); | ||||
} | ||||
const REQUIRED_CONFIG_FLAGS: [&'static str; 2] = ["Py_USING_UNICODE", "WITH_THREAD"]; | ||||
fn main() { | ||||
let config = get_python_config(); | ||||
println!("Using Python: {}", config.python); | ||||
println!("cargo:rustc-env=PYTHON_INTERPRETER={}", config.python); | ||||
let prefix = config.config.get("prefix").unwrap(); | ||||
println!("Prefix: {}", prefix); | ||||
// TODO Windows builds don't expose these config flags. Figure out another | ||||
// way. | ||||
#[cfg(not(target_os = "windows"))] | ||||
for key in REQUIRED_CONFIG_FLAGS.iter() { | ||||
let result = match config.config.get(*key) { | ||||
Some(value) => value == "1", | ||||
None => false, | ||||
}; | ||||
if !result { | ||||
panic!("Detected Python requires feature {}", key); | ||||
} | ||||
} | ||||
// We need a Python shared library. | ||||
if !have_shared(&config) { | ||||
panic!("Detected Python lacks a shared library, which is required"); | ||||
} | ||||
let ucs4 = match config.config.get("Py_UNICODE_SIZE") { | ||||
Some(value) => value == "4", | ||||
None => false, | ||||
}; | ||||
if !ucs4 { | ||||
#[cfg(not(target_os = "windows"))] | ||||
panic!("Detected Python doesn't support UCS-4 code points"); | ||||
} | ||||
} | ||||