Skip to content

Commit 6cb69a6

Browse files
committed
rust: optionally run interpreter tests during verification
Before this commit, verify_distribution.py was just sitting around doing a lot of nothing. This commit moves it to the Rust code and teaches the Rust verification command to run it. We still don't run the additional tests in CI.
1 parent 07c3cc5 commit 6cb69a6

File tree

4 files changed

+192
-11
lines changed

4 files changed

+192
-11
lines changed

Cargo.lock

Lines changed: 120 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ edition = "2018"
77
[dependencies]
88
anyhow = "1"
99
clap = "2"
10+
duct = "0.13"
1011
goblin = "0"
1112
once_cell = "1"
1213
scroll = "0"
1314
serde_json = "1"
1415
serde = { version = "1", features = ["derive"] }
1516
tar = "0"
17+
tempfile = "3"
1618
version-compare = "0"
1719
zstd = "0"
1820

src/main.rs

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ const GLOBALLY_BANNED_EXTENSIONS: &[&str] = &[
378378
"nis",
379379
];
380380

381+
const PYTHON_VERIFICATIONS: &str = include_str!("verify_distribution.py");
382+
381383
fn allowed_dylibs_for_triple(triple: &str) -> Vec<MachOAllowedDylib> {
382384
match triple {
383385
"aarch64-apple-darwin" => DARWIN_ALLOWED_DYLIBS.clone(),
@@ -669,6 +671,16 @@ fn validate_json(json: &PythonJsonMain, triple: &str, is_debug: bool) -> Result<
669671
Ok(errors)
670672
}
671673

674+
fn open_distribution_archive(path: &Path) -> Result<tar::Archive<impl Read>> {
675+
let fh =
676+
std::fs::File::open(path).with_context(|| format!("unable to open {}", path.display()))?;
677+
678+
let reader = std::io::BufReader::new(fh);
679+
let dctx = zstd::stream::Decoder::new(reader)?;
680+
681+
Ok(tar::Archive::new(dctx))
682+
}
683+
672684
fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
673685
let mut errors = vec![];
674686
let mut seen_dylibs = BTreeSet::new();
@@ -680,9 +692,6 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
680692
.expect("unable to obtain filename")
681693
.to_string_lossy();
682694

683-
let fh = std::fs::File::open(&dist_path)
684-
.with_context(|| format!("unable to open {}", dist_path.display()))?;
685-
686695
let triple = RECOGNIZED_TRIPLES
687696
.iter()
688697
.find(|triple| {
@@ -709,9 +718,7 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
709718

710719
let is_debug = dist_filename.contains("-debug-");
711720

712-
let reader = std::io::BufReader::new(fh);
713-
let dctx = zstd::stream::Decoder::new(reader)?;
714-
let mut tf = tar::Archive::new(dctx);
721+
let mut tf = open_distribution_archive(&dist_path)?;
715722

716723
// First entry in archive should be python/PYTHON.json.
717724
let mut entries = tf.entries()?;
@@ -832,19 +839,56 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
832839
Ok(errors)
833840
}
834841

842+
fn verify_distribution_behavior(dist_path: &Path) -> Result<Vec<String>> {
843+
let mut errors = vec![];
844+
845+
let temp_dir = tempfile::TempDir::new()?;
846+
847+
let mut tf = open_distribution_archive(dist_path)?;
848+
849+
tf.unpack(temp_dir.path())?;
850+
851+
let python_json_path = temp_dir.path().join("python").join("PYTHON.json");
852+
let python_json_data = std::fs::read(&python_json_path)?;
853+
let python_json = parse_python_json(&python_json_data)?;
854+
855+
let python_exe = temp_dir.path().join("python").join(python_json.python_exe);
856+
857+
let test_file = temp_dir.path().join("verify.py");
858+
std::fs::write(&test_file, PYTHON_VERIFICATIONS.as_bytes())?;
859+
860+
eprintln!(" running interpreter tests (output may follow)");
861+
let output = duct::cmd(&python_exe, &[test_file.display().to_string()])
862+
.stdout_to_stderr()
863+
.unchecked()
864+
.run()?;
865+
866+
if !output.status.success() {
867+
errors.push("errors running interpreter tests".to_string());
868+
}
869+
870+
Ok(errors)
871+
}
872+
835873
fn command_validate_distribution(args: &ArgMatches) -> Result<()> {
874+
let run = args.is_present("run");
875+
836876
let mut success = true;
837877

838878
for path in args.values_of("path").unwrap() {
839879
let path = PathBuf::from(path);
840880
println!("validating {}", path.display());
841-
let errors = validate_distribution(&path)?;
881+
let mut errors = validate_distribution(&path)?;
882+
883+
if run {
884+
errors.extend(verify_distribution_behavior(&path)?.into_iter());
885+
}
842886

843887
if errors.is_empty() {
844-
println!("{} OK", path.display());
888+
println!(" {} OK", path.display());
845889
} else {
846890
for error in errors {
847-
println!("error: {}", error);
891+
println!(" error: {}", error);
848892
}
849893

850894
success = false;
@@ -867,6 +911,11 @@ fn main_impl() -> Result<()> {
867911
.subcommand(
868912
SubCommand::with_name("validate-distribution")
869913
.about("Ensure a distribution archive conforms to standards")
914+
.arg(
915+
Arg::with_name("run")
916+
.long("--run")
917+
.help("Run the interpreter to verify behavior"),
918+
)
870919
.arg(
871920
Arg::with_name("path")
872921
.help("Path to tar.zst file to validate")

pythonbuild/verify_distribution.py renamed to src/verify_distribution.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
# License, v. 2.0. If a copy of the MPL was not distributed with this
33
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
TERMINFO_DIRS = [
6+
"/etc/terminfo",
7+
"/lib/terminfo",
8+
"/usr/share/terminfo",
9+
]
10+
511

612
def verify():
713
"""Verifies that a built Python distribution behaves as expected."""
@@ -17,6 +23,12 @@ def verify():
1723
# Need to set TCL_LIBRARY so local tcl/tk files get picked up.
1824
os.environ["TCL_LIBRARY"] = os.path.join(install_root, "lib", "tcl", "tcl")
1925

26+
# Need to set TERMINFO_DIRS so terminfo database can be located.
27+
if "TERMINFO_DIRS" not in os.environ:
28+
terminfo_dirs = [p for p in TERMINFO_DIRS if os.path.exists(p)]
29+
if terminfo_dirs:
30+
os.environ["TERMINFO_DIRS"] = ":".join(terminfo_dirs)
31+
2032
verify_compression()
2133
verify_ctypes()
2234
verify_curses()
@@ -25,8 +37,6 @@ def verify():
2537
verify_ssl()
2638
verify_tkinter()
2739

28-
print("distribution verified!")
29-
3040

3141
def verify_compression():
3242
import bz2, lzma, zlib

0 commit comments

Comments
 (0)