#16: Try "python" first and only try "pythonX" or "pythonX.Y" if the previous

tries result in the wrong python version.
This commit is contained in:
Daniel Grunwald 2015-07-04 18:46:37 +02:00
parent 43552a075d
commit dae7c891dc
2 changed files with 131 additions and 97 deletions

View File

@ -6,6 +6,7 @@ use std::collections::HashMap;
use std::env;
use regex::Regex;
use std::fs;
use std::fmt;
struct PythonVersion {
major: u8,
@ -13,6 +14,18 @@ struct PythonVersion {
minor: Option<u8>
}
impl fmt::Display for PythonVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
try!(self.major.fmt(f));
try!(f.write_str("."));
match self.minor {
Some(minor) => try!(minor.fmt(f)),
None => try!(f.write_str("*"))
};
Ok(())
}
}
const CFG_KEY: &'static str = "py_sys_config";
// windows' python writes out lines with the windows crlf sequence;
@ -166,20 +179,6 @@ fn run_python_script(interpreter: &str, script: &str) -> Result<String, String>
return Ok(out);
}
fn run_python_script_try_interpreters(version: &PythonVersion, script: &str) -> Result<String, String> {
if let Some(minor) = version.minor {
let interpreter = format!("python{}.{}", version.major, minor);
if let Ok(r) = run_python_script(&interpreter, script) {
return Ok(r);
}
}
let interpreter = format!("python{}", version.major);
if let Ok(r) = run_python_script(&interpreter, script) {
return Ok(r);
}
run_python_script("python", script)
}
#[cfg(not(target_os="macos"))]
#[cfg(not(target_os="windows"))]
fn get_rustc_link_lib(version: &PythonVersion, enable_shared: bool) -> Result<String, String> {
@ -213,34 +212,16 @@ fn get_rustc_link_lib(version: &PythonVersion, _: bool) -> Result<String, String
other => Err(format!("unknown linkmodel {}", other))
}
}
/// Check that the interpreter's reported version matches expected_version;
/// if it does, return the specific version the interpreter reports.
fn get_interpreter_version(line: &str, expected_version: &PythonVersion)
-> Result<PythonVersion, String> {
/// Parse string as interpreter version.
fn get_interpreter_version(line: &str) -> Result<PythonVersion, String> {
let version_re = Regex::new(r"\((\d+), (\d+)\)").unwrap();
let interpreter_version = match version_re.captures(&line) {
Some(cap) => PythonVersion {
match version_re.captures(&line) {
Some(cap) => Ok(PythonVersion {
major: cap.at(1).unwrap().parse().unwrap(),
minor: Some(cap.at(2).unwrap().parse().unwrap())
},
None => return Err(
}),
None => Err(
format!("Unexpected response to version query {}", line))
};
if interpreter_version.major != expected_version.major ||
(expected_version.minor.is_some() &&
interpreter_version.minor != expected_version.minor)
{
Err(format!("'python' is not version {}.{} (is {})",
expected_version.major,
match expected_version.minor {
Some(v) => v.to_string(),
None => "*".to_owned()
},
line))
} else {
Ok(interpreter_version)
}
}
@ -254,24 +235,59 @@ fn get_rustc_link_lib(version: &PythonVersion, _: bool) -> Result<String, String
}))
}
fn matching_version(expected_version: &PythonVersion, actual_version: &PythonVersion) -> bool {
actual_version.major == expected_version.major &&
(expected_version.minor.is_none() ||
actual_version.minor == expected_version.minor)
}
fn find_interpreter_and_get_config(expected_version: &PythonVersion) -> Result<(PythonVersion, Vec<String>), String> {
let (interpreter_version, lines) = try!(get_config_from_interpreter("python"));
if matching_version(expected_version, &interpreter_version) {
return Ok((interpreter_version, lines));
}
{
let (interpreter_version, lines) = try!(get_config_from_interpreter(
&format!("python{}", expected_version.major)));
if matching_version(expected_version, &interpreter_version) {
return Ok((interpreter_version, lines));
}
}
if let Some(minor) = expected_version.minor {
let (interpreter_version, lines) = try!(get_config_from_interpreter(
&format!("python{}.{}", expected_version.major, minor)));
if matching_version(expected_version, &interpreter_version) {
return Ok((interpreter_version, lines));
}
}
Err(format!("'python' is not version {} (is {})",
expected_version, interpreter_version))
}
/// Deduce configuration from the 'python' in the current PATH and print
/// cargo vars to stdout.
///
/// Note that if the python doesn't satisfy expected_version, this will error.
fn get_config_from_interpreter(interpreter: &str) -> Result<(PythonVersion, Vec<String>), String> {
let script = "import sys; import sysconfig; print(sys.version_info[0:2]); \
print(sysconfig.get_config_var('LIBDIR')); \
print(sysconfig.get_config_var('Py_ENABLE_SHARED')); \
print(sys.exec_prefix);";
let out = try!(run_python_script(interpreter, script));
let lines: Vec<String> = out.split(NEWLINE_SEQUENCE).map(|line| line.to_owned()).collect();
let interpreter_version = try!(get_interpreter_version(&lines[0]));
Ok((interpreter_version, lines))
}
/// Deduce configuration from the 'python' in the current PATH and print
/// cargo vars to stdout.
///
/// Note that if the python doesn't satisfy expected_version, this will error.
fn configure_from_path(expected_version: &PythonVersion) -> Result<String, String> {
let script = "import sys; import sysconfig; print(sys.version_info[0:2]); \
print(sysconfig.get_config_var('LIBDIR')); \
print(sysconfig.get_config_var('Py_ENABLE_SHARED')); \
print(sys.exec_prefix);";
let out = run_python_script_try_interpreters(expected_version, script).unwrap();
let lines: Vec<&str> = out.split(NEWLINE_SEQUENCE).collect();
let version: &str = lines[0];
let libpath: &str = lines[1];
let enable_shared: &str = lines[2];
let exec_prefix: &str = lines[3];
let interpreter_version = try!(get_interpreter_version(version,
expected_version));
let (interpreter_version, lines) = try!(find_interpreter_and_get_config(expected_version));
let libpath: &str = &lines[1];
let enable_shared: &str = &lines[2];
let exec_prefix: &str = &lines[3];
println!("{}", get_rustc_link_lib(&interpreter_version,
enable_shared == "1").unwrap());

View File

@ -6,6 +6,7 @@ use std::collections::HashMap;
use std::env;
use regex::Regex;
use std::fs;
use std::fmt;
struct PythonVersion {
major: u8,
@ -13,6 +14,18 @@ struct PythonVersion {
minor: Option<u8>
}
impl fmt::Display for PythonVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
try!(self.major.fmt(f));
try!(f.write_str("."));
match self.minor {
Some(minor) => try!(minor.fmt(f)),
None => try!(f.write_str("*"))
};
Ok(())
}
}
const CFG_KEY: &'static str = "py_sys_config";
// windows' python writes out lines with the windows crlf sequence;
@ -166,20 +179,6 @@ fn run_python_script(interpreter: &str, script: &str) -> Result<String, String>
return Ok(out);
}
fn run_python_script_try_interpreters(version: &PythonVersion, script: &str) -> Result<String, String> {
if let Some(minor) = version.minor {
let interpreter = format!("python{}.{}", version.major, minor);
if let Ok(r) = run_python_script(&interpreter, script) {
return Ok(r);
}
}
let interpreter = format!("python{}", version.major);
if let Ok(r) = run_python_script(&interpreter, script) {
return Ok(r);
}
run_python_script("python", script)
}
#[cfg(not(target_os="macos"))]
#[cfg(not(target_os="windows"))]
fn get_rustc_link_lib(_: &PythonVersion, ld_version: &str, enable_shared: bool) -> Result<String, String> {
@ -212,33 +211,16 @@ fn get_rustc_link_lib(_: &PythonVersion, ld_version: &str, _: bool) -> Result<St
}
}
/// Check that the interpreter's reported version matches expected_version;
/// if it does, return the specific version the interpreter reports.
fn get_interpreter_version(line: &str, expected_version: &PythonVersion)
-> Result<PythonVersion, String> {
/// Parse string as interpreter version.
fn get_interpreter_version(line: &str) -> Result<PythonVersion, String> {
let version_re = Regex::new(r"\((\d+), (\d+)\)").unwrap();
let interpreter_version = match version_re.captures(&line) {
Some(cap) => PythonVersion {
match version_re.captures(&line) {
Some(cap) => Ok(PythonVersion {
major: cap.at(1).unwrap().parse().unwrap(),
minor: Some(cap.at(2).unwrap().parse().unwrap())
},
None => return Err(
}),
None => Err(
format!("Unexpected response to version query {}", line))
};
if interpreter_version.major != expected_version.major ||
(expected_version.minor.is_some() &&
interpreter_version.minor != expected_version.minor)
{
Err(format!("'python' is not version {}.{} (is {})",
expected_version.major,
match expected_version.minor {
Some(v) => v.to_string(),
None => "*".to_owned()
},
line))
} else {
Ok(interpreter_version)
}
}
@ -252,25 +234,61 @@ fn get_rustc_link_lib(version: &PythonVersion, _: &str, _: bool) -> Result<Strin
}))
}
fn matching_version(expected_version: &PythonVersion, actual_version: &PythonVersion) -> bool {
actual_version.major == expected_version.major &&
(expected_version.minor.is_none() ||
actual_version.minor == expected_version.minor)
}
fn find_interpreter_and_get_config(expected_version: &PythonVersion) -> Result<(PythonVersion, Vec<String>), String> {
let (interpreter_version, lines) = try!(get_config_from_interpreter("python"));
if matching_version(expected_version, &interpreter_version) {
return Ok((interpreter_version, lines));
}
{
let (interpreter_version, lines) = try!(get_config_from_interpreter(
&format!("python{}", expected_version.major)));
if matching_version(expected_version, &interpreter_version) {
return Ok((interpreter_version, lines));
}
}
if let Some(minor) = expected_version.minor {
let (interpreter_version, lines) = try!(get_config_from_interpreter(
&format!("python{}.{}", expected_version.major, minor)));
if matching_version(expected_version, &interpreter_version) {
return Ok((interpreter_version, lines));
}
}
Err(format!("'python' is not version {} (is {})",
expected_version, interpreter_version))
}
/// Deduce configuration from the 'python' in the current PATH and print
/// cargo vars to stdout.
///
/// Note that if the python doesn't satisfy expected_version, this will error.
fn configure_from_path(expected_version: &PythonVersion) -> Result<String, String> {
fn get_config_from_interpreter(interpreter: &str) -> Result<(PythonVersion, Vec<String>), String> {
let script = "import sys; import sysconfig; print(sys.version_info[0:2]); \
print(sysconfig.get_config_var('LIBDIR')); \
print(sysconfig.get_config_var('Py_ENABLE_SHARED')); \
print(sysconfig.get_config_var('LDVERSION')); \
print(sys.exec_prefix);";
let out = run_python_script_try_interpreters(expected_version, script).unwrap();
let lines: Vec<&str> = out.split(NEWLINE_SEQUENCE).collect();
let version: &str = lines[0];
let libpath: &str = lines[1];
let enable_shared: &str = lines[2];
let ld_version: &str = lines[3];
let exec_prefix: &str = lines[4];
let out = try!(run_python_script(interpreter, script));
let lines: Vec<String> = out.split(NEWLINE_SEQUENCE).map(|line| line.to_owned()).collect();
let interpreter_version = try!(get_interpreter_version(&lines[0]));
Ok((interpreter_version, lines))
}
let interpreter_version = try!(get_interpreter_version(version, expected_version));
/// Deduce configuration from the 'python' in the current PATH and print
/// cargo vars to stdout.
///
/// Note that if the python doesn't satisfy expected_version, this will error.
fn configure_from_path(expected_version: &PythonVersion) -> Result<String, String> {
let (interpreter_version, lines) = try!(find_interpreter_and_get_config(expected_version));
let libpath: &str = &lines[1];
let enable_shared: &str = &lines[2];
let ld_version: &str = &lines[3];
let exec_prefix: &str = &lines[4];
println!("{}", get_rustc_link_lib(&interpreter_version,
ld_version, enable_shared == "1").unwrap());