airbender_build/
config.rs1use crate::build::ReproducibleBuild;
4use crate::build::{DistArtifact, DistArtifacts, LocalBuild};
5use crate::constants::DEFAULT_APP_NAME;
6use crate::errors::Result;
7use crate::resolver::ResolvedBuildParams;
8use crate::{ArtifactEntry, BuildMetadata, Manifest, Profile, MANIFEST_VERSION_V1};
9use std::fs;
10use std::path::{Path, PathBuf};
11
12#[derive(Clone, Debug)]
14pub struct BuildConfig {
15 pub project_dir: PathBuf,
17 pub app_name: String,
19 pub bin_name: Option<String>,
21 pub target: Option<String>,
23 pub profile: Profile,
25 pub dist_dir: Option<PathBuf>,
27 pub cargo_args: Vec<String>,
29 pub reproducible: bool,
32 pub workspace_root_override: Option<PathBuf>,
38}
39
40impl BuildConfig {
41 pub fn new(project_dir: impl Into<PathBuf>) -> Self {
43 Self {
44 project_dir: project_dir.into(),
45 app_name: DEFAULT_APP_NAME.to_string(),
46 bin_name: None,
47 target: None,
48 profile: Profile::Release,
49 dist_dir: None,
50 cargo_args: Vec::new(),
51 reproducible: false,
52 workspace_root_override: None,
53 }
54 }
55
56 fn build_dist(&self) -> Result<DistArtifacts> {
58 let cwd = std::env::current_dir()?;
59 let params = ResolvedBuildParams::resolve(self, &cwd)?;
60
61 let extra_config = params
65 .panic_immediate_abort
66 .then_some(r#"build.rustflags=["-Zunstable-options","-Cpanic=immediate-abort"]"#);
67
68 fs::create_dir_all(params.dist_app.dir())?;
69
70 if self.reproducible {
71 ReproducibleBuild::new(¶ms)?.run(self.profile, &self.cargo_args, extra_config)?;
72 } else {
73 LocalBuild::new(¶ms).run(self.profile, &self.cargo_args, extra_config)?;
74 }
75
76 let artifacts = DistArtifacts {
77 dir: params.dist_app.dir().to_path_buf(),
78 app_bin: DistArtifact::new(params.dist_app.bin().to_path_buf())?,
79 app_elf: DistArtifact::new(params.dist_app.elf().to_path_buf())?,
80 app_text: DistArtifact::new(params.dist_app.text().to_path_buf())?,
81 manifest_path: params.dist_app.manifest().to_path_buf(),
82 };
83
84 fn file_name(p: &Path) -> String {
85 p.file_name()
86 .expect("must be valid")
87 .to_str()
88 .expect("must be valid")
89 .to_string()
90 }
91 let manifest = Manifest {
92 package: params.package_name,
93 bin_name: params.manifest_bin_name,
94 manifest: MANIFEST_VERSION_V1.to_string(),
95 codec: format!("v{}", airbender_codec::AIRBENDER_CODEC_V0),
96 target: params.target,
97 bin: ArtifactEntry {
98 path: file_name(params.dist_app.bin()),
99 sha256: artifacts.app_bin.sha256.clone(),
100 },
101 elf: ArtifactEntry {
102 path: file_name(params.dist_app.elf()),
103 sha256: artifacts.app_elf.sha256.clone(),
104 },
105 text: ArtifactEntry {
106 path: file_name(params.dist_app.text()),
107 sha256: artifacts.app_text.sha256.clone(),
108 },
109 build: BuildMetadata {
110 profile: self.profile,
111 reproducible: self.reproducible,
112 git_branch: params.git.branch,
113 git_commit: params.git.commit,
114 is_dirty: params.git.is_dirty,
115 },
116 };
117 manifest.write_to_file(params.dist_app.manifest())?;
118
119 Ok(artifacts)
120 }
121}
122
123pub fn build_dist(config: &BuildConfig) -> Result<DistArtifacts> {
125 config.build_dist()
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn reproducible_flag_defaults_to_false() {
134 let config = BuildConfig::new(PathBuf::from("."));
135 assert!(!config.reproducible);
136 }
137
138 #[test]
139 fn workspace_root_override_defaults_to_none() {
140 let config = BuildConfig::new(PathBuf::from("."));
141 assert!(config.workspace_root_override.is_none());
142 }
143}