flowey_lib_hvlite/_jobs/
check_openvmm_hcl_size.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Compares the size of the OpenHCL binary in the current PR with the size of the binary from the last successful merge to main.
5
6use crate::artifact_openhcl_igvm_from_recipe_extras;
7use crate::build_openhcl_igvm_from_recipe;
8use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
9use crate::build_openvmm_hcl;
10use crate::build_openvmm_hcl::OpenvmmHclBuildParams;
11use crate::build_openvmm_hcl::OpenvmmHclBuildProfile::OpenvmmHclShip;
12use crate::run_cargo_build::common::CommonArch;
13use crate::run_cargo_build::common::CommonPlatform;
14use crate::run_cargo_build::common::CommonTriple;
15use flowey::node::prelude::*;
16use flowey_lib_common::download_gh_artifact;
17use flowey_lib_common::gh_workflow_id;
18use flowey_lib_common::git_merge_commit;
19
20flowey_request! {
21    pub struct Request {
22        pub target: CommonTriple,
23        pub done: WriteVar<SideEffect>,
24        pub pipeline_name: String,
25        pub job_name: String,
26    }
27}
28
29new_simple_flow_node!(struct Node);
30
31impl SimpleFlowNode for Node {
32    type Request = Request;
33
34    fn imports(ctx: &mut ImportCtx<'_>) {
35        ctx.import::<crate::build_xtask::Node>();
36        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
37        ctx.import::<download_gh_artifact::Node>();
38        ctx.import::<git_merge_commit::Node>();
39        ctx.import::<gh_workflow_id::Node>();
40        ctx.import::<build_openhcl_igvm_from_recipe::Node>();
41        ctx.import::<build_openvmm_hcl::Node>();
42        ctx.import::<artifact_openhcl_igvm_from_recipe_extras::publish::Node>();
43    }
44
45    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
46        let Request {
47            target,
48            done,
49            pipeline_name,
50            job_name,
51        } = request;
52
53        let xtask_target = CommonTriple::Common {
54            arch: match ctx.arch() {
55                FlowArch::X86_64 => CommonArch::X86_64,
56                FlowArch::Aarch64 => CommonArch::Aarch64,
57                arch => anyhow::bail!("unsupported arch {arch}"),
58            },
59            platform: match ctx.platform() {
60                FlowPlatform::Windows => CommonPlatform::WindowsMsvc,
61                FlowPlatform::Linux(_) => CommonPlatform::LinuxGnu,
62                FlowPlatform::MacOs => CommonPlatform::MacOs,
63                platform => anyhow::bail!("unsupported platform {platform}"),
64            },
65        };
66
67        let xtask = ctx.reqv(|v| crate::build_xtask::Request {
68            target: xtask_target,
69            xtask: v,
70        });
71        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
72
73        let recipe = match target.common_arch().unwrap() {
74            CommonArch::X86_64 => OpenhclIgvmRecipe::X64,
75            CommonArch::Aarch64 => OpenhclIgvmRecipe::Aarch64,
76        }
77        .recipe_details(true);
78
79        let built_openvmm_hcl = ctx.reqv(|v| build_openvmm_hcl::Request {
80            build_params: OpenvmmHclBuildParams {
81                target: target.clone(),
82                profile: OpenvmmHclShip,
83                features: recipe.openvmm_hcl_features,
84                no_split_dbg_info: false,
85                max_trace_level: recipe.max_trace_level,
86            },
87            openvmm_hcl_output: v,
88        });
89
90        let file_name = match target.common_arch().unwrap() {
91            CommonArch::X86_64 => "x64-openhcl-baseline",
92            CommonArch::Aarch64 => "aarch64-openhcl-baseline",
93        };
94
95        let merge_commit = ctx.reqv(|v| git_merge_commit::Request {
96            repo_path: openvmm_repo_path.clone(),
97            merge_commit: v,
98            base_branch: "main".into(),
99        });
100
101        let merge_run = ctx.reqv(|v| {
102            gh_workflow_id::Request::WithStatusAndJob(gh_workflow_id::QueryWithStatusAndJob {
103                params: gh_workflow_id::WorkflowQueryParams {
104                    github_commit_hash: merge_commit,
105                    repo_path: openvmm_repo_path.clone(),
106                    pipeline_name,
107                    gh_workflow: v,
108                },
109                gh_run_status: gh_workflow_id::GhRunStatus::Completed,
110                gh_run_job_name: job_name,
111            })
112        });
113
114        let run_id = merge_run.map(ctx, |r| r.id);
115        let merge_head_artifact = ctx.reqv(|old_openhcl| download_gh_artifact::Request {
116            repo_owner: "microsoft".into(),
117            repo_name: "openvmm".into(),
118            file_name: file_name.into(),
119            path: old_openhcl,
120            run_id,
121        });
122
123        // Publish the built binary as an artifact for offline analysis.
124        //
125        // FUTURE: Flowey should have a general mechanism for this. We cannot
126        // use the existing artifact support because all artifacts are only
127        // published at the end of the job, if everything else succeeds.
128        let publish_artifact = if ctx.backend() == FlowBackend::Github {
129            let dir = ctx.emit_rust_stepv("collect openvmm_hcl files for analysis", |ctx| {
130                let built_openvmm_hcl = built_openvmm_hcl.clone().claim(ctx);
131                move |rt| {
132                    let built_openvmm_hcl = rt.read(built_openvmm_hcl);
133                    let path = Path::new("artifact");
134                    fs_err::create_dir_all(path)?;
135                    fs_err::copy(built_openvmm_hcl.bin, path.join("openvmm_hcl"))?;
136                    if let Some(dbg) = built_openvmm_hcl.dbg {
137                        fs_err::copy(dbg, path.join("openvmm_hcl.dbg"))?;
138                    }
139                    Ok(path
140                        .absolute()?
141                        .into_os_string()
142                        .into_string()
143                        .ok()
144                        .unwrap())
145                }
146            });
147            let name = format!(
148                "{}_openvmm_hcl_for_size_analysis",
149                target.common_arch().unwrap().as_arch()
150            );
151            Some(
152                ctx.emit_gh_step(
153                    "publish openvmm_hcl for analysis",
154                    "actions/upload-artifact@v4",
155                )
156                .with("name", name)
157                .with("path", dir)
158                .finish(ctx),
159            )
160        } else {
161            None
162        };
163
164        let comparison = ctx.emit_rust_step("binary size comparison", |ctx| {
165            // Ensure the artifact is published before the analysis since this step may fail.
166            let _publish_artifact = publish_artifact.claim(ctx);
167            let xtask = xtask.claim(ctx);
168            let openvmm_repo_path = openvmm_repo_path.claim(ctx);
169            let old_openhcl = merge_head_artifact.claim(ctx);
170            let new_openhcl = built_openvmm_hcl.claim(ctx);
171            let merge_run = merge_run.claim(ctx);
172
173            move |rt| {
174                let xtask = match rt.read(xtask) {
175                    crate::build_xtask::XtaskOutput::LinuxBin { bin, .. } => bin,
176                    crate::build_xtask::XtaskOutput::WindowsBin { exe, .. } => exe,
177                };
178
179                let old_openhcl = rt.read(old_openhcl);
180                let new_openhcl = rt.read(new_openhcl);
181                let merge_run = rt.read(merge_run);
182
183                let old_path = old_openhcl.join(file_name).join("openhcl");
184                let new_path = new_openhcl.bin;
185
186                println!(
187                    "comparing HEAD to merge commit {} and workflow {}",
188                    merge_run.commit, merge_run.id
189                );
190
191                let sh = xshell::Shell::new()?;
192                sh.change_dir(rt.read(openvmm_repo_path));
193                xshell::cmd!(
194                    sh,
195                    "{xtask} verify-size --original {old_path} --new {new_path}"
196                )
197                .run()?;
198
199                Ok(())
200            }
201        });
202
203        ctx.emit_side_effect_step(vec![comparison], [done]);
204
205        Ok(())
206    }
207}