rust/hgcli/build.rs
changeset 35569 964212780daf
child 35603 11c86ab69e67
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hgcli/build.rs	Wed Jan 10 08:53:22 2018 -0800
@@ -0,0 +1,128 @@
+// 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");
+    }
+}