Compare commits

..

No commits in common. "3f302c71801d6d8bca5baa57becb990aa2729b49" and "5ec24bbfaf39edd60865b2879107835b99e133d0" have entirely different histories.

10 changed files with 105 additions and 150 deletions

2
.gitignore vendored
View file

@ -1,4 +1,4 @@
/target /target
/targets /targets
/results /results
/db.sqlite* /db.sqlite

View file

@ -31,10 +31,6 @@ fn try_get_commit() -> color_eyre::Result<String> {
} }
fn has_no_changes() -> color_eyre::Result<bool> { fn has_no_changes() -> color_eyre::Result<bool> {
if std::env::var("DOES_IT_BUILD_OVERRIDE_VERSION").is_ok() {
return Ok(true);
}
Ok(std::process::Command::new("git") Ok(std::process::Command::new("git")
.args(["diff", "--no-ext-diff", "--quiet", "--exit-code"]) .args(["diff", "--no-ext-diff", "--quiet", "--exit-code"])
.output()? .output()?

View file

@ -1,8 +0,0 @@
-- Add migration script here
ALTER TABLE build_info
ADD COLUMN rustflags VARCHAR;
DELETE FROM build_info WHERE target IN ('avr-none', 'amdgcn-amd-amdhsa') AND mode = 'core';
DELETE FROM finished_nightly WHERE nightly > '2025-02-11';

View file

@ -15,25 +15,9 @@ use tracing::{debug, error, info};
use crate::{ use crate::{
db::{BuildMode, Db, FullBuildInfo, Status}, db::{BuildMode, Db, FullBuildInfo, Status},
nightlies::Nightlies, nightlies::{Nightlies, NightlyCache},
}; };
struct CustomBuildFlags {
target: &'static str,
flags: &'static [&'static str],
}
const CUSTOM_CORE_FLAGS: &[CustomBuildFlags] = &[
CustomBuildFlags {
target: "avr-none",
flags: &["-Ctarget-cpu=atmega328p"],
},
CustomBuildFlags {
target: "amdgcn-amd-amdhsa",
flags: &["-Ctarget-cpu=gfx1100"],
},
];
pub struct Toolchain(String); pub struct Toolchain(String);
impl Toolchain { impl Toolchain {
pub fn from_nightly(nightly: &str) -> Self { pub fn from_nightly(nightly: &str) -> Self {
@ -52,15 +36,18 @@ impl Display for Toolchain {
} }
pub async fn background_builder(db: Db) -> Result<()> { pub async fn background_builder(db: Db) -> Result<()> {
let mut nightly_cache = NightlyCache::default();
loop { loop {
if let Err(err) = background_builder_inner(&db).await { if let Err(err) = background_builder_inner(&db, &mut nightly_cache).await {
error!("error in background builder: {err}"); error!("error in background builder: {err}");
} }
} }
} }
async fn background_builder_inner(db: &Db) -> Result<()> { async fn background_builder_inner(db: &Db, nightly_cache: &mut NightlyCache) -> Result<()> {
let nightlies = Nightlies::fetch().await.wrap_err("fetching nightlies")?; let nightlies = Nightlies::fetch(nightly_cache)
.await
.wrap_err("fetching nightlies")?;
let already_finished = db let already_finished = db
.finished_nightlies() .finished_nightlies()
.await .await
@ -257,7 +244,6 @@ async fn build_single_target(db: &Db, nightly: &str, target: &str, mode: BuildMo
status: result.status, status: result.status,
stderr: result.stderr, stderr: result.stderr,
mode, mode,
rustflags: result.rustflags,
}) })
.await?; .await?;
@ -267,7 +253,6 @@ async fn build_single_target(db: &Db, nightly: &str, target: &str, mode: BuildMo
struct BuildResult { struct BuildResult {
status: Status, status: Status,
stderr: String, stderr: String,
rustflags: Option<String>,
} }
/// Build a target core in a temporary directory and see whether it passes or not. /// Build a target core in a temporary directory and see whether it passes or not.
@ -277,8 +262,6 @@ async fn build_target(
target: &str, target: &str,
mode: BuildMode, mode: BuildMode,
) -> Result<BuildResult> { ) -> Result<BuildResult> {
let mut rustflags = None;
let output = match mode { let output = match mode {
BuildMode::Core => { BuildMode::Core => {
let init = Command::new("cargo") let init = Command::new("cargo")
@ -295,22 +278,11 @@ async fn build_target(
std::fs::write(&librs, "#![no_std]\n") std::fs::write(&librs, "#![no_std]\n")
.wrap_err_with(|| format!("writing to {}", librs.display()))?; .wrap_err_with(|| format!("writing to {}", librs.display()))?;
let mut cmd = Command::new("cargo"); Command::new("cargo")
cmd.arg(format!("+{toolchain}")) .arg(format!("+{toolchain}"))
.args(["build", "-Zbuild-std=core", "--release"]) .args(["build", "-Zbuild-std=core", "--release"])
.args(["--target", target]); .args(["--target", target])
.current_dir(tmpdir)
let extra_flags = CUSTOM_CORE_FLAGS
.iter()
.find(|flags| flags.target == target);
if let Some(extra_flags) = extra_flags {
let flags = extra_flags.flags.join(" ");
cmd.env("RUSTFLAGS", &flags);
rustflags = Some(flags);
}
cmd.current_dir(tmpdir)
.output() .output()
.await .await
.wrap_err("spawning cargo build")? .wrap_err("spawning cargo build")?
@ -336,9 +308,5 @@ async fn build_target(
info!("Finished build"); info!("Finished build");
Ok(BuildResult { Ok(BuildResult { status, stderr })
status,
stderr,
rustflags,
})
} }

View file

@ -48,7 +48,6 @@ pub struct FullBuildInfo {
pub status: Status, pub status: Status,
pub stderr: String, pub stderr: String,
pub mode: BuildMode, pub mode: BuildMode,
pub rustflags: Option<String>,
} }
#[derive(Debug, PartialEq, Clone, Copy, sqlx::Type, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Copy, sqlx::Type, Serialize, Deserialize)]
@ -96,14 +95,13 @@ impl Db {
pub async fn insert(&self, info: FullBuildInfo) -> Result<()> { pub async fn insert(&self, info: FullBuildInfo) -> Result<()> {
sqlx::query( sqlx::query(
"INSERT INTO build_info (nightly, target, status, stderr, mode, rustflags) VALUES (?, ?, ?, ?, ?, ?);", "INSERT INTO build_info (nightly, target, status, stderr, mode) VALUES (?, ?, ?, ?, ?);",
) )
.bind(info.nightly) .bind(info.nightly)
.bind(info.target) .bind(info.target)
.bind(info.status) .bind(info.status)
.bind(info.stderr) .bind(info.stderr)
.bind(info.mode) .bind(info.mode)
.bind(info.rustflags)
.execute(&self.conn) .execute(&self.conn)
.await .await
.wrap_err("inserting build info into database")?; .wrap_err("inserting build info into database")?;
@ -175,7 +173,7 @@ impl Db {
mode: BuildMode, mode: BuildMode,
) -> Result<Option<FullBuildInfo>> { ) -> Result<Option<FullBuildInfo>> {
let result = sqlx::query_as::<_, FullBuildInfo>( let result = sqlx::query_as::<_, FullBuildInfo>(
"SELECT nightly, target, status, stderr, mode, rustflags FROM build_info "SELECT nightly, target, status, stderr, mode FROM build_info
WHERE nightly = ? AND target = ? AND mode = ?", WHERE nightly = ? AND target = ? AND mode = ?",
) )
.bind(nightly) .bind(nightly)

View file

@ -1,13 +1,21 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::hash::RandomState; use std::hash::RandomState;
use color_eyre::eyre::Context; use color_eyre::eyre::{Context, OptionExt};
use color_eyre::Result; use color_eyre::Result;
use reqwest::StatusCode;
use time::Duration;
use tracing::debug; use tracing::debug;
use crate::db::{BuildMode, FinishedNightly}; use crate::db::{BuildMode, FinishedNightly};
const EARLIEST_CUTOFF_DATE: &str = "2023-01-01"; const EARLIEST_CUTOFF_DATE: &str = "2022-01-01";
#[derive(Default)]
pub struct NightlyCache {
/// Nightlies that exist.
exists: HashSet<String>,
}
/// All nightlies that exist. /// All nightlies that exist.
pub struct Nightlies { pub struct Nightlies {
@ -15,7 +23,7 @@ pub struct Nightlies {
} }
impl Nightlies { impl Nightlies {
pub async fn fetch() -> Result<Nightlies> { pub async fn fetch(cache: &mut NightlyCache) -> Result<Nightlies> {
let manifests = reqwest::get("https://static.rust-lang.org/manifests.txt") let manifests = reqwest::get("https://static.rust-lang.org/manifests.txt")
.await .await
.wrap_err("fetching https://static.rust-lang.org/manifests.txt")? .wrap_err("fetching https://static.rust-lang.org/manifests.txt")?
@ -27,7 +35,24 @@ impl Nightlies {
.filter(|date| date.as_str() > EARLIEST_CUTOFF_DATE) .filter(|date| date.as_str() > EARLIEST_CUTOFF_DATE)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
all.sort_by(|a, b| b.cmp(a)); // Reverse sort. all.sort();
// The manifests is only updated weekly, which means new nightlies won't be contained.
// We probe for their existence.
let latest = all
.last()
.ok_or_eyre("did not find any nightlies in manifests.txt")?;
for nightly in guess_more_recent_nightlies(latest)? {
if nightly_exists(&nightly, cache)
.await
.wrap_err_with(|| format!("checking whether {nightly} exists"))?
{
all.push(nightly);
}
}
all.reverse();
debug!( debug!(
"Loaded {} nightlies from the manifest and manual additions", "Loaded {} nightlies from the manifest and manual additions",
@ -67,6 +92,31 @@ fn nightlies_from_manifest(manifest: &str) -> Vec<String> {
.collect() .collect()
} }
fn guess_more_recent_nightlies(latest: &str) -> Result<Vec<String>> {
let format = time::macros::format_description!("[year]-[month]-[day]");
let latest = time::Date::parse(latest, format).wrap_err("latest nightly has invalid format")?;
// manifests.txt is updated weekly, so let's try 8 just in case.
Ok((1..=8)
.filter_map(|offset| latest.checked_add(Duration::days(offset)))
.map(|date| date.format(format).unwrap())
.collect())
}
async fn nightly_exists(nightly: &str, cache: &mut NightlyCache) -> Result<bool> {
if cache.exists.contains(nightly) {
return Ok(true);
}
let url = format!("https://static.rust-lang.org/dist/{nightly}/channel-rust-nightly.toml");
let resp = reqwest::get(&url).await.wrap_err("fetching channel")?;
debug!(%nightly, %url, status = %resp.status(), "Checked whether a recent nightly exists");
let exists = resp.status() == StatusCode::OK;
if exists {
cache.exists.insert(nightly.to_owned());
}
Ok(exists)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]
@ -79,4 +129,22 @@ static.rust-lang.org/dist/2024-08-23/channel-rust-nightly.toml";
let nightlies = super::nightlies_from_manifest(&test_manifest); let nightlies = super::nightlies_from_manifest(&test_manifest);
assert_eq!(nightlies, vec!["2024-08-22", "2024-08-23"]); assert_eq!(nightlies, vec!["2024-08-22", "2024-08-23"]);
} }
#[test]
fn guess() {
let nightlies = super::guess_more_recent_nightlies("2024-08-28").unwrap();
assert_eq!(
nightlies,
[
"2024-08-29",
"2024-08-30",
"2024-08-31",
"2024-09-01",
"2024-09-02",
"2024-09-03",
"2024-09-04",
"2024-09-05",
]
);
}
} }

View file

@ -41,19 +41,6 @@ struct BuildQuery {
} }
async fn web_build(State(state): State<AppState>, Query(query): Query<BuildQuery>) -> Response { async fn web_build(State(state): State<AppState>, Query(query): Query<BuildQuery>) -> Response {
use askama::Template;
#[derive(askama::Template)]
#[template(path = "build.html")]
struct BuildPage {
nightly: String,
target: String,
stderr: String,
mode: BuildMode,
rustflags: Option<String>,
version: &'static str,
status: Status,
}
match state match state
.db .db
.build_status_full( .build_status_full(
@ -64,16 +51,15 @@ async fn web_build(State(state): State<AppState>, Query(query): Query<BuildQuery
.await .await
{ {
Ok(Some(build)) => { Ok(Some(build)) => {
let page = BuildPage { let page = include_str!("../static/build.html")
nightly: query.nightly, .replace("{{nightly}}", &query.nightly)
target: query.target, .replace("{{target}}", &query.target)
stderr: build.stderr, .replace("{{stderr}}", &build.stderr)
mode: build.mode, .replace("{{mode}}", &build.mode.to_string())
rustflags: build.rustflags, .replace("{{version}}", crate::VERSION)
version: crate::VERSION, .replace("{{status}}", &build.status.to_string());
status: build.status,
}; Html(page).into_response()
Html(page.render().unwrap()).into_response()
} }
Ok(None) => StatusCode::NOT_FOUND.into_response(), Ok(None) => StatusCode::NOT_FOUND.into_response(),
Err(err) => { Err(err) => {
@ -86,7 +72,6 @@ async fn web_build(State(state): State<AppState>, Query(query): Query<BuildQuery
#[derive(Deserialize)] #[derive(Deserialize)]
struct TargetQuery { struct TargetQuery {
target: String, target: String,
failures: Option<bool>,
} }
async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQuery>) -> Response { async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQuery>) -> Response {
@ -98,11 +83,8 @@ async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQue
status: String, status: String,
version: &'static str, version: &'static str,
builds: Vec<(String, Option<BuildInfo>, Option<BuildInfo>)>, builds: Vec<(String, Option<BuildInfo>, Option<BuildInfo>)>,
showing_failures: bool,
} }
let filter_failures = query.failures.unwrap_or(false);
match state.db.history_for_target(&query.target).await { match state.db.history_for_target(&query.target).await {
Ok(builds) => { Ok(builds) => {
let latest_core = builds let latest_core = builds
@ -140,10 +122,6 @@ async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQue
let mut builds = builds_grouped let mut builds = builds_grouped
.into_iter() .into_iter()
.map(|(k, (v1, v2))| (k, v1, v2)) .map(|(k, (v1, v2))| (k, v1, v2))
.filter(|(_, core_build, std_build)| {
filter_build(filter_failures, core_build)
|| filter_build(filter_failures, std_build)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
builds.sort_by_cached_key(|build| Reverse(build.0.clone())); builds.sort_by_cached_key(|build| Reverse(build.0.clone()));
@ -152,7 +130,6 @@ async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQue
target: query.target, target: query.target,
version: crate::VERSION, version: crate::VERSION,
builds, builds,
showing_failures: filter_failures,
}; };
Html(page.render().unwrap()).into_response() Html(page.render().unwrap()).into_response()
@ -167,7 +144,6 @@ async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQue
#[derive(Deserialize)] #[derive(Deserialize)]
struct NightlyQuery { struct NightlyQuery {
nightly: String, nightly: String,
failures: Option<bool>,
} }
async fn web_nightly(State(state): State<AppState>, Query(query): Query<NightlyQuery>) -> Response { async fn web_nightly(State(state): State<AppState>, Query(query): Query<NightlyQuery>) -> Response {
@ -182,11 +158,8 @@ async fn web_nightly(State(state): State<AppState>, Query(query): Query<NightlyQ
std_failures: usize, std_failures: usize,
core_broken: Option<String>, core_broken: Option<String>,
std_broken: Option<String>, std_broken: Option<String>,
showing_failures: bool,
} }
let filter_failures = query.failures.unwrap_or(false);
match state.db.history_for_nightly(&query.nightly).await { match state.db.history_for_nightly(&query.nightly).await {
Ok(builds) => match state.db.nightly_info(&query.nightly).await { Ok(builds) => match state.db.nightly_info(&query.nightly).await {
Ok(info) => { Ok(info) => {
@ -214,10 +187,6 @@ async fn web_nightly(State(state): State<AppState>, Query(query): Query<NightlyQ
let mut builds = builds_grouped let mut builds = builds_grouped
.into_iter() .into_iter()
.map(|(k, (v1, v2))| (k, v1, v2)) .map(|(k, (v1, v2))| (k, v1, v2))
.filter(|(_, core_build, std_build)| {
filter_build(filter_failures, core_build)
|| filter_build(filter_failures, std_build)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
builds.sort_by_cached_key(|build| build.0.clone()); builds.sort_by_cached_key(|build| build.0.clone());
@ -238,7 +207,6 @@ async fn web_nightly(State(state): State<AppState>, Query(query): Query<NightlyQ
core_failures, core_failures,
core_broken, core_broken,
std_broken, std_broken,
showing_failures: filter_failures,
}; };
Html(page.render().unwrap()).into_response() Html(page.render().unwrap()).into_response()
@ -320,10 +288,3 @@ impl BuildInfo {
) )
} }
} }
fn filter_build(filter_failures: bool, build: &Option<BuildInfo>) -> bool {
!filter_failures
|| build
.as_ref()
.is_some_and(|build| build.status == Status::Error)
}

View file

@ -13,27 +13,17 @@
</head> </head>
<body> <body>
<h1>Build results for nightly-{{nightly}} target {{target}} {{mode}}</h1> <h1>Build results for nightly-{{nightly}} target {{target}} {{mode}}</h1>
<a href="/">Home</a>
<div style="margin-top: 20px" class="{{status}} build-indicator-big"> <div style="margin-top: 20px" class="{{status}} build-indicator-big">
{{status}} {{status}}
</div> </div>
{% if let Some(rustflags) = rustflags %}
<p>
Using rustflags: <b><code>{{rustflags}}</code></b>
</p>
{% endif %}
<pre> <pre>
{{stderr}} {{stderr}}
</pre> </pre>
<p> <p>
<a href="/target?target={{target}}" <a href="/target?target={{target}}">Build history for target {{target}}</a>
>Build history for target {{target}}</a
>
</p> </p>
<p> <p>
<a href="/nightly?nightly={{nightly}}" <a href="/nightly?nightly={{nightly}}">Build state for nightly {{nightly}}</a>
>Build state for nightly {{nightly}}</a
>
</p> </p>
<footer class="footer"> <footer class="footer">
<span>does-it-build {{version}}</span> <span>does-it-build {{version}}</span>

View file

@ -8,7 +8,7 @@
</head> </head>
<body> <body>
<h1>Nightly build state for {{nightly}}</h1> <h1>Nightly build state for {{nightly}}</h1>
<a href="/">Home</a> <a href="/">Back</a>
<p> <p>
This contains the status of this nightly. Core is built with This contains the status of this nightly. Core is built with
<code>cargo build --release -Zbuild-std=core</code>. This checks that <code>cargo build --release -Zbuild-std=core</code>. This checks that
@ -47,20 +47,11 @@
<dd>{{std_failures}}</dd> <dd>{{std_failures}}</dd>
</dl> </dl>
</p> </p>
{% if showing_failures %}
<p>
<a href="/nightly?nightly={{nightly}}">show all</a>
</p>
{% else %}
<p>
<a href="/nightly?nightly={{nightly}}&failures=true">filter failures</a>
</p>
{% endif %}
<table> <table>
<tr> <tr>
<th>nightly</th> <th>nightly</th>
<th>core<br>codegen</th> <th>core</th>
<th>std<br>check</th> <th>std</th>
</tr> </tr>
{% for build in builds %} {% for build in builds %}
<tr> <tr>

View file

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@ -13,7 +13,7 @@
</head> </head>
<body> <body>
<h1>Target build history for {{target}}</h1> <h1>Target build history for {{target}}</h1>
<a href="/">Home</a> <a href="/">Back</a>
<div style="margin-top: 20px" class="{{status}} build-indicator-big"> <div style="margin-top: 20px" class="{{status}} build-indicator-big">
{{status}} {{status}}
</div> </div>
@ -28,20 +28,11 @@
std builds (on targets that have it) but does not check whether std builds (on targets that have it) but does not check whether
codegen/linking works. codegen/linking works.
</p> </p>
{% if showing_failures %}
<p>
<a href="/target?target={{target}}">show all</a>
</p>
{% else %}
<p>
<a href="/target?target={{target}}&failures=true">filter failures</a>
</p>
{% endif %}
<table> <table>
<tr> <tr>
<th>nightly</th> <th>nightly</th>
<th>core<br />codegen</th> <th>core</th>
<th>std<br />check</th> <th>std</th>
</tr> </tr>
{% for build in builds %} {% for build in builds %}
<tr> <tr>