build: support emitting arbitrary lines from pyo3 build script

PyOxidizer needs to do some... questionable things with regards to
configuring how the Python interpreter is linked. The way I solved this
problem for the `cpython` / `python3-sys` crates was by adding a bunch
of crate features to control what `cargo:` lines were emitted by the
build scripts. This added a lot of complexity to the those crates for
a target audience of ~1.

Now that PyO3 has support for config files to control settings, this
provides a richer mechanism than crate features to influence the build
script.

This commit defines a new field on the `InterpreterConfig` struct to
hold an arbitrary list of strings/lines that should be emitted by
the build script. This field is only every populated when parsing config
files and it is only read by pyo3's build script to `println!()`
additional values.

My intended use case for this is to have PyOxidizer effectively control
the interpreter link settings via the config file (at my own peril)
while having minimal impact on the maintainability of PyO3's code base.
Given the complexity of the link hacks employed, you probably don't want
this polluting pyo3's code base.
This commit is contained in:
Gregory Szorc 2021-08-14 10:48:56 -07:00
parent b7c62b8a53
commit 04c77e35c5
3 changed files with 37 additions and 1 deletions

View File

@ -152,6 +152,11 @@ fn configure_pyo3() -> Result<()> {
println!("cargo:rustc-cfg=addr_of");
}
// Extra lines come last, to support last write wins.
for line in &interpreter_config.extra_build_script_lines {
println!("{}", line);
}
Ok(())
}

View File

@ -67,6 +67,7 @@ pub fn abi3_config() -> Option<InterpreterConfig> {
pointer_width: None,
executable: None,
shared: true,
extra_build_script_lines: vec![],
});
}
}

View File

@ -100,6 +100,18 @@ pub struct InterpreterConfig {
///
/// Serialized to `build_flags`.
pub build_flags: BuildFlags,
/// Additional lines to `println!()` from Cargo build scripts.
///
/// This field can be populated to enable the `pyo3` crate to emit additional lines from its
/// its Cargo build script.
///
/// This crate doesn't populate this field itself. Rather, it is intended to be used with
/// externally provided config files to give them significant control over how the crate
/// is build/configured.
///
/// Serialized to multiple `extra_build_script_line` values.
pub extra_build_script_lines: Vec<String>,
}
impl InterpreterConfig {
@ -232,6 +244,7 @@ print("mingw", get_platform() == "mingw")
executable: map.get("executable").cloned(),
pointer_width: Some(calcsize_pointer * 8),
build_flags: BuildFlags::from_interpreter(interpreter)?.fixup(version, implementation),
extra_build_script_lines: vec![],
})
}
@ -271,6 +284,7 @@ print("mingw", get_platform() == "mingw")
let mut executable = None;
let mut pointer_width = None;
let mut build_flags = None;
let mut extra_build_script_lines = vec![];
for (i, line) in lines.enumerate() {
let line = line.context("failed to read line from config")?;
@ -293,6 +307,9 @@ print("mingw", get_platform() == "mingw")
"executable" => parse_value!(executable, value),
"pointer_width" => parse_value!(pointer_width, value),
"build_flags" => parse_value!(build_flags, value),
"extra_build_script_line" => {
extra_build_script_lines.push(value.to_string());
}
unknown => bail!("unknown config key `{}`", unknown),
}
}
@ -318,6 +335,7 @@ print("mingw", get_platform() == "mingw")
}
.fixup(version, implementation)
}),
extra_build_script_lines,
})
}
@ -356,6 +374,10 @@ print("mingw", get_platform() == "mingw")
write_option_line!(executable)?;
write_option_line!(pointer_width)?;
write_line!(build_flags)?;
for line in &self.extra_build_script_lines {
writeln!(writer, "extra_build_script_line={}", line)
.context("failed to write extra_build_script_line")?;
}
Ok(())
}
}
@ -928,6 +950,7 @@ fn load_cross_compile_from_sysconfigdata(
executable: None,
pointer_width,
build_flags: BuildFlags::from_config_map(&sysconfig_data).fixup(version, implementation),
extra_build_script_lines: vec![],
})
}
@ -947,6 +970,7 @@ fn windows_hardcoded_cross_compile(
executable: None,
pointer_width: None,
build_flags: BuildFlags::windows_hardcoded(),
extra_build_script_lines: vec![],
})
}
@ -1150,6 +1174,7 @@ mod tests {
lib_dir: Some("lib_dir".into()),
shared: true,
version: MINIMUM_SUPPORTED_VERSION,
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
};
let mut buf: Vec<u8> = Vec::new();
config.to_writer(&mut buf).unwrap();
@ -1179,6 +1204,7 @@ mod tests {
major: 3,
minor: 10,
},
extra_build_script_lines: vec![],
};
let mut buf: Vec<u8> = Vec::new();
config.to_writer(&mut buf).unwrap();
@ -1204,6 +1230,7 @@ mod tests {
executable: None,
pointer_width: None,
build_flags: BuildFlags::default(),
extra_build_script_lines: vec![],
}
)
}
@ -1313,7 +1340,8 @@ mod tests {
lib_dir: Some("C:\\some\\path".into()),
executable: None,
pointer_width: None,
build_flags: BuildFlags::windows_hardcoded()
build_flags: BuildFlags::windows_hardcoded(),
extra_build_script_lines: vec![],
}
);
}
@ -1379,6 +1407,7 @@ mod tests {
lib_name: None,
shared: true,
version: PythonVersion { major: 3, minor: 7 },
extra_build_script_lines: vec![],
};
fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 6 })).unwrap();
@ -1397,6 +1426,7 @@ mod tests {
lib_name: None,
shared: true,
version: PythonVersion { major: 3, minor: 6 },
extra_build_script_lines: vec![],
};
assert!(