summaryrefslogtreecommitdiffstats
path: root/builder/src/main.rs
blob: 67bae7f661fb93c113f3e1db4fcc39c993e029ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::collections::HashMap;

use crate_db::builddb::*;
mod parse;

use kitchen_sink::*;
use parse::*;

use std::path::Path;
use std::process::Command;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let crates = kitchen_sink::KitchenSink::new_default()?;

    let db = BuildDb::new(crates.main_cache_dir().join("builds.db"))?;
    let docker_root = crates.main_cache_dir().join("docker");
    prepare_docker(&docker_root)?;

    let filter = std::env::args().skip(1).next();

    for (_, all) in crates.all_crates_io_crates() {
        if stopped() {
            break;
        }
        if let Some(f) = &filter {
            if !all.name().contains(f) {
                continue;
            }
        }
        if let Err(e) = analyze_crate(&all, &db, &crates, &docker_root) {
            eprintln!("•• {}: {}", all.name(), e);
            continue;
        }
    }
    Ok(())
}

fn analyze_crate(all: &CratesIndexCrate, db: &BuildDb, crates: &KitchenSink, docker_root: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let ref origin = Origin::from_crates_io_name(all.name());

    let compat_info = db.get_compat(origin)?;
    // look for missing 1.24 tests (ignoring the "probably" ones that aren't authoritative)
    if compat_info.iter().any(|c| c.compat != Compat::ProbablyWorks && c.crate_version.minor == 24) {
        println!("{} got it {:?}", all.name(), compat_info);
        return Ok(());
    }

    println!("checking {}", all.name());
    let ver = all.latest_version();

    let (stdout, stderr) = do_builds(&crates, &all, &docker_root)?;
    println!("{}\n{}\n", stdout, stderr);
    db.set_raw_build_info(origin, ver.version(), &stdout, &stderr)?;

    for f in parse_analyses(&stdout, &stderr) {
        println!("{:#?}", f);
        if let Some(rustc_version) = f.rustc_version {
            for (rustc_override, name, version, compat) in f.crates {
                let rustc_version = rustc_override.unwrap_or(&rustc_version);
                let certain = rustc_override.is_some(); // that's for 2018 edition
                db.set_compat(&Origin::from_crates_io_name(&name), &version, rustc_version, compat, certain)?;
            }
        }
    }
    Ok(())
}

fn prepare_docker(docker_root: &Path) -> Result<(), Box<dyn std::error::Error>> {

    let res = Command::new("docker")
        .current_dir(docker_root)
        .arg("build")
        .arg("-t").arg("testing1")
        .arg(".")
        .status()?;

    if !res.success() {
        Err("failed build")?;
    }
    Ok(())
}

fn do_builds(_crates: &KitchenSink, all: &CratesIndexCrate, docker_root: &Path) -> Result<(String, String), Box<dyn std::error::Error>> {
    let mut versions = HashMap::new();
    for ver in all.versions().iter().filter(|v| !v.is_yanked()).filter_map(|v| SemVer::parse(v.version()).ok()) {
        let unstable = ver.major == 0;
        let major = if unstable {ver.minor} else {ver.major};
        versions.insert((unstable, major), ver); // later wins
    }

    let mut cmd = Command::new("docker");
    cmd
        .current_dir(docker_root)
        .arg("run")
        .arg("--rm")
        .arg("-m1500m")
        .arg("testing1")
        .arg("/tmp/run-crate-tests.sh");
    for ver in versions.values().take(15) {
        cmd.arg(format!("{}=\"{}\"\n", all.name(), ver));
    }
    let out = cmd
        .output()?;

    let stdout = String::from_utf8_lossy(&out.stdout).into_owned();
    let mut stderr = String::from_utf8_lossy(&out.stderr).into_owned();

    if !out.status.success() {
        stderr += "\nexit failure\n";
    }

    Ok((stdout, stderr))
}