--- /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");
+ }
+}