refactor(xtask): introduce ResolvedBuildArgs
What changed, and why it matters
This commit is a straightforward internal code refactor in Trezor's build tooling. It introduces a new ResolvedBuildArgs structure that centralizes default values for command-line build options before they are used. There is no change to user-facing behavior, no fix for a bug, and no security-related change.
No action required. This is a non-security refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors core/embed/xtask to resolve BuildArgs into ResolvedBuildArgs once at entry points (build, clippy, check), then passes the resolved struct through the build pipeline. Default-value logic (e.g., debug defaulting to emulator, pyopt defaulting to true, debug_link defaulting to !pyopt) is moved from scattered unwrap_or calls into a single from_build_args constructor. The profile selection logic is simplified but semantically equivalent for the resolved values. No functional behavior is altered.
Changed components
core/embed/xtask/src/args.rscore/embed/xtask/src/artifacts.rscore/embed/xtask/src/cargo.rscore/embed/xtask/src/feature_resolver.rscore/embed/xtask/src/helpers.rsInspect captured patch +99 / −29
diff --git a/core/embed/xtask/src/args.rs b/core/embed/xtask/src/args.rs
index 40d402d0..ad3c4749 100644
--- a/core/embed/xtask/src/args.rs
+++ b/core/embed/xtask/src/args.rs
@@ -267,13 +267,81 @@ pub struct BuildArgs {
pub verbose: bool,
}
-impl BuildArgs {
+#[derive(Debug, Clone)]
+pub struct ResolvedBuildArgs {
+ pub project: Project,
+ pub model: Model,
+ pub emulator: bool,
+ pub debug: bool,
+ pub dbg_console: Option<ConsoleType>,
+ pub btc_only: bool,
+ pub production: bool,
+ pub force_bootloader_upgrade: bool,
+ pub bootloader_devel: bool,
+ pub unsafe_fw: bool,
+ pub frozen: bool,
+ pub source_lines: bool,
+ pub pyopt: bool,
+ pub mem_perf: bool,
+ pub debug_link: bool,
+ pub n4w1: bool,
+ pub disable_animation: bool,
+ pub perf_overlay: bool,
+ pub benchmark: bool,
+ pub log_stack_usage: bool,
+ pub block_on_vcp: bool,
+ pub asan: bool,
+ pub apps: bool,
+ pub disable_optiga: bool,
+ pub board: Option<String>,
+ pub disable_tropic: bool,
+ pub storage_insecure_testing_mode: bool,
+ pub emit_memory_analysis: bool,
+ pub timings: bool,
+ pub verbose: bool,
+}
+
+impl ResolvedBuildArgs {
+ pub fn from_build_args(args: &BuildArgs) -> Self {
+ let pyopt = args.pyopt.unwrap_or(true);
+ Self {
+ project: args.project,
+ model: args.model,
+ emulator: args.emulator,
+ debug: args.debug.unwrap_or(args.emulator),
+ dbg_console: args.dbg_console,
+ btc_only: args.btc_only,
+ production: args.production,
+ force_bootloader_upgrade: args.force_bootloader_upgrade,
+ bootloader_devel: args.bootloader_devel,
+ unsafe_fw: args.unsafe_fw,
+ frozen: args.frozen,
+ source_lines: args.source_lines.unwrap_or(args.emulator),
+ pyopt,
+ mem_perf: args.mem_perf,
+ debug_link: args.debug_link.unwrap_or(!pyopt),
+ n4w1: args.n4w1,
+ disable_animation: args.disable_animation,
+ perf_overlay: args.perf_overlay,
+ benchmark: args.benchmark,
+ log_stack_usage: args.log_stack_usage,
+ block_on_vcp: args.block_on_vcp,
+ asan: args.asan,
+ apps: args.apps,
+ disable_optiga: args.disable_optiga,
+ board: args.board.clone(),
+ disable_tropic: args.disable_tropic,
+ storage_insecure_testing_mode: args.storage_insecure_testing_mode,
+ emit_memory_analysis: args.emit_memory_analysis,
+ timings: args.timings,
+ verbose: args.verbose,
+ }
+ }
+
/// Determines the Cargo profile to use
pub fn profile_name(&self) -> &'static str {
- if self.emulator && self.debug.unwrap_or(true) {
- "dev"
- } else if !self.emulator && self.debug.unwrap_or(false) {
- "debug-opt"
+ if self.debug {
+ if self.emulator { "dev" } else { "debug-opt" }
} else {
"release"
}
diff --git a/core/embed/xtask/src/artifacts.rs b/core/embed/xtask/src/artifacts.rs
index aaf8cf0a..cf7f07e9 100644
--- a/core/embed/xtask/src/artifacts.rs
+++ b/core/embed/xtask/src/artifacts.rs
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
-use crate::args::BuildArgs;
+use crate::args::ResolvedBuildArgs;
use crate::helpers;
/// Returns whether a filesystem entry exists without following symlinks, so
@@ -64,7 +64,7 @@ fn copy_if_newer(src: &Path, dst: &Path) -> Result<bool> {
/// Collects build artifacts into `build-xtask/artifacts/{MODEL_ID}/`.
/// When `is_dependency` is true, the `.bin` file is skipped (only ELF, MAP,
/// and compile_commands are collected).
-pub fn collect_artifacts(args: &BuildArgs, is_dependency: bool) -> Result<()> {
+pub fn collect_artifacts(args: &ResolvedBuildArgs, is_dependency: bool) -> Result<()> {
let artifact_dir = helpers::build_dir()?
.join("artifacts")
.join(args.model.model_id());
diff --git a/core/embed/xtask/src/cargo.rs b/core/embed/xtask/src/cargo.rs
index 04ebbcfe..8b1e0e20 100644
--- a/core/embed/xtask/src/cargo.rs
+++ b/core/embed/xtask/src/cargo.rs
@@ -3,13 +3,15 @@ use std::process;
use anyhow::{Context, Result, ensure};
use owo_colors::OwoColorize;
-use crate::args::{BuildArgs, Project, TestArgs};
+use crate::args::{BuildArgs, Project, ResolvedBuildArgs, TestArgs};
use crate::{artifacts, feature_resolver, helpers, memusage, postbuild, prebuild};
pub fn build(args: BuildArgs) -> Result<()> {
- build_impl(args.clone(), false)?;
+ let resolved_args = ResolvedBuildArgs::from_build_args(&args);
- if args.storage_insecure_testing_mode {
+ build_impl(resolved_args.clone(), false)?;
+
+ if resolved_args.storage_insecure_testing_mode {
println!(
"{}",
"STORAGE_INSECURE_TESTING_MODE enabled, DO NOT USE"
@@ -22,11 +24,13 @@ pub fn build(args: BuildArgs) -> Result<()> {
}
pub fn clippy(args: BuildArgs) -> Result<()> {
- run_cargo_subcommand("clippy", &args)
+ let resolved_args = ResolvedBuildArgs::from_build_args(&args);
+ run_cargo_subcommand("clippy", &resolved_args)
}
pub fn check(args: BuildArgs) -> Result<()> {
- run_cargo_subcommand("check", &args)
+ let resolved_args = ResolvedBuildArgs::from_build_args(&args);
+ run_cargo_subcommand("check", &resolved_args)
}
pub fn test(args: TestArgs) -> Result<()> {
@@ -82,12 +86,12 @@ pub fn fmt() -> Result<()> {
Ok(())
}
-fn build_impl(args: BuildArgs, is_dependency: bool) -> Result<()> {
+fn build_impl(args: ResolvedBuildArgs, is_dependency: bool) -> Result<()> {
if !args.emulator {
// Recursively build dependencies (Firmware -> Kernel -> Secmon)
if let Some(dependency) = args.project.dependency(args.model)? {
build_impl(
- BuildArgs {
+ ResolvedBuildArgs {
project: dependency,
..args.clone()
},
@@ -164,7 +168,7 @@ fn build_impl(args: BuildArgs, is_dependency: bool) -> Result<()> {
Ok(())
}
-fn run_cargo_subcommand(subcommand: &str, args: &BuildArgs) -> Result<()> {
+fn run_cargo_subcommand(subcommand: &str, args: &ResolvedBuildArgs) -> Result<()> {
let mut cmd = process::Command::new("cargo");
cmd.arg(subcommand).current_dir(helpers::workspace_dir()?);
diff --git a/core/embed/xtask/src/feature_resolver.rs b/core/embed/xtask/src/feature_resolver.rs
index e73db330..53eb17b0 100644
--- a/core/embed/xtask/src/feature_resolver.rs
+++ b/core/embed/xtask/src/feature_resolver.rs
@@ -2,7 +2,7 @@ use std::process;
use anyhow::{Result, bail};
-use crate::args::{BuildArgs, ConsoleType, Project};
+use crate::args::{ConsoleType, Project, ResolvedBuildArgs};
use crate::{config, helpers};
pub struct ResolvedBuildFeatures {
@@ -12,7 +12,7 @@ pub struct ResolvedBuildFeatures {
}
/// Resolves cargo features and target triple from the provided CLI arguments.
-pub fn resolve_features(args: &BuildArgs) -> Result<ResolvedBuildFeatures> {
+pub fn resolve_features(args: &ResolvedBuildArgs) -> Result<ResolvedBuildFeatures> {
let mut features: Vec<String> = vec![args.model.feature_name()];
if args.emulator {
@@ -50,16 +50,14 @@ pub fn resolve_features(args: &BuildArgs) -> Result<ResolvedBuildFeatures> {
}
}
- let pyopt = args.pyopt.unwrap_or(true);
-
if args.project == Project::Firmware {
- if pyopt {
+ if args.pyopt {
features.push("pyopt".into());
} else {
features.push("debug".into());
}
- if args.source_lines.unwrap_or(args.emulator) {
+ if args.source_lines {
features.push("micropy_enable_source_lines".into());
}
@@ -102,7 +100,7 @@ pub fn resolve_features(args: &BuildArgs) -> Result<ResolvedBuildFeatures> {
features.push("universal_fw".into());
}
- if !pyopt {
+ if !args.pyopt {
features.push("optiga_testing".into());
}
@@ -126,11 +124,11 @@ pub fn resolve_features(args: &BuildArgs) -> Result<ResolvedBuildFeatures> {
features.push("ui_performance_overlay".into());
}
- if !pyopt {
+ if !args.pyopt {
features.push("ui_debug_overlay".into());
}
- if args.debug_link.unwrap_or(!pyopt) {
+ if args.debug_link {
features.push("debuglink".into());
features.push("ui_debug".into());
}
@@ -141,7 +139,7 @@ pub fn resolve_features(args: &BuildArgs) -> Result<ResolvedBuildFeatures> {
}
if matches!(args.project, Project::Kernel) {
- if args.debug_link.unwrap_or(!pyopt) {
+ if args.debug_link {
features.push("debuglink".into());
}
}
@@ -183,7 +181,7 @@ pub fn resolve_features(args: &BuildArgs) -> Result<ResolvedBuildFeatures> {
}
/// Configures a cargo command with the appropriate arguments and features.
-pub fn configure_cargo(args: &BuildArgs, cmd: &mut process::Command) -> Result<()> {
+pub fn configure_cargo(args: &ResolvedBuildArgs, cmd: &mut process::Command) -> Result<()> {
let resolved = resolve_features(args)?;
let mut rebuild_std = false;
diff --git a/core/embed/xtask/src/helpers.rs b/core/embed/xtask/src/helpers.rs
index bb259247..d6e6a0a8 100644
--- a/core/embed/xtask/src/helpers.rs
+++ b/core/embed/xtask/src/helpers.rs
@@ -4,17 +4,17 @@ use std::{env, fs};
use anyhow::{Context, Result, anyhow};
use cargo_metadata::MetadataCommand;
-use crate::args::{BuildArgs, Model, Project};
+use crate::args::{Model, Project, ResolvedBuildArgs};
/// Returns the path to the built ELF file for the given build arguments.
-pub fn elf_path(args: &BuildArgs) -> Result<PathBuf> {
+pub fn elf_path(args: &ResolvedBuildArgs) -> Result<PathBuf> {
let elf_name = args.project.package_name(args.emulator);
Ok(profile_dir(args)?.join(elf_name))
}
/// Returns the profile output directory (e.g.
/// `build/thumbv7em-none-eabihf/release`).
-pub fn profile_dir(args: &BuildArgs) -> Result<PathBuf> {
+pub fn profile_dir(args: &ResolvedBuildArgs) -> Result<PathBuf> {
let mut path = build_dir()?;
if !args.emulator {
let model_config = args.model.config()?;
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.