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