Support using custom rustflags for targets

This is necessary for AVR and AMDGPU. We currently just hardcode some
flags in the binary, which is good enough. There's a new column which
keeps track of the used flags and shows them for a build.

A migration makes sure that the older results are properly backfilled
with the flags to make them build.
This commit is contained in:
nora 2025-07-04 18:21:52 +02:00
parent 42cd9fda83
commit b1d1728c42
5 changed files with 81 additions and 19 deletions

View file

@ -0,0 +1,8 @@
-- 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

@ -18,6 +18,22 @@ use crate::{
nightlies::Nightlies, nightlies::Nightlies,
}; };
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 {
@ -44,9 +60,7 @@ pub async fn background_builder(db: Db) -> Result<()> {
} }
async fn background_builder_inner(db: &Db) -> Result<()> { async fn background_builder_inner(db: &Db) -> Result<()> {
let nightlies = Nightlies::fetch() let nightlies = Nightlies::fetch().await.wrap_err("fetching nightlies")?;
.await
.wrap_err("fetching nightlies")?;
let already_finished = db let already_finished = db
.finished_nightlies() .finished_nightlies()
.await .await
@ -243,6 +257,7 @@ 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?;
@ -252,6 +267,7 @@ 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.
@ -261,6 +277,8 @@ 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")
@ -277,11 +295,22 @@ 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()))?;
Command::new("cargo") let mut cmd = Command::new("cargo");
.arg(format!("+{toolchain}")) cmd.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")?
@ -307,5 +336,9 @@ async fn build_target(
info!("Finished build"); info!("Finished build");
Ok(BuildResult { status, stderr }) Ok(BuildResult {
status,
stderr,
rustflags,
})
} }

View file

@ -48,6 +48,7 @@ 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)]
@ -95,13 +96,14 @@ 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) VALUES (?, ?, ?, ?, ?);", "INSERT INTO build_info (nightly, target, status, stderr, mode, rustflags) 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")?;
@ -173,7 +175,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 FROM build_info "SELECT nightly, target, status, stderr, mode, rustflags FROM build_info
WHERE nightly = ? AND target = ? AND mode = ?", WHERE nightly = ? AND target = ? AND mode = ?",
) )
.bind(nightly) .bind(nightly)

View file

@ -41,6 +41,19 @@ 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(
@ -51,15 +64,16 @@ async fn web_build(State(state): State<AppState>, Query(query): Query<BuildQuery
.await .await
{ {
Ok(Some(build)) => { Ok(Some(build)) => {
let page = include_str!("../static/build.html") let page = BuildPage {
.replace("{{nightly}}", &query.nightly) nightly: query.nightly,
.replace("{{target}}", &query.target) target: query.target,
.replace("{{stderr}}", &build.stderr) stderr: build.stderr,
.replace("{{mode}}", &build.mode.to_string()) mode: build.mode,
.replace("{{version}}", crate::VERSION) rustflags: build.rustflags,
.replace("{{status}}", &build.status.to_string()); version: crate::VERSION,
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) => {

View file

@ -17,6 +17,11 @@
<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>