rust/hgcli/build.rs
changeset 35569 964212780daf
child 35603 11c86ab69e67
equal deleted inserted replaced
35568:ebf14075a5c1 35569:964212780daf
       
     1 // build.rs -- Configure build environment for `hgcli` Rust package.
       
     2 //
       
     3 // Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
       
     4 //
       
     5 // This software may be used and distributed according to the terms of the
       
     6 // GNU General Public License version 2 or any later version.
       
     7 
       
     8 use std::collections::HashMap;
       
     9 use std::env;
       
    10 use std::path::Path;
       
    11 #[cfg(target_os = "windows")]
       
    12 use std::path::PathBuf;
       
    13 
       
    14 use std::process::Command;
       
    15 
       
    16 struct PythonConfig {
       
    17     python: String,
       
    18     config: HashMap<String, String>,
       
    19 }
       
    20 
       
    21 fn get_python_config() -> PythonConfig {
       
    22     // The python27-sys crate exports a Cargo variable defining the full
       
    23     // path to the interpreter being used.
       
    24     let python = env::var("DEP_PYTHON27_PYTHON_INTERPRETER").expect(
       
    25         "Missing DEP_PYTHON27_PYTHON_INTERPRETER; bad python27-sys crate?",
       
    26     );
       
    27 
       
    28     if !Path::new(&python).exists() {
       
    29         panic!(
       
    30             "Python interpreter {} does not exist; this should never happen",
       
    31             python
       
    32         );
       
    33     }
       
    34 
       
    35     // This is a bit hacky but it gets the job done.
       
    36     let separator = "SEPARATOR STRING";
       
    37 
       
    38     let script = "import sysconfig; \
       
    39 c = sysconfig.get_config_vars(); \
       
    40 print('SEPARATOR STRING'.join('%s=%s' % i for i in c.items()))";
       
    41 
       
    42     let mut command = Command::new(&python);
       
    43     command.arg("-c").arg(script);
       
    44 
       
    45     let out = command.output().unwrap();
       
    46 
       
    47     if !out.status.success() {
       
    48         panic!(
       
    49             "python script failed: {}",
       
    50             String::from_utf8_lossy(&out.stderr)
       
    51         );
       
    52     }
       
    53 
       
    54     let stdout = String::from_utf8_lossy(&out.stdout);
       
    55     let mut m = HashMap::new();
       
    56 
       
    57     for entry in stdout.split(separator) {
       
    58         let mut parts = entry.splitn(2, "=");
       
    59         let key = parts.next().unwrap();
       
    60         let value = parts.next().unwrap();
       
    61         m.insert(String::from(key), String::from(value));
       
    62     }
       
    63 
       
    64     PythonConfig {
       
    65         python: python,
       
    66         config: m,
       
    67     }
       
    68 }
       
    69 
       
    70 #[cfg(not(target_os = "windows"))]
       
    71 fn have_shared(config: &PythonConfig) -> bool {
       
    72     match config.config.get("Py_ENABLE_SHARED") {
       
    73         Some(value) => value == "1",
       
    74         None => false,
       
    75     }
       
    76 }
       
    77 
       
    78 #[cfg(target_os = "windows")]
       
    79 fn have_shared(config: &PythonConfig) -> bool {
       
    80     // python27.dll should exist next to python2.7.exe.
       
    81     let mut dll = PathBuf::from(&config.python);
       
    82     dll.pop();
       
    83     dll.push("python27.dll");
       
    84 
       
    85     return dll.exists();
       
    86 }
       
    87 
       
    88 const REQUIRED_CONFIG_FLAGS: [&'static str; 2] = ["Py_USING_UNICODE", "WITH_THREAD"];
       
    89 
       
    90 fn main() {
       
    91     let config = get_python_config();
       
    92 
       
    93     println!("Using Python: {}", config.python);
       
    94     println!("cargo:rustc-env=PYTHON_INTERPRETER={}", config.python);
       
    95 
       
    96     let prefix = config.config.get("prefix").unwrap();
       
    97 
       
    98     println!("Prefix: {}", prefix);
       
    99 
       
   100     // TODO Windows builds don't expose these config flags. Figure out another
       
   101     // way.
       
   102     #[cfg(not(target_os = "windows"))]
       
   103     for key in REQUIRED_CONFIG_FLAGS.iter() {
       
   104         let result = match config.config.get(*key) {
       
   105             Some(value) => value == "1",
       
   106             None => false,
       
   107         };
       
   108 
       
   109         if !result {
       
   110             panic!("Detected Python requires feature {}", key);
       
   111         }
       
   112     }
       
   113 
       
   114     // We need a Python shared library.
       
   115     if !have_shared(&config) {
       
   116         panic!("Detected Python lacks a shared library, which is required");
       
   117     }
       
   118 
       
   119     let ucs4 = match config.config.get("Py_UNICODE_SIZE") {
       
   120         Some(value) => value == "4",
       
   121         None => false,
       
   122     };
       
   123 
       
   124     if !ucs4 {
       
   125         #[cfg(not(target_os = "windows"))]
       
   126         panic!("Detected Python doesn't support UCS-4 code points");
       
   127     }
       
   128 }