1use crate::BuildContext;
6use std::path::PathBuf;
7
8#[derive(Clone, Debug)]
10pub enum BuildStep {
11 Run {
12 description: String,
13 program: String,
14 args: Vec<String>,
15 cwd: PathBuf,
16 env: Vec<(String, String)>,
17 },
18 CreateDir {
19 path: PathBuf,
20 },
21 Copy {
22 from: PathBuf,
23 to: PathBuf,
24 },
25 RemoveIfExists {
26 path: PathBuf,
27 },
28 Write {
30 path: PathBuf,
31 contents: Vec<u8>,
32 },
33 Mirror {
40 dir: PathBuf,
41 files: Vec<(PathBuf, PathBuf)>,
43 keep: Vec<PathBuf>,
45 },
46}
47
48#[derive(Clone, Debug, Default)]
49pub struct BuildPlan {
50 pub steps: Vec<BuildStep>,
51 pub products: Vec<PathBuf>,
53 pub run: Option<Run>,
55 pub notes: Vec<String>,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum Run {
63 Program(PathBuf),
65 Page(PathBuf),
68 PageHost(PathBuf),
72 Simulator {
75 app: PathBuf,
76 bundle_id: String,
77 system: SimulatedSystem,
78 },
79 Android {
82 apk: PathBuf,
83 package: String,
84 adb: PathBuf,
85 forward: Option<(PathBuf, String)>,
88 },
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub enum SimulatedSystem {
94 Ios,
95 Tvos,
96}
97
98impl SimulatedSystem {
99 pub fn name(self) -> &'static str {
102 match self {
103 SimulatedSystem::Ios => "iOS",
104 SimulatedSystem::Tvos => "tvOS",
105 }
106 }
107}
108
109pub fn dist_dir(context: &BuildContext) -> PathBuf {
111 context
112 .workspace_dir
113 .join("target")
114 .join("bern")
115 .join("dist")
116}
117
118pub fn package_architecture() -> &'static str {
121 match (std::env::consts::OS, std::env::consts::ARCH) {
122 (_, "aarch64") => "arm64",
123 ("macos", _) => "x86_64",
124 _ => "x64",
125 }
126}
127
128pub fn archive(dir: &std::path::Path, stem: &std::path::Path) -> (BuildStep, PathBuf) {
132 let parent = dir.parent().unwrap_or(dir).to_path_buf();
133 let name = dir
134 .file_name()
135 .map(|name| name.to_string_lossy().into_owned())
136 .unwrap_or_default();
137 let with = |extension: &str| {
138 let mut file = stem.as_os_str().to_owned();
139 file.push(extension);
140 PathBuf::from(file)
141 };
142 let (program, args, out) = match std::env::consts::OS {
143 "macos" => {
144 let out = with(".zip");
145 let args = vec![
146 "-c".to_string(),
147 "-k".into(),
148 "--norsrc".into(),
151 "--noextattr".into(),
152 "--noqtn".into(),
153 "--keepParent".into(),
154 dir.display().to_string(),
155 out.display().to_string(),
156 ];
157 ("ditto", args, out)
158 }
159 "windows" => {
160 let out = with(".zip");
161 let args = vec![
162 "-a".to_string(),
163 "-c".into(),
164 "-f".into(),
165 out.display().to_string(),
166 "-C".into(),
167 parent.display().to_string(),
168 name.clone(),
169 ];
170 ("tar", args, out)
171 }
172 _ => {
173 let out = with(".tar.gz");
174 let args = vec![
175 "-czf".to_string(),
176 out.display().to_string(),
177 "-C".into(),
178 parent.display().to_string(),
179 name.clone(),
180 ];
181 ("tar", args, out)
182 }
183 };
184 let step = BuildStep::Run {
185 description: format!("Archive {name}"),
186 program: program.into(),
187 args,
188 cwd: parent,
189 env: vec![],
190 };
191 (step, out)
192}
193
194pub fn files_below(dir: &std::path::Path) -> std::io::Result<Vec<(PathBuf, PathBuf)>> {
196 fn walk(
197 dir: &std::path::Path,
198 relative: &std::path::Path,
199 out: &mut Vec<(PathBuf, PathBuf)>,
200 ) -> std::io::Result<()> {
201 for entry in std::fs::read_dir(dir.join(relative))? {
202 let entry = entry?;
203 let path = relative.join(entry.file_name());
204 if entry.file_type()?.is_dir() {
205 walk(dir, &path, out)?;
206 } else {
207 out.push((dir.join(&path), path));
208 }
209 }
210 Ok(())
211 }
212 let mut out = Vec::new();
213 walk(dir, std::path::Path::new(""), &mut out)?;
214 out.sort();
215 Ok(out)
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn archives_use_the_build_machine_s_own_tools() {
224 let (step, out) = archive(
225 std::path::Path::new("/stage/Hello"),
226 std::path::Path::new("/dist/Hello-1.0"),
227 );
228 let BuildStep::Run { program, args, .. } = step else {
229 panic!("a command");
230 };
231 match std::env::consts::OS {
232 "macos" => {
233 assert_eq!(
234 (program.as_str(), out),
235 ("ditto", PathBuf::from("/dist/Hello-1.0.zip"))
236 );
237 assert!(args.contains(&"--noextattr".to_string()));
238 }
239 "windows" => assert_eq!(program, "tar"),
240 _ => assert_eq!(
241 (program.as_str(), out),
242 ("tar", PathBuf::from("/dist/Hello-1.0.tar.gz"))
243 ),
244 }
245 }
246}