1use flowey::node::prelude::*;
7
8use crate::build_openhcl_boot::OpenhclBootOutput;
9use crate::build_openhcl_igvm_from_recipe::IgvmManifestPath;
10use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
11use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeDetails;
12use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipeDetailsLocalOnly;
13use crate::build_openhcl_igvm_from_recipe::OpenhclKernelPackage;
14use crate::build_openhcl_igvm_from_recipe::Vtl0KernelType;
15use crate::build_openhcl_initrd::OpenhclInitrdExtraParams;
16use crate::build_openvmm_hcl::OpenvmmHclBuildProfile;
17use crate::build_openvmm_hcl::OpenvmmHclFeature;
18use crate::build_openvmm_hcl::OpenvmmHclOutput;
19use crate::run_cargo_build::common::CommonArch;
20use crate::run_cargo_build::common::CommonTriple;
21use crate::run_igvmfilegen::IgvmOutput;
22
23#[derive(Default, Serialize, Deserialize, PartialEq, Eq)]
24pub struct Customizations {
25 pub build_label: Option<String>,
26 pub custom_directory: Vec<PathBuf>,
27 pub custom_kernel_modules: Option<PathBuf>,
28 pub custom_kernel: Option<PathBuf>,
29 pub custom_layer: Vec<PathBuf>,
30 pub custom_openhcl_boot: Option<PathBuf>,
31 pub custom_openvmm_hcl: Option<PathBuf>,
32 pub custom_sidecar: Option<PathBuf>,
33 pub custom_uefi: Option<PathBuf>,
34 pub custom_vtl0_kernel: Option<PathBuf>,
35 pub custom_extra_rootfs: Vec<PathBuf>,
36 pub override_arch: Option<CommonArch>,
37 pub override_kernel_pkg: Option<OpenhclKernelPackage>,
38 pub override_manifest: Option<PathBuf>,
39 pub override_openvmm_hcl_feature: Vec<String>,
40 pub with_debuginfo: bool,
41 pub with_perf_tools: bool,
42 pub with_sidecar: bool,
43}
44
45flowey_request! {
46 pub struct Params {
47 pub artifact_dir: ReadVar<PathBuf>,
48 pub done: WriteVar<SideEffect>,
49
50 pub base_recipe: OpenhclIgvmRecipe,
51 pub release: bool,
52
53 pub customizations: Customizations,
54 }
55}
56
57new_simple_flow_node!(struct Node);
58
59impl SimpleFlowNode for Node {
60 type Request = Params;
61
62 fn imports(ctx: &mut ImportCtx<'_>) {
63 ctx.import::<crate::build_openhcl_igvm_from_recipe::Node>();
64 }
65
66 fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
67 let Params {
68 artifact_dir,
69 done,
70
71 base_recipe,
72 release,
73
74 customizations,
75 } = request;
76
77 let has_customizations = customizations != Customizations::default();
78
79 let Customizations {
80 build_label,
81 custom_directory,
82 custom_kernel_modules,
83 custom_kernel,
84 custom_layer,
85 override_manifest,
86 custom_openhcl_boot,
87 custom_openvmm_hcl,
88 custom_sidecar,
89 custom_uefi,
90 custom_vtl0_kernel,
91 override_arch,
92 override_kernel_pkg,
93 override_openvmm_hcl_feature,
94 with_debuginfo,
95 with_perf_tools,
96 with_sidecar,
97 custom_extra_rootfs,
98 } = customizations;
99
100 let profile = if release {
101 OpenvmmHclBuildProfile::OpenvmmHclShip
102 } else {
103 OpenvmmHclBuildProfile::Debug
104 };
105
106 let mut recipe_details = base_recipe.recipe_details(profile);
107 {
108 let OpenhclIgvmRecipeDetails {
109 local_only,
110 igvm_manifest,
111 openhcl_kernel_package,
112 openvmm_hcl_features,
113 target,
114 vtl0_kernel_type,
115 with_uefi,
116 with_interactive,
117 with_sidecar: with_sidecar_details,
118 } = &mut recipe_details;
119
120 if custom_kernel.is_some() {
121 *with_uefi = true
122 }
123
124 if with_sidecar || custom_sidecar.is_some() {
125 *with_sidecar_details = true;
126 }
127
128 *with_interactive = matches!(profile, OpenvmmHclBuildProfile::Debug) || with_perf_tools;
130
131 assert!(local_only.is_none());
132 *local_only = Some(OpenhclIgvmRecipeDetailsLocalOnly {
133 openvmm_hcl_no_strip: with_perf_tools || with_debuginfo,
136 openhcl_initrd_extra_params: Some(OpenhclInitrdExtraParams {
137 extra_initrd_layers: custom_layer
138 .into_iter()
139 .map(|p| p.absolute())
140 .collect::<Result<_, _>>()?,
141 extra_initrd_directories: custom_directory
142 .into_iter()
143 .map(|p| p.absolute())
144 .collect::<Result<_, _>>()?,
145 custom_kernel_modules,
146 }),
147 custom_openvmm_hcl: custom_openvmm_hcl.map(|p| p.absolute()).transpose()?,
148 custom_openhcl_boot: custom_openhcl_boot.map(|p| p.absolute()).transpose()?,
149 custom_uefi: custom_uefi.map(|p| p.absolute()).transpose()?,
150 custom_kernel: custom_kernel.map(|p| p.absolute()).transpose()?,
151 custom_sidecar: custom_sidecar.map(|p| p.absolute()).transpose()?,
152 custom_extra_rootfs: custom_extra_rootfs
153 .into_iter()
154 .map(|p| p.absolute())
155 .collect::<Result<_, _>>()?,
156 });
157
158 if let Some(p) = override_manifest {
159 *igvm_manifest = IgvmManifestPath::LocalOnlyCustom(p.absolute()?);
160 }
161
162 if let Some(override_kernel_pkg) = override_kernel_pkg {
163 *openhcl_kernel_package = override_kernel_pkg;
164 }
165
166 if !override_openvmm_hcl_feature.is_empty() {
167 *openvmm_hcl_features = override_openvmm_hcl_feature
168 .into_iter()
169 .map(OpenvmmHclFeature::LocalOnlyCustom)
170 .collect()
171 }
172
173 if let Some(arch) = override_arch {
174 *target = match arch {
175 CommonArch::X86_64 => CommonTriple::X86_64_LINUX_MUSL,
176 CommonArch::Aarch64 => CommonTriple::AARCH64_LINUX_MUSL,
177 };
178 }
179
180 if let Some(p) = custom_vtl0_kernel {
181 *vtl0_kernel_type = Some(Vtl0KernelType::LocalOnlyCustom(p.absolute()?))
182 }
183 }
184
185 let build_label = if let Some(label) = build_label {
186 label
187 } else {
188 let base = match &recipe_details.igvm_manifest {
189 IgvmManifestPath::InTree(_) => {
190 non_production_build_igvm_tool_out_name(&base_recipe).to_string()
191 }
192 IgvmManifestPath::LocalOnlyCustom(path) => path
193 .file_name()
194 .unwrap()
195 .to_str()
196 .unwrap()
197 .strip_suffix(".json")
198 .unwrap()
199 .to_string(),
200 };
201
202 if has_customizations {
203 format!("{base}-custom")
204 } else {
205 base
206 }
207 };
208
209 let (built_openvmm_hcl, write_built_openvmm_hcl) = ctx.new_var();
210 let (built_openhcl_boot, write_built_openhcl_boot) = ctx.new_var();
211 let (built_openhcl_igvm, write_built_openhcl_igvm) = ctx.new_var();
212 let (built_sidecar, write_built_sidecar) = ctx.new_var();
213
214 ctx.req(crate::build_openhcl_igvm_from_recipe::Request {
215 profile,
216 recipe: OpenhclIgvmRecipe::LocalOnlyCustom(recipe_details.clone()),
217 custom_target: None,
218 built_openvmm_hcl: write_built_openvmm_hcl,
219 built_openhcl_boot: write_built_openhcl_boot,
220 built_openhcl_igvm: write_built_openhcl_igvm,
221 built_sidecar: write_built_sidecar,
222 });
223
224 ctx.emit_rust_step("copy to output directory", |ctx| {
225 done.claim(ctx);
226 let artifact_dir = artifact_dir.claim(ctx);
227 let built_openvmm_hcl = built_openvmm_hcl.claim(ctx);
228 let built_openhcl_boot = built_openhcl_boot.claim(ctx);
229 let built_openhcl_igvm = built_openhcl_igvm.claim(ctx);
230 let built_sidecar = built_sidecar.claim(ctx);
231 move |rt| {
232 let output_dir = rt
233 .read(artifact_dir)
234 .join(match profile {
235 OpenvmmHclBuildProfile::Debug => "debug",
236 OpenvmmHclBuildProfile::Release => "release",
237 OpenvmmHclBuildProfile::OpenvmmHclShip => "ship",
238 })
239 .join(&build_label);
240 fs_err::create_dir_all(&output_dir)?;
241
242 let OpenvmmHclOutput { bin, dbg } = rt.read(built_openvmm_hcl);
243 fs_err::copy(bin, output_dir.join("openvmm_hcl"))?;
244 if let Some(dbg) = dbg {
245 fs_err::copy(dbg, output_dir.join("openvmm_hcl.dbg"))?;
246 }
247
248 let OpenhclBootOutput { bin, dbg } = rt.read(built_openhcl_boot);
249 fs_err::copy(bin, output_dir.join("openhcl_boot"))?;
250 fs_err::copy(dbg, output_dir.join("openhcl_boot.dbg"))?;
251
252 if let Some(built_sidecar) = rt.read(built_sidecar) {
253 let crate::build_sidecar::SidecarOutput { bin, dbg } = built_sidecar;
254 fs_err::copy(bin, output_dir.join("sidecar"))?;
255 fs_err::copy(dbg, output_dir.join("sidecar.dbg"))?;
256 }
257
258 let IgvmOutput {
259 igvm_bin,
260 igvm_map,
261 igvm_tdx_json,
262 igvm_snp_json,
263 igvm_vbs_json,
264 } = rt.read(built_openhcl_igvm);
265 fs_err::copy(
266 igvm_bin,
267 output_dir.join(format!("openhcl-{build_label}.bin")),
268 )?;
269 if let Some(igvm_map) = igvm_map {
270 fs_err::copy(
271 igvm_map,
272 output_dir.join(format!("openhcl-{build_label}.bin.map")),
273 )?;
274 }
275 if let Some(igvm_tdx_json) = igvm_tdx_json {
276 fs_err::copy(igvm_tdx_json, output_dir.join("openhcl-tdx.json"))?;
277 }
278 if let Some(igvm_snp_json) = igvm_snp_json {
279 fs_err::copy(igvm_snp_json, output_dir.join("openhcl-snp.json"))?;
280 }
281 if let Some(igvm_vbs_json) = igvm_vbs_json {
282 fs_err::copy(igvm_vbs_json, output_dir.join("openhcl-vbs.json"))?;
283 }
284 for e in fs_err::read_dir(output_dir)? {
285 let e = e?;
286 log::info!("{}", e.path().display());
287 }
288
289 Ok(())
290 }
291 });
292
293 Ok(())
294 }
295}
296
297pub fn non_production_build_igvm_tool_out_name(recipe: &OpenhclIgvmRecipe) -> &'static str {
298 match recipe {
299 OpenhclIgvmRecipe::X64 => "x64",
300 OpenhclIgvmRecipe::X64Devkern => "x64-devkern",
301 OpenhclIgvmRecipe::X64TestLinuxDirect => "x64-test-linux-direct",
302 OpenhclIgvmRecipe::X64TestLinuxDirectDevkern => "x64-test-linux-direct-devkern",
303 OpenhclIgvmRecipe::X64Cvm => "x64-cvm",
304 OpenhclIgvmRecipe::X64CvmDevkern => "x64-cvm-devkern",
305 OpenhclIgvmRecipe::Aarch64 => "aarch64",
306 OpenhclIgvmRecipe::Aarch64Devkern => "aarch64-devkern",
307 OpenhclIgvmRecipe::LocalOnlyCustom(_) => unreachable!(),
308 }
309}