Skip to content

Commit c4e68e6

Browse files
authored
Update build.rs and remove serde and regex dependencies. (#896)
1 parent 0f07cf8 commit c4e68e6

File tree

2 files changed

+88
-77
lines changed

2 files changed

+88
-77
lines changed

Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@ assert_approx_eq = "1.1.0"
3535
trybuild = "1.0.23"
3636

3737
[build-dependencies]
38-
regex = { version = "1", default_features = false, features = ["std"] }
3938
version_check = "0.9.1"
40-
serde = { version = "1.0", features = ["derive"] }
41-
serde_json = "1.0"
4239

4340
[features]
4441
default = []

build.rs

Lines changed: 88 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
use regex::Regex;
2-
use serde::Deserialize;
3-
use std::io::{self, BufRead, BufReader};
4-
use std::process::{Command, Stdio};
5-
use std::{collections::HashMap, convert::AsRef, env, fmt, fs::File, path::Path};
1+
use std::{
2+
collections::HashMap,
3+
convert::AsRef,
4+
env, fmt,
5+
fs::File,
6+
io::{self, BufRead, BufReader},
7+
path::{Path, PathBuf},
8+
process::{Command, Stdio},
9+
str::FromStr,
10+
};
611
use version_check::{Channel, Date, Version};
712

813
/// Specifies the minimum nightly version needed to compile pyo3.
@@ -23,25 +28,25 @@ macro_rules! bail {
2328
}
2429

2530
/// Information returned from python interpreter
26-
#[derive(Deserialize, Debug)]
31+
#[derive(Debug)]
2732
struct InterpreterConfig {
2833
version: PythonVersion,
2934
libdir: Option<String>,
3035
shared: bool,
3136
ld_version: String,
3237
/// Prefix used for determining the directory of libpython
3338
base_prefix: String,
34-
executable: String,
39+
executable: PathBuf,
3540
calcsize_pointer: Option<u32>,
3641
}
3742

38-
#[derive(Deserialize, Debug, Clone, PartialEq)]
43+
#[derive(Debug, Clone, PartialEq)]
3944
pub enum PythonInterpreterKind {
4045
CPython,
4146
PyPy,
4247
}
4348

44-
#[derive(Deserialize, Debug, Clone)]
49+
#[derive(Debug, Clone)]
4550
struct PythonVersion {
4651
major: u8,
4752
// minor == None means any minor version will do
@@ -67,6 +72,17 @@ impl fmt::Display for PythonVersion {
6772
}
6873
}
6974

75+
impl FromStr for PythonInterpreterKind {
76+
type Err = Box<dyn std::error::Error>;
77+
fn from_str(s: &str) -> Result<Self> {
78+
match s {
79+
"CPython" => Ok(Self::CPython),
80+
"PyPy" => Ok(Self::PyPy),
81+
_ => Err(format!("Invalid interpreter: {}", s).into()),
82+
}
83+
}
84+
}
85+
7086
/// A list of python interpreter compile-time preprocessor defines that
7187
/// we will pick up and pass to rustc via --cfg=py_sys_config={varname};
7288
/// this allows using them conditional cfg attributes in the .rs files, so
@@ -101,22 +117,15 @@ static SYSCONFIG_VALUES: [&str; 1] = [
101117
/// Attempts to parse the header at the given path, returning a map of definitions to their values.
102118
/// Each entry in the map directly corresponds to a `#define` in the given header.
103119
fn parse_header_defines(header_path: impl AsRef<Path>) -> Result<HashMap<String, String>> {
104-
// This regex picks apart a C style, single line `#define` statement into an identifier and a
105-
// value. e.g. for the line `#define Py_DEBUG 1`, this regex will capture `Py_DEBUG` into
106-
// `ident` and `1` into `value`.
107-
let define_regex = Regex::new(r"^\s*#define\s+(?P<ident>[a-zA-Z0-9_]+)\s+(?P<value>.+)\s*$")?;
108-
109-
let header_file = File::open(header_path.as_ref())?;
110-
let header_reader = BufReader::new(&header_file);
111-
120+
let header_reader = BufReader::new(File::open(header_path.as_ref())?);
112121
let mut definitions = HashMap::new();
113-
let tostr = |r: regex::Match<'_>| r.as_str().to_string();
114122
for maybe_line in header_reader.lines() {
115-
if let Some(captures) = define_regex.captures(&maybe_line?) {
116-
match (captures.name("ident"), captures.name("value")) {
117-
(Some(key), Some(val)) => definitions.insert(tostr(key), tostr(val)),
118-
_ => None,
119-
};
123+
let line = maybe_line?;
124+
let mut i = line.trim().split_whitespace();
125+
if i.next() == Some("#define") {
126+
if let (Some(key), Some(value), None) = (i.next(), i.next(), i.next()) {
127+
definitions.insert(key.into(), value.into());
128+
}
120129
}
121130
}
122131
Ok(definitions)
@@ -179,7 +188,7 @@ fn load_cross_compile_info() -> Result<(InterpreterConfig, HashMap<String, Strin
179188
shared,
180189
ld_version: "".to_string(),
181190
base_prefix: "".to_string(),
182-
executable: "".to_string(),
191+
executable: PathBuf::new(),
183192
calcsize_pointer: None,
184193
};
185194

@@ -190,10 +199,7 @@ fn load_cross_compile_info() -> Result<(InterpreterConfig, HashMap<String, Strin
190199
/// the interpreter and printing variables of interest from
191200
/// sysconfig.get_config_vars.
192201
#[cfg(not(target_os = "windows"))]
193-
fn get_config_vars(python_path: &str) -> Result<HashMap<String, String>> {
194-
// FIXME: We can do much better here using serde:
195-
// import json, sysconfig; print(json.dumps({k:str(v) for k, v in sysconfig.get_config_vars().items()}))
196-
202+
fn get_config_vars(python_path: &Path) -> Result<HashMap<String, String>> {
197203
let mut script = "import sysconfig; \
198204
config = sysconfig.get_config_vars();"
199205
.to_owned();
@@ -228,7 +234,7 @@ fn get_config_vars(python_path: &str) -> Result<HashMap<String, String>> {
228234
}
229235

230236
#[cfg(target_os = "windows")]
231-
fn get_config_vars(_: &str) -> Result<HashMap<String, String>> {
237+
fn get_config_vars(_: &Path) -> Result<HashMap<String, String>> {
232238
// sysconfig is missing all the flags on windows, so we can't actually
233239
// query the interpreter directly for its build flags.
234240
//
@@ -274,7 +280,7 @@ fn cfg_line_for_var(key: &str, val: &str) -> Option<String> {
274280
}
275281

276282
/// Run a python script using the specified interpreter binary.
277-
fn run_python_script(interpreter: &str, script: &str) -> Result<String> {
283+
fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
278284
let out = Command::new(interpreter)
279285
.args(&["-c", script])
280286
.stderr(Stdio::inherit())
@@ -286,12 +292,12 @@ fn run_python_script(interpreter: &str, script: &str) -> Result<String> {
286292
bail!(
287293
"Could not find any interpreter at {}, \
288294
are you sure you have Python installed on your PATH?",
289-
interpreter
295+
interpreter.display()
290296
);
291297
} else {
292298
bail!(
293299
"Failed to run the Python interpreter at {}: {}",
294-
interpreter,
300+
interpreter.display(),
295301
err
296302
);
297303
}
@@ -395,33 +401,24 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {
395401
///
396402
/// If none of the above works, an error is returned
397403
fn find_interpreter_and_get_config() -> Result<(InterpreterConfig, HashMap<String, String>)> {
398-
if let Some(sys_executable) = env::var_os("PYTHON_SYS_EXECUTABLE") {
399-
let interpreter_path = sys_executable
400-
.to_str()
401-
.ok_or("Unable to get PYTHON_SYS_EXECUTABLE value")?;
402-
let interpreter_config = get_config_from_interpreter(interpreter_path)?;
403-
404-
return Ok((interpreter_config, get_config_vars(interpreter_path)?));
404+
let python_interpreter = if let Some(exe) = env::var_os("PYTHON_SYS_EXECUTABLE") {
405+
exe.into()
406+
} else {
407+
PathBuf::from(
408+
["python", "python3"]
409+
.iter()
410+
.find(|bin| {
411+
if let Ok(out) = Command::new(bin).arg("--version").output() {
412+
// begin with `Python 3.X.X :: additional info`
413+
out.stdout.starts_with(b"Python 3") || out.stderr.starts_with(b"Python 3")
414+
} else {
415+
false
416+
}
417+
})
418+
.ok_or("Python 3.x interpreter not found")?,
419+
)
405420
};
406421

407-
let python_interpreter = ["python", "python3"]
408-
.iter()
409-
.find(|bin| {
410-
if let Ok(out) = Command::new(bin).arg("--version").output() {
411-
// begin with `Python 3.X.X :: additional info`
412-
out.stdout.starts_with(b"Python 3") || out.stderr.starts_with(b"Python 3")
413-
} else {
414-
false
415-
}
416-
})
417-
.ok_or("Python 3.x interpreter not found")?;
418-
419-
// check default python
420-
let interpreter_config = get_config_from_interpreter(&python_interpreter)?;
421-
if interpreter_config.version.major == 3 {
422-
return Ok((interpreter_config, get_config_vars(&python_interpreter)?));
423-
}
424-
425422
let interpreter_config = get_config_from_interpreter(&python_interpreter)?;
426423
if interpreter_config.version.major == 3 {
427424
return Ok((interpreter_config, get_config_vars(&python_interpreter)?));
@@ -431,7 +428,7 @@ fn find_interpreter_and_get_config() -> Result<(InterpreterConfig, HashMap<Strin
431428
}
432429

433430
/// Extract compilation vars from the specified interpreter.
434-
fn get_config_from_interpreter(interpreter: &str) -> Result<InterpreterConfig> {
431+
fn get_config_from_interpreter(interpreter: &Path) -> Result<InterpreterConfig> {
435432
let script = r#"
436433
import json
437434
import platform
@@ -446,23 +443,40 @@ try:
446443
except AttributeError:
447444
base_prefix = sys.exec_prefix
448445
449-
print(json.dumps({
450-
"version": {
451-
"major": sys.version_info[0],
452-
"minor": sys.version_info[1],
453-
"implementation": platform.python_implementation()
454-
},
455-
"libdir": sysconfig.get_config_var('LIBDIR'),
456-
"ld_version": sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short'),
457-
"base_prefix": base_prefix,
458-
"shared": PYPY or bool(sysconfig.get_config_var('Py_ENABLE_SHARED')),
459-
"executable": sys.executable,
460-
"calcsize_pointer": struct.calcsize("P"),
461-
}))
446+
libdir = sysconfig.get_config_var('LIBDIR')
447+
448+
print("version_major", sys.version_info[0])
449+
print("version_minor", sys.version_info[1])
450+
print("implementation", platform.python_implementation())
451+
if libdir is not None:
452+
print("libdir", libdir)
453+
print("ld_version", sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short'))
454+
print("base_prefix", base_prefix)
455+
print("shared", PYPY or bool(sysconfig.get_config_var('Py_ENABLE_SHARED')))
456+
print("executable", sys.executable)
457+
print("calcsize_pointer", struct.calcsize("P"))
462458
"#;
463-
let json = run_python_script(interpreter, script)?;
464-
Ok(serde_json::from_str(&json)
465-
.map_err(|e| format!("Failed to get InterPreterConfig: {}", e))?)
459+
let output = run_python_script(interpreter, script)?;
460+
let map: HashMap<String, String> = output
461+
.lines()
462+
.filter_map(|line| {
463+
let mut i = line.splitn(2, ' ');
464+
Some((i.next()?.into(), i.next()?.into()))
465+
})
466+
.collect();
467+
Ok(InterpreterConfig {
468+
version: PythonVersion {
469+
major: map["version_major"].parse()?,
470+
minor: Some(map["version_minor"].parse()?),
471+
implementation: map["implementation"].parse()?,
472+
},
473+
libdir: map.get("libdir").cloned(),
474+
shared: map["shared"] == "True",
475+
ld_version: map["ld_version"].clone(),
476+
base_prefix: map["base_prefix"].clone(),
477+
executable: map["executable"].clone().into(),
478+
calcsize_pointer: Some(map["calcsize_pointer"].parse()?),
479+
})
466480
}
467481

468482
fn configure(interpreter_config: &InterpreterConfig) -> Result<String> {

0 commit comments

Comments
 (0)