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 ssl_pkg = match ctx.platform() {
60            FlowPlatform::Linux(
61                FlowPlatformLinuxDistro::Fedora | FlowPlatformLinuxDistro::AzureLinux,
62            ) => "openssl-devel",
63            _ => "libssl-dev",
64        };
65        let pre_build_deps =
66            [
67                ctx.reqv(|v| flowey_lib_common::install_dist_pkg::Request::Install {
68                    package_names: vec![ssl_pkg.into()],
69                    done: v,
70                }),
71            ]
72            .to_vec();
73
74        let output = ctx.reqv(|v| crate::run_cargo_build::Request {
75            crate_name: "vmgs_lib".into(),
76            out_name: "vmgs_lib".into(),
77            crate_type: CargoCrateType::DynamicLib,
78            profile: profile.into(),
79            features: Default::default(),
80            target: target.as_triple(),
81            no_split_dbg_info: false,
82            extra_env: None,
83            pre_build_deps,
84            output: v,
85        });
86
87        let built_vmgs_lib = ctx.emit_minor_rust_stepv("check built vmgs_lib", |ctx| {
88            let output = output.claim(ctx);
89            move |rt| match rt.read(output) {
90                CargoBuildOutput::LinuxDynamicLib { so } => VmgsLibOutput::LinuxDynamicLib { so },
91                CargoBuildOutput::WindowsDynamicLib { dll, dll_lib, pdb } => {
92                    VmgsLibOutput::WindowsDynamicLib { dll, dll_lib, pdb }
93                }
94                _ => unreachable!(),
95            }
96        });
97
98        // given how simple the test is for vmgs_lib, it's fine to just
99        // do it "inline" with the compilation
100        //
101        // if we ever decide to do more involved testing for this lib,
102        // this should get split out into a separate step
103
104        // TODO: figure out how to test vmgs_lib on other architectures.
105        // Currently x86 only
106        let did_test = if matches!(
107            &target.as_triple().architecture,
108            target_lexicon::Architecture::X86_64
109        ) && matches!(ctx.arch(), FlowArch::X86_64)
110        {
111            let clang_installed =
112                ctx.reqv(|v| flowey_lib_common::install_dist_pkg::Request::Install {
113                    package_names: vec!["clang".into()],
114                    done: v,
115                });
116
117            let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
118
119            if matches!(ctx.platform(), FlowPlatform::Linux(_)) {
120                ctx.emit_rust_step("test vmgs_lib", |ctx| {
121                    clang_installed.claim(ctx);
122
123                    let built_vmgs_lib = built_vmgs_lib.clone().claim(ctx);
124                    let openvmm_repo_path = openvmm_repo_path.claim(ctx);
125
126                    move |rt| {
127                        let VmgsLibOutput::LinuxDynamicLib { so } = rt.read(built_vmgs_lib) else {
128                            unreachable!()
129                        };
130
131                        let so_dir = so.parent().unwrap();
132
133                        let openvmm_repo_path = rt.read(openvmm_repo_path);
134
135                        let vmgs_testlib_c =
136                            openvmm_repo_path.join("vm/vmgs/vmgs_lib/examples/vmgs_testlib.c");
137
138                        if which::which("clang").is_ok() {
139                            flowey::shell_cmd!(
140                                rt,
141                                "clang {vmgs_testlib_c} {so} -rpath {so_dir} -o ./vmgs_lib_test"
142                            )
143                            .run()?;
144                            flowey::shell_cmd!(rt, "./vmgs_lib_test").run()?;
145                        } else {
146                            log::warn!("skipping vmgs_lib test (could not find clang)");
147                        }
148
149                        Ok(())
150                    }
151                })
152            } else if matches!(ctx.platform(), FlowPlatform::Windows) {
153                // HACK: clang-on-bash-on-windows-on-ADO is... wild. This
154                // works, but it's undoubtedly suboptimal, and someone who
155                // actually _understands_ how clang is set up in this
156                // context could do a wildly better job here.
157                ctx.emit_rust_step("test vmgs_lib", |ctx| {
158                    clang_installed.claim(ctx);
159
160                    let built_vmgs_lib = built_vmgs_lib.clone().claim(ctx);
161                    let openvmm_repo_path = openvmm_repo_path.claim(ctx);
162
163                    move |rt| {
164                        // TODO: figure out how to test cross-compile windows in wsl2
165                        if flowey_lib_common::_util::running_in_wsl(rt)
166                            && matches!(
167                                &target.as_triple().operating_system,
168                                target_lexicon::OperatingSystem::Windows
169                            )
170                        {
171                            log::warn!("unimplemented: skip testing windows vmgs_lib via WSL2");
172                            return Ok(());
173                        }
174
175                        let openvmm_repo_path = rt.read(openvmm_repo_path);
176
177                        let VmgsLibOutput::WindowsDynamicLib {
178                            dll,
179                            dll_lib,
180                            pdb: _,
181                        } = rt.read(built_vmgs_lib)
182                        else {
183                            unreachable!()
184                        };
185
186                        if which::which("clang").is_err() {
187                            log::warn!("skipping vmgs_lib test (could not find clang)");
188                            return Ok(());
189                        }
190
191                        let vmgs_testlib_c =
192                            openvmm_repo_path.join("vm/vmgs/vmgs_lib/examples/vmgs_testlib.c");
193
194                        // make sure to copy the dll import lib over as well!
195                        fs_err::copy(dll_lib, "vmgs_lib.dll.lib")?;
196                        fs_err::copy(dll, "vmgs_lib.dll")?;
197
198                        flowey::shell_cmd!(
199                            rt,
200                            "clang {vmgs_testlib_c} -o vmgs_lib_test.exe -l vmgs_lib.dll"
201                        )
202                        .run()?;
203                        flowey::shell_cmd!(rt, "./vmgs_lib_test.exe").run()?;
204
205                        Ok(())
206                    }
207                })
208            } else {
209                anyhow::bail!("unsupported platform")
210            }
211        } else {
212            ReadVar::from_static(()).into_side_effect()
213        };
214
215        ctx.emit_minor_rust_step("report built vmgs_lib", |ctx| {
216            did_test.claim(ctx);
217            let built_vmgs_lib = built_vmgs_lib.claim(ctx);
218            let vmgs_lib = vmgs_lib.claim(ctx);
219            move |rt| {
220                let built_vmgs_lib = rt.read(built_vmgs_lib);
221                rt.write(vmgs_lib, &built_vmgs_lib);
222            }
223        });
224
225        Ok(())
226    }
227}