flowey_lib_hvlite/
build_and_test_vmgs_lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Builds and tests `vmgs_lib` library.
5//!
6//! Tests are windows only at the moment.
7
8use crate::run_cargo_build::CargoBuildOutput;
9use crate::run_cargo_build::common::CommonProfile;
10use crate::run_cargo_build::common::CommonTriple;
11use flowey::node::prelude::*;
12use flowey_lib_common::run_cargo_build::CargoCrateType;
13
14#[derive(Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum VmgsLibOutput {
17    LinuxDynamicLib {
18        #[serde(rename = "libvmgs_lib.so")]
19        so: PathBuf,
20    },
21    WindowsDynamicLib {
22        #[serde(rename = "vmgs_lib.dll")]
23        dll: PathBuf,
24        #[serde(rename = "vmgs_lib.dll.lib")]
25        dll_lib: PathBuf,
26        #[serde(rename = "vmgs_lib.pdb")]
27        pdb: PathBuf,
28    },
29}
30
31impl Artifact for VmgsLibOutput {}
32
33flowey_request! {
34    pub struct Request {
35        pub target: CommonTriple,
36        pub profile: CommonProfile,
37        pub vmgs_lib: WriteVar<VmgsLibOutput>,
38    }
39}
40
41new_simple_flow_node!(struct Node);
42
43impl SimpleFlowNode for Node {
44    type Request = Request;
45
46    fn imports(ctx: &mut ImportCtx<'_>) {
47        ctx.import::<crate::run_cargo_build::Node>();
48        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
49        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
50    }
51
52    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
53        let Request {
54            target,
55            profile,
56            vmgs_lib,
57        } = request;
58
59        let pre_build_deps =
60            [
61                ctx.reqv(|v| flowey_lib_common::install_dist_pkg::Request::Install {
62                    package_names: vec!["libssl-dev".into()],
63                    done: v,
64                }),
65            ]
66            .to_vec();
67
68        let output = ctx.reqv(|v| crate::run_cargo_build::Request {
69            crate_name: "vmgs_lib".into(),
70            out_name: "vmgs_lib".into(),
71            crate_type: CargoCrateType::DynamicLib,
72            profile: profile.into(),
73            features: [].into(),
74            target: target.as_triple(),
75            no_split_dbg_info: false,
76            extra_env: None,
77            pre_build_deps,
78            output: v,
79        });
80
81        let built_vmgs_lib = ctx.emit_minor_rust_stepv("check built vmgs_lib", |ctx| {
82            let output = output.claim(ctx);
83            move |rt| match rt.read(output) {
84                CargoBuildOutput::LinuxDynamicLib { so } => VmgsLibOutput::LinuxDynamicLib { so },
85                CargoBuildOutput::WindowsDynamicLib { dll, dll_lib, pdb } => {
86                    VmgsLibOutput::WindowsDynamicLib { dll, dll_lib, pdb }
87                }
88                _ => unreachable!(),
89            }
90        });
91
92        // given how simple the test is for vmgs_lib, it's fine to just
93        // do it "inline" with the compilation
94        //
95        // if we ever decide to do more involved testing for this lib,
96        // this should get split out into a separate step
97
98        // TODO: figure out how to test vmgs_lib on other architectures.
99        // Currently x86 only
100        let did_test = if matches!(
101            &target.as_triple().architecture,
102            target_lexicon::Architecture::X86_64
103        ) && matches!(ctx.arch(), FlowArch::X86_64)
104        {
105            let clang_installed =
106                ctx.reqv(|v| flowey_lib_common::install_dist_pkg::Request::Install {
107                    package_names: vec!["clang".into()],
108                    done: v,
109                });
110
111            let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
112
113            if matches!(ctx.platform(), FlowPlatform::Linux(_)) {
114                ctx.emit_rust_step("test vmgs_lib", |ctx| {
115                    clang_installed.claim(ctx);
116
117                    let built_vmgs_lib = built_vmgs_lib.clone().claim(ctx);
118                    let openvmm_repo_path = openvmm_repo_path.claim(ctx);
119
120                    move |rt| {
121                        let VmgsLibOutput::LinuxDynamicLib { so } = rt.read(built_vmgs_lib) else {
122                            unreachable!()
123                        };
124
125                        let so_dir = so.parent().unwrap();
126
127                        let openvmm_repo_path = rt.read(openvmm_repo_path);
128
129                        let vmgs_testlib_c =
130                            openvmm_repo_path.join("vm/vmgs/vmgs_lib/examples/vmgs_testlib.c");
131
132                        if which::which("clang").is_ok() {
133                            let sh = xshell::Shell::new()?;
134                            xshell::cmd!(
135                                sh,
136                                "clang {vmgs_testlib_c} {so} -rpath {so_dir} -o ./vmgs_lib_test"
137                            )
138                            .run()?;
139                            xshell::cmd!(sh, "./vmgs_lib_test").run()?;
140                        } else {
141                            log::warn!("skipping vmgs_lib test (could not find clang)");
142                        }
143
144                        Ok(())
145                    }
146                })
147            } else if matches!(ctx.platform(), FlowPlatform::Windows) {
148                // HACK: clang-on-bash-on-windows-on-ADO is... wild. This
149                // works, but it's undoubtedly suboptimal, and someone who
150                // actually _understands_ how clang is set up in this
151                // context could do a wildly better job here.
152                ctx.emit_rust_step("test vmgs_lib", |ctx| {
153                    clang_installed.claim(ctx);
154
155                    let built_vmgs_lib = built_vmgs_lib.clone().claim(ctx);
156                    let openvmm_repo_path = openvmm_repo_path.claim(ctx);
157
158                    move |rt| {
159                        // TODO: figure out how to test cross-compile windows in wsl2
160                        if flowey_lib_common::_util::running_in_wsl(rt)
161                            && matches!(
162                                &target.as_triple().operating_system,
163                                target_lexicon::OperatingSystem::Windows
164                            )
165                        {
166                            log::warn!("unimplemented: skip testing windows vmgs_lib via WSL2");
167                            return Ok(());
168                        }
169
170                        let openvmm_repo_path = rt.read(openvmm_repo_path);
171
172                        let VmgsLibOutput::WindowsDynamicLib {
173                            dll,
174                            dll_lib,
175                            pdb: _,
176                        } = rt.read(built_vmgs_lib)
177                        else {
178                            unreachable!()
179                        };
180
181                        if which::which("clang").is_err() {
182                            log::warn!("skipping vmgs_lib test (could not find clang)");
183                            return Ok(());
184                        }
185
186                        let vmgs_testlib_c =
187                            openvmm_repo_path.join("vm/vmgs/vmgs_lib/examples/vmgs_testlib.c");
188
189                        // make sure to copy the dll import lib over as well!
190                        fs_err::copy(dll_lib, "vmgs_lib.dll.lib")?;
191                        fs_err::copy(dll, "vmgs_lib.dll")?;
192
193                        let sh = xshell::Shell::new()?;
194                        xshell::cmd!(
195                            sh,
196                            "clang {vmgs_testlib_c} -o vmgs_lib_test.exe -l vmgs_lib.dll"
197                        )
198                        .run()?;
199                        xshell::cmd!(sh, "./vmgs_lib_test.exe").run()?;
200
201                        Ok(())
202                    }
203                })
204            } else {
205                anyhow::bail!("unsupported platform")
206            }
207        } else {
208            ReadVar::from_static(()).into_side_effect()
209        };
210
211        ctx.emit_minor_rust_step("report built vmgs_lib", |ctx| {
212            did_test.claim(ctx);
213            let built_vmgs_lib = built_vmgs_lib.claim(ctx);
214            let vmgs_lib = vmgs_lib.claim(ctx);
215            move |rt| {
216                let built_vmgs_lib = rt.read(built_vmgs_lib);
217                rt.write(vmgs_lib, &built_vmgs_lib);
218            }
219        });
220
221        Ok(())
222    }
223}