mirror of
https://github.com/Noratrieb/does-it-build.git
synced 2026-01-17 03:35:01 +01:00
Compare commits
No commits in common. "5ec24bbfaf39edd60865b2879107835b99e133d0" and "7f4c69e51f38328f7408e06fb2046b991d429d87" have entirely different histories.
5ec24bbfaf
...
7f4c69e51f
12 changed files with 301 additions and 309 deletions
|
|
@ -1,5 +0,0 @@
|
||||||
-- Add migration script here
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS build_info_nightly ON build_info (nightly);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS build_info_target ON build_info (target);
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
-- Add migration script here
|
|
||||||
|
|
||||||
ALTER TABLE finished_nightly
|
|
||||||
ADD COLUMN broken_error BOOLEAN DEFAULT NULL;
|
|
||||||
14
src/build.rs
14
src/build.rs
|
|
@ -57,19 +57,19 @@ async fn background_builder_inner(db: &Db, nightly_cache: &mut NightlyCache) ->
|
||||||
match next {
|
match next {
|
||||||
Some((nightly, mode)) => {
|
Some((nightly, mode)) => {
|
||||||
info!(%nightly, %mode, "Building next nightly");
|
info!(%nightly, %mode, "Building next nightly");
|
||||||
let result = build_every_target_for_toolchain(db, &nightly, mode)
|
let result = build_every_target_for_toolchain(&db, &nightly, mode)
|
||||||
.await
|
.await
|
||||||
.wrap_err_with(|| format!("building targets for toolchain {nightly}"));
|
.wrap_err_with(|| format!("building targets for toolchain {nightly}"));
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
error!(%nightly, %mode, ?err, "Failed to build nightly");
|
error!(%nightly, %mode, ?err, "Failed to build nightly");
|
||||||
db.finish_nightly_as_broken(&nightly, mode, &format!("{err:?}"))
|
db.finish_nightly_as_broken(&nightly, mode)
|
||||||
.await
|
.await
|
||||||
.wrap_err("marking nightly as broken")?;
|
.wrap_err("marking nightly as broken")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
info!("No new nightly, waiting for an hour to try again");
|
info!("No new nightly, waiting for an hour to try again");
|
||||||
tokio::time::sleep(Duration::from_secs(60 * 60)).await;
|
tokio::time::sleep(Duration::from_secs(1 * 60 * 60)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -191,7 +191,7 @@ pub async fn build_every_target_for_toolchain(
|
||||||
let results = futures::stream::iter(
|
let results = futures::stream::iter(
|
||||||
targets
|
targets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|target| build_single_target(db, nightly, target, mode)),
|
.map(|target| build_single_target(&db, nightly, target, mode)),
|
||||||
)
|
)
|
||||||
.buffer_unordered(concurrent)
|
.buffer_unordered(concurrent)
|
||||||
.collect::<Vec<Result<()>>>()
|
.collect::<Vec<Result<()>>>()
|
||||||
|
|
@ -266,7 +266,7 @@ async fn build_target(
|
||||||
BuildMode::Core => {
|
BuildMode::Core => {
|
||||||
let init = Command::new("cargo")
|
let init = Command::new("cargo")
|
||||||
.args(["init", "--lib", "--name", "target-test"])
|
.args(["init", "--lib", "--name", "target-test"])
|
||||||
.current_dir(tmpdir)
|
.current_dir(&tmpdir)
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
.wrap_err("spawning cargo init")?;
|
.wrap_err("spawning cargo init")?;
|
||||||
|
|
@ -282,7 +282,7 @@ async fn build_target(
|
||||||
.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)
|
.current_dir(&tmpdir)
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
.wrap_err("spawning cargo build")?
|
.wrap_err("spawning cargo build")?
|
||||||
|
|
@ -291,7 +291,7 @@ async fn build_target(
|
||||||
.arg(format!("+{toolchain}"))
|
.arg(format!("+{toolchain}"))
|
||||||
.args(["miri", "setup"])
|
.args(["miri", "setup"])
|
||||||
.args(["--target", target])
|
.args(["--target", target])
|
||||||
.current_dir(tmpdir)
|
.current_dir(&tmpdir)
|
||||||
.env("MIRI_SYSROOT", tmpdir)
|
.env("MIRI_SYSROOT", tmpdir)
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
64
src/db.rs
64
src/db.rs
|
|
@ -33,7 +33,7 @@ impl Display for BuildMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::FromRow, Serialize, Deserialize, Clone)]
|
#[derive(sqlx::FromRow, Serialize, Deserialize)]
|
||||||
pub struct BuildInfo {
|
pub struct BuildInfo {
|
||||||
pub nightly: String,
|
pub nightly: String,
|
||||||
pub target: String,
|
pub target: String,
|
||||||
|
|
@ -73,14 +73,6 @@ pub struct FinishedNightly {
|
||||||
pub mode: BuildMode,
|
pub mode: BuildMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::FromRow, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct FinishedNightlyWithBroken {
|
|
||||||
pub nightly: String,
|
|
||||||
pub mode: BuildMode,
|
|
||||||
pub is_broken: bool,
|
|
||||||
pub broken_error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Db {
|
impl Db {
|
||||||
pub async fn open(path: &str) -> Result<Self> {
|
pub async fn open(path: &str) -> Result<Self> {
|
||||||
let db_opts = SqliteConnectOptions::from_str(path)
|
let db_opts = SqliteConnectOptions::from_str(path)
|
||||||
|
|
@ -89,7 +81,7 @@ impl Db {
|
||||||
|
|
||||||
let conn = Pool::connect_with(db_opts)
|
let conn = Pool::connect_with(db_opts)
|
||||||
.await
|
.await
|
||||||
.wrap_err_with(|| format!("opening db from `{path}`"))?;
|
.wrap_err_with(|| format!("opening db from `{}`", path))?;
|
||||||
Ok(Self { conn })
|
Ok(Self { conn })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,6 +100,13 @@ impl Db {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn full_mega_monster(&self) -> Result<Vec<BuildInfo>> {
|
||||||
|
sqlx::query_as::<_, BuildInfo>("SELECT nightly, target, status, mode FROM build_info")
|
||||||
|
.fetch_all(&self.conn)
|
||||||
|
.await
|
||||||
|
.wrap_err("getting build status from DB")
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn history_for_target(&self, target: &str) -> Result<Vec<BuildInfo>> {
|
pub async fn history_for_target(&self, target: &str) -> Result<Vec<BuildInfo>> {
|
||||||
sqlx::query_as::<_, BuildInfo>(
|
sqlx::query_as::<_, BuildInfo>(
|
||||||
"SELECT nightly, target, status, mode FROM build_info WHERE target = ?",
|
"SELECT nightly, target, status, mode FROM build_info WHERE target = ?",
|
||||||
|
|
@ -118,26 +117,6 @@ impl Db {
|
||||||
.wrap_err("getting history for single target")
|
.wrap_err("getting history for single target")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn history_for_nightly(&self, nightly: &str) -> Result<Vec<BuildInfo>> {
|
|
||||||
sqlx::query_as::<_, BuildInfo>(
|
|
||||||
"SELECT nightly, target, status, mode FROM build_info WHERE nightly = ?",
|
|
||||||
)
|
|
||||||
.bind(nightly)
|
|
||||||
.fetch_all(&self.conn)
|
|
||||||
.await
|
|
||||||
.wrap_err("getting history for single nightly")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn nightly_info(&self, nightly: &str) -> Result<Vec<FinishedNightlyWithBroken>> {
|
|
||||||
sqlx::query_as::<_, FinishedNightlyWithBroken>(
|
|
||||||
"SELECT nightly, mode, is_broken, broken_error FROM finished_nightly WHERE nightly = ?",
|
|
||||||
)
|
|
||||||
.bind(nightly)
|
|
||||||
.fetch_all(&self.conn)
|
|
||||||
.await
|
|
||||||
.wrap_err("getting finished_nightly for single nightly")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn target_list(&self) -> Result<Vec<String>> {
|
pub async fn target_list(&self) -> Result<Vec<String>> {
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
struct TargetName {
|
struct TargetName {
|
||||||
|
|
@ -151,21 +130,6 @@ impl Db {
|
||||||
.map(|elems| elems.into_iter().map(|elem| elem.target).collect())
|
.map(|elems| elems.into_iter().map(|elem| elem.target).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn nightly_list(&self) -> Result<Vec<String>> {
|
|
||||||
#[derive(sqlx::FromRow)]
|
|
||||||
struct NightlyName {
|
|
||||||
nightly: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlx::query_as::<_, NightlyName>(
|
|
||||||
"SELECT DISTINCT nightly FROM build_info ORDER BY nightly DESC",
|
|
||||||
)
|
|
||||||
.fetch_all(&self.conn)
|
|
||||||
.await
|
|
||||||
.wrap_err("getting list of all targets")
|
|
||||||
.map(|elems| elems.into_iter().map(|elem| elem.nightly).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn build_status_full(
|
pub async fn build_status_full(
|
||||||
&self,
|
&self,
|
||||||
nightly: &str,
|
nightly: &str,
|
||||||
|
|
@ -222,16 +186,10 @@ impl Db {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn finish_nightly_as_broken(
|
pub async fn finish_nightly_as_broken(&self, nightly: &str, mode: BuildMode) -> Result<()> {
|
||||||
&self,
|
sqlx::query("INSERT INTO finished_nightly (nightly, mode, is_broken) VALUES (?, ?, TRUE)")
|
||||||
nightly: &str,
|
|
||||||
mode: BuildMode,
|
|
||||||
error: &str,
|
|
||||||
) -> Result<()> {
|
|
||||||
sqlx::query("INSERT INTO finished_nightly (nightly, mode, is_broken, broken_error) VALUES (?, ?, TRUE, ?)")
|
|
||||||
.bind(nightly)
|
.bind(nightly)
|
||||||
.bind(mode)
|
.bind(mode)
|
||||||
.bind(error)
|
|
||||||
.execute(&self.conn)
|
.execute(&self.conn)
|
||||||
.await
|
.await
|
||||||
.wrap_err("inserting finished broken nightly")?;
|
.wrap_err("inserting finished broken nightly")?;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use tracing::debug;
|
||||||
|
|
||||||
use crate::db::{BuildMode, FinishedNightly};
|
use crate::db::{BuildMode, FinishedNightly};
|
||||||
|
|
||||||
const EARLIEST_CUTOFF_DATE: &str = "2022-01-01";
|
const EARLIEST_CUTOFF_DATE: &str = "2023-01-01";
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NightlyCache {
|
pub struct NightlyCache {
|
||||||
|
|
@ -43,7 +43,7 @@ impl Nightlies {
|
||||||
.last()
|
.last()
|
||||||
.ok_or_eyre("did not find any nightlies in manifests.txt")?;
|
.ok_or_eyre("did not find any nightlies in manifests.txt")?;
|
||||||
|
|
||||||
for nightly in guess_more_recent_nightlies(latest)? {
|
for nightly in guess_more_recent_nightlies(&latest)? {
|
||||||
if nightly_exists(&nightly, cache)
|
if nightly_exists(&nightly, cache)
|
||||||
.await
|
.await
|
||||||
.wrap_err_with(|| format!("checking whether {nightly} exists"))?
|
.wrap_err_with(|| format!("checking whether {nightly} exists"))?
|
||||||
|
|
@ -54,10 +54,7 @@ impl Nightlies {
|
||||||
|
|
||||||
all.reverse();
|
all.reverse();
|
||||||
|
|
||||||
debug!(
|
debug!("Loaded {} nightlies from the manifest and manual additions", all.len());
|
||||||
"Loaded {} nightlies from the manifest and manual additions",
|
|
||||||
all.len()
|
|
||||||
);
|
|
||||||
Ok(Self { all })
|
Ok(Self { all })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
136
src/web.rs
136
src/web.rs
|
|
@ -4,8 +4,8 @@ use axum::{
|
||||||
extract::{Query, State},
|
extract::{Query, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
routing::get,
|
routing::{get, post},
|
||||||
Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
use color_eyre::{eyre::Context, Result};
|
use color_eyre::{eyre::Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -23,8 +23,11 @@ pub async fn webserver(db: Db) -> Result<()> {
|
||||||
.route("/", get(web_root))
|
.route("/", get(web_root))
|
||||||
.route("/build", get(web_build))
|
.route("/build", get(web_build))
|
||||||
.route("/target", get(web_target))
|
.route("/target", get(web_target))
|
||||||
.route("/nightly", get(web_nightly))
|
.route("/full-table", get(web_full_table))
|
||||||
.route("/index.css", get(index_css))
|
.route("/index.css", get(index_css))
|
||||||
|
.route("/index.js", get(index_js))
|
||||||
|
.route("/full-mega-monster", get(full_mega_monster))
|
||||||
|
.route("/trigger-build", post(trigger_build))
|
||||||
.with_state(AppState { db });
|
.with_state(AppState { db });
|
||||||
|
|
||||||
info!("Serving website on port 3000 (commit {})", crate::VERSION);
|
info!("Serving website on port 3000 (commit {})", crate::VERSION);
|
||||||
|
|
@ -141,114 +144,24 @@ async fn web_target(State(state): State<AppState>, Query(query): Query<TargetQue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct NightlyQuery {
|
|
||||||
nightly: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn web_nightly(State(state): State<AppState>, Query(query): Query<NightlyQuery>) -> Response {
|
|
||||||
use askama::Template;
|
|
||||||
#[derive(askama::Template)]
|
|
||||||
#[template(path = "nightly.html")]
|
|
||||||
struct NightlyPage {
|
|
||||||
nightly: String,
|
|
||||||
version: &'static str,
|
|
||||||
builds: Vec<(String, Option<BuildInfo>, Option<BuildInfo>)>,
|
|
||||||
core_failures: usize,
|
|
||||||
std_failures: usize,
|
|
||||||
core_broken: Option<String>,
|
|
||||||
std_broken: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
match state.db.history_for_nightly(&query.nightly).await {
|
|
||||||
Ok(builds) => match state.db.nightly_info(&query.nightly).await {
|
|
||||||
Ok(info) => {
|
|
||||||
let mut builds_grouped =
|
|
||||||
HashMap::<String, (Option<BuildInfo>, Option<BuildInfo>)>::new();
|
|
||||||
for build in &builds {
|
|
||||||
let v = builds_grouped.entry(build.target.clone()).or_default();
|
|
||||||
match build.mode {
|
|
||||||
BuildMode::Core => v.0 = Some(build.clone()),
|
|
||||||
BuildMode::MiriStd => v.1 = Some(build.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut std_failures = 0;
|
|
||||||
let mut core_failures = 0;
|
|
||||||
for build in builds {
|
|
||||||
if build.status == Status::Error {
|
|
||||||
match build.mode {
|
|
||||||
BuildMode::Core => core_failures += 1,
|
|
||||||
BuildMode::MiriStd => std_failures += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut builds = builds_grouped
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, (v1, v2))| (k, v1, v2))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
builds.sort_by_cached_key(|build| build.0.clone());
|
|
||||||
|
|
||||||
let core_broken = info
|
|
||||||
.iter()
|
|
||||||
.find(|info| info.mode == BuildMode::Core && info.is_broken)
|
|
||||||
.and_then(|info| info.broken_error.clone());
|
|
||||||
let std_broken = info
|
|
||||||
.iter()
|
|
||||||
.find(|info| info.mode == BuildMode::MiriStd && info.is_broken)
|
|
||||||
.and_then(|info| info.broken_error.clone());
|
|
||||||
|
|
||||||
let page = NightlyPage {
|
|
||||||
nightly: query.nightly,
|
|
||||||
version: crate::VERSION,
|
|
||||||
builds,
|
|
||||||
std_failures,
|
|
||||||
core_failures,
|
|
||||||
core_broken,
|
|
||||||
std_broken,
|
|
||||||
};
|
|
||||||
|
|
||||||
Html(page.render().unwrap()).into_response()
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(?err, "Error loading target state");
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
error!(?err, "Error loading target state");
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn web_root(State(state): State<AppState>) -> impl IntoResponse {
|
async fn web_root(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
#[derive(askama::Template)]
|
#[derive(askama::Template)]
|
||||||
#[template(path = "index.html")]
|
#[template(path = "index.html")]
|
||||||
struct RootPage {
|
struct RootPage {
|
||||||
targets: Vec<String>,
|
targets: Vec<String>,
|
||||||
nightlies: Vec<String>,
|
|
||||||
version: &'static str,
|
version: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
match state.db.target_list().await {
|
match state.db.target_list().await {
|
||||||
Ok(targets) => match state.db.nightly_list().await {
|
Ok(targets) => {
|
||||||
Ok(nightlies) => {
|
|
||||||
let page = RootPage {
|
let page = RootPage {
|
||||||
targets,
|
targets,
|
||||||
nightlies,
|
|
||||||
version: crate::VERSION,
|
version: crate::VERSION,
|
||||||
};
|
};
|
||||||
|
|
||||||
Html(page.render().unwrap()).into_response()
|
Html(page.render().unwrap()).into_response()
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
error!(?err, "Error loading nightly state");
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(?err, "Error loading target state");
|
error!(?err, "Error loading target state");
|
||||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
|
@ -256,6 +169,9 @@ async fn web_root(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn web_full_table() -> impl IntoResponse {
|
||||||
|
Html(include_str!("../static/full-table.html").replace("{{version}}", crate::VERSION))
|
||||||
|
}
|
||||||
async fn index_css() -> impl IntoResponse {
|
async fn index_css() -> impl IntoResponse {
|
||||||
(
|
(
|
||||||
[(
|
[(
|
||||||
|
|
@ -265,12 +181,44 @@ async fn index_css() -> impl IntoResponse {
|
||||||
include_str!("../static/index.css"),
|
include_str!("../static/index.css"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
async fn index_js() -> impl IntoResponse {
|
||||||
|
(
|
||||||
|
[(
|
||||||
|
axum::http::header::CONTENT_TYPE,
|
||||||
|
axum::http::HeaderValue::from_static("text/javascript"),
|
||||||
|
)],
|
||||||
|
include_str!("../static/index.js"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn full_mega_monster(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
|
state.db.full_mega_monster().await.map(Json).map_err(|err| {
|
||||||
|
error!(?err, "Error loading target state");
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct TriggerBuildBody {
|
struct TriggerBuildBody {
|
||||||
nightly: String,
|
nightly: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[axum::debug_handler]
|
||||||
|
async fn trigger_build(
|
||||||
|
State(_state): State<AppState>,
|
||||||
|
_body: Json<TriggerBuildBody>,
|
||||||
|
) -> StatusCode {
|
||||||
|
return StatusCode::BAD_REQUEST;
|
||||||
|
// tokio::spawn(async move {
|
||||||
|
// let result = build::build_every_target_for_toolchain(&state.db, &body.nightly).await;
|
||||||
|
// if let Err(err) = result {
|
||||||
|
// error!(?err, "Error while building");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// StatusCode::ACCEPTED
|
||||||
|
}
|
||||||
|
|
||||||
impl Status {
|
impl Status {
|
||||||
fn to_emoji(&self) -> &'static str {
|
fn to_emoji(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
|
@ -19,12 +19,6 @@
|
||||||
<pre>
|
<pre>
|
||||||
{{stderr}}
|
{{stderr}}
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
|
||||||
<a href="/target?target={{target}}">Build history for target {{target}}</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="/nightly?nightly={{nightly}}">Build state for nightly {{nightly}}</a>
|
|
||||||
</p>
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<span>does-it-build {{version}}</span>
|
<span>does-it-build {{version}}</span>
|
||||||
<a href="https://github.com/Noratrieb/does-it-build">
|
<a href="https://github.com/Noratrieb/does-it-build">
|
||||||
|
|
|
||||||
68
static/full-table.html
Normal file
68
static/full-table.html
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Does it build?</title>
|
||||||
|
<link rel="stylesheet" href="index.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Does it build?</h1>
|
||||||
|
<p>This website builds every rustc target on many nightlies to check which ones work and which ones do not.</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#core-build">Core build</a></li>
|
||||||
|
<li><a href="#std-check-build">Std check build</a></li>
|
||||||
|
</ul>
|
||||||
|
<!--core-->
|
||||||
|
<h2 id="core-build">Core Build</h2>
|
||||||
|
<p>Builds every target with:
|
||||||
|
<pre>cargo build --release -Zbuild-std=core</pre></p>
|
||||||
|
<p>This checks that codegen/linking of core works, but does not check whether std builds.</p>
|
||||||
|
<label for="target-filter">Target Filter</label>
|
||||||
|
<input id="target-filter" type="search" />
|
||||||
|
<label for="target-filter-failed">Filter failed</label>
|
||||||
|
<input type="checkbox" id="target-filter-failed" />
|
||||||
|
|
||||||
|
<table id="target-state" class="target-state-table">
|
||||||
|
<tr>
|
||||||
|
<td>loading...</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!--std-->
|
||||||
|
<h2 id="std-check-build">Std Check Build</h2>
|
||||||
|
<p>Builds every target with:
|
||||||
|
<pre>cargo miri setup</pre></p>
|
||||||
|
<p>This checks that std builds (on targets that have it) but does not check whether codegen/linking works.</p>
|
||||||
|
<label for="target-filter-miri">Target Filter</label>
|
||||||
|
<input id="target-filter-miri" type="search" />
|
||||||
|
<label for="target-filter-failed-miri">Filter failed</label>
|
||||||
|
<input type="checkbox" id="target-filter-failed-miri" />
|
||||||
|
|
||||||
|
<table id="target-state-miri" class="target-state-table">
|
||||||
|
<tr>
|
||||||
|
<td>loading...</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<span>does-it-build {{version}}</span>
|
||||||
|
<a href="https://github.com/Noratrieb/does-it-build">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
aria-labelledby="github-logo-title"
|
||||||
|
>
|
||||||
|
<title id="github-logo-title">GitHub</title>
|
||||||
|
<path
|
||||||
|
fill="black"
|
||||||
|
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
<script src="index.js">
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
159
static/index.js
Normal file
159
static/index.js
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
class Table {
|
||||||
|
constructor(data, tableElemId, filterElemId, filterFailedElemId) {
|
||||||
|
this.data = data;
|
||||||
|
this.elem = document.getElementById(tableElemId);
|
||||||
|
|
||||||
|
document.getElementById(filterElemId).addEventListener("input", (e) => {
|
||||||
|
this.filter.search = e.target.value;
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
document
|
||||||
|
.getElementById(filterFailedElemId)
|
||||||
|
.addEventListener("input", (e) => {
|
||||||
|
this.filter.filterFailed = e.target.checked;
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.filter = {
|
||||||
|
search: "",
|
||||||
|
filterFailed: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
update(data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const allTargets = new Set();
|
||||||
|
const allNightlies = new Set();
|
||||||
|
|
||||||
|
// The infos grouped by target.
|
||||||
|
const targetInfos = new Map();
|
||||||
|
|
||||||
|
// Targets that have, at some point, errored
|
||||||
|
const targetsWithErrors = new Set();
|
||||||
|
|
||||||
|
// Whether a nightly is completely broken.
|
||||||
|
// These are still filtered out when filter failed is selected.
|
||||||
|
const isNightlyBroken = new Map();
|
||||||
|
|
||||||
|
// The first pass over the data, to find nightlies that are broken.
|
||||||
|
for (const info of this.data) {
|
||||||
|
if (!isNightlyBroken.has(info.nightly)) {
|
||||||
|
// Assume that a nightly is broken until proven otherwise.
|
||||||
|
isNightlyBroken.set(info.nightly, true);
|
||||||
|
}
|
||||||
|
if (info.status == "pass") {
|
||||||
|
// This nightly has built something, so it's clearly not broken :).
|
||||||
|
isNightlyBroken.set(info.nightly, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass over the data, group by nightly and prepare data for filter.
|
||||||
|
for (const info of this.data) {
|
||||||
|
allNightlies.add(info.nightly);
|
||||||
|
|
||||||
|
if (!info.target.includes(this.filter.search)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.status === "error" && !isNightlyBroken.get(info.nightly)) {
|
||||||
|
targetsWithErrors.add(info.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
allTargets.add(info.target);
|
||||||
|
if (!targetInfos.has(info.target)) {
|
||||||
|
targetInfos.set(info.target, new Map());
|
||||||
|
}
|
||||||
|
targetInfos.get(info.target).set(info.nightly, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nightlies = Array.from(allNightlies);
|
||||||
|
nightlies.sort();
|
||||||
|
nightlies.reverse();
|
||||||
|
const targets = Array.from(allTargets);
|
||||||
|
targets.sort();
|
||||||
|
|
||||||
|
const header = document.createElement("tr");
|
||||||
|
const headerTarget = document.createElement("th");
|
||||||
|
headerTarget.innerText = "target";
|
||||||
|
header.appendChild(headerTarget);
|
||||||
|
nightlies.forEach((target) => {
|
||||||
|
const elem = document.createElement("th");
|
||||||
|
elem.classList.add("target-header");
|
||||||
|
elem.innerText = target;
|
||||||
|
header.appendChild(elem);
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = targets.flatMap((target) => {
|
||||||
|
if (this.filter.filterFailed && !targetsWithErrors.has(target)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
|
||||||
|
const targetCol = document.createElement("td");
|
||||||
|
targetCol.innerText = target;
|
||||||
|
targetCol.classList.add("target-name-col");
|
||||||
|
tr.appendChild(targetCol);
|
||||||
|
|
||||||
|
const info = targetInfos.get(target) ?? new Map();
|
||||||
|
|
||||||
|
for (const nightly of nightlies) {
|
||||||
|
const td = document.createElement("td");
|
||||||
|
const targetInfo = info.get(nightly);
|
||||||
|
|
||||||
|
if (targetInfo) {
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.classList.add("build-info-a");
|
||||||
|
a.href = `build?nightly=${encodeURIComponent(
|
||||||
|
nightly
|
||||||
|
)}&target=${encodeURIComponent(target)}&mode=${encodeURIComponent(
|
||||||
|
targetInfo.mode
|
||||||
|
)}`;
|
||||||
|
a.innerText = targetInfo.status == "pass" ? "✅" : "❌";
|
||||||
|
td.appendChild(a);
|
||||||
|
td.classList.add("build-cell");
|
||||||
|
td.classList.add(targetInfo.status);
|
||||||
|
} else {
|
||||||
|
td.innerText = "";
|
||||||
|
td.classList.add("missing");
|
||||||
|
}
|
||||||
|
tr.appendChild(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [tr];
|
||||||
|
});
|
||||||
|
this.elem.replaceChildren(header, ...rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const coreTable = new Table(
|
||||||
|
[],
|
||||||
|
"target-state",
|
||||||
|
"target-filter",
|
||||||
|
"target-filter-failed"
|
||||||
|
);
|
||||||
|
const miriTable = new Table(
|
||||||
|
[],
|
||||||
|
"target-state-miri",
|
||||||
|
"target-filter-miri",
|
||||||
|
"target-filter-failed-miri"
|
||||||
|
);
|
||||||
|
|
||||||
|
function fetchTargets() {
|
||||||
|
fetch("full-mega-monster")
|
||||||
|
.then((body) => body.json())
|
||||||
|
.then((body) => {
|
||||||
|
const core = body.filter((info) => info.mode === "core");
|
||||||
|
const miri = body.filter((info) => info.mode === "miri-std");
|
||||||
|
coreTable.update(core);
|
||||||
|
miriTable.update(miri);
|
||||||
|
coreTable.render();
|
||||||
|
miriTable.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial fetch
|
||||||
|
fetchTargets();
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
|
@ -22,22 +22,7 @@
|
||||||
your browser).
|
your browser).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>Nightlies</h1>
|
<ul class="target-list">
|
||||||
<ul>
|
|
||||||
{% for nightly in nightlies.iter().take(5) %}
|
|
||||||
<li>
|
|
||||||
<a href="nightly?nightly={{ nightly }}">{{ nightly }}</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
To view a list of all nightlies, check
|
|
||||||
<a href="#all-nightlies">the list at the end</a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Targets</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for target in targets %}
|
{% for target in targets %}
|
||||||
<li>
|
<li>
|
||||||
<a href="target?target={{ target }}">{{ target }}</a>
|
<a href="target?target={{ target }}">{{ target }}</a>
|
||||||
|
|
@ -45,18 +30,6 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<section id="all-nightlies">
|
|
||||||
<h2>All Nightlies</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for nightly in nightlies %}
|
|
||||||
<li>
|
|
||||||
<a href="nightly?nightly={{ nightly }}">{{ nightly }}</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<span>does-it-build {{version}}</span>
|
<span>does-it-build {{version}}</span>
|
||||||
<a href="https://github.com/Noratrieb/does-it-build">
|
<a href="https://github.com/Noratrieb/does-it-build">
|
||||||
|
|
@ -74,5 +47,6 @@
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script src="index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>{{nightly}} build history</title>
|
|
||||||
<link rel="stylesheet" href="/index.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Nightly build state for {{nightly}}</h1>
|
|
||||||
<a href="/">Back</a>
|
|
||||||
<p>
|
|
||||||
This contains the status of this nightly. Core is built with
|
|
||||||
<code>cargo build --release -Zbuild-std=core</code>. This checks that
|
|
||||||
codegen/linking of core works, but does not check whether std builds.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
std is being built with <code>cargo miri setup</code>. If a target does
|
|
||||||
not support std, the std column represents core/alloc. This checks that
|
|
||||||
std builds (on targets that have it) but does not check whether
|
|
||||||
codegen/linking works.
|
|
||||||
</p>
|
|
||||||
{% if let Some(core_broken) = core_broken %}
|
|
||||||
<p>
|
|
||||||
⚠️ The core build is broken in general for this nightly, so no data is available ⚠️
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
{{core_broken}}
|
|
||||||
</pre>
|
|
||||||
{% endif %}
|
|
||||||
{% if let Some(std_broken) = std_broken %}
|
|
||||||
<p>
|
|
||||||
⚠️ The std build is broken for this nightly, so no data is available ⚠️
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
{{std_broken}}
|
|
||||||
</pre>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<dl>
|
|
||||||
<dt>core failures</dt>
|
|
||||||
<dd>{{core_failures}}</dd>
|
|
||||||
<dt>std failures</dt>
|
|
||||||
<dd>{{std_failures}}</dd>
|
|
||||||
</dl>
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>nightly</th>
|
|
||||||
<th>core</th>
|
|
||||||
<th>std</th>
|
|
||||||
</tr>
|
|
||||||
{% for build in builds %}
|
|
||||||
<tr>
|
|
||||||
<td><a href="/target?target={{build.0}}">{{ build.0 }}</a></td>
|
|
||||||
{% match build.1 %} {% when Some with (build) %}
|
|
||||||
<td class="build-cell">
|
|
||||||
<a class="build-info-a" href="{{ build.link() }}">
|
|
||||||
{{ build.status.to_emoji() }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% when None %}
|
|
||||||
<td class="missing"></td>
|
|
||||||
{% endmatch %} {% match build.2 %} {% when Some with (build) %}
|
|
||||||
<td class="build-cell">
|
|
||||||
<a class="build-info-a" href="{{ build.link() }}">
|
|
||||||
<span> {{ build.status.to_emoji() }} </span>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% when None %}
|
|
||||||
<td class="missing"></td>
|
|
||||||
{% endmatch %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<footer class="footer">
|
|
||||||
<span>does-it-build {{version}}</span>
|
|
||||||
<a href="https://github.com/Noratrieb/does-it-build">
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
aria-labelledby="github-logo-title"
|
|
||||||
>
|
|
||||||
<title id="github-logo-title">GitHub</title>
|
|
||||||
<path
|
|
||||||
fill="black"
|
|
||||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% for build in builds %}
|
{% for build in builds %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/nightly?nightly={{build.0}}">{{ build.0 }}</a></td>
|
<td>{{ build.0 }}</td>
|
||||||
{% match build.1 %} {% when Some with (build) %}
|
{% match build.1 %} {% when Some with (build) %}
|
||||||
<td class="build-cell">
|
<td class="build-cell">
|
||||||
<a class="build-info-a" href="{{ build.link() }}">
|
<a class="build-info-a" href="{{ build.link() }}">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue