Skip to main content

flowey_lib_hvlite/_jobs/
check_clippy.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Ensure the OpenVMM repo is `clippy` clean.
5
6use crate::common::CommonArch;
7use crate::common::CommonProfile;
8use crate::common::CommonTriple;
9use flowey::node::prelude::*;
10use flowey_lib_common::run_cargo_build::CargoBuildProfile;
11use flowey_lib_common::run_cargo_build::CargoFeatureSet;
12use flowey_lib_common::run_cargo_clippy::CargoPackage;
13
14flowey_request! {
15    pub struct Request {
16        pub target: target_lexicon::Triple,
17        pub profile: CommonProfile,
18        pub done: WriteVar<SideEffect>,
19        pub also_check_misc_nostd_crates: bool,
20    }
21}
22
23new_simple_flow_node!(struct Node);
24
25impl SimpleFlowNode for Node {
26    type Request = Request;
27
28    fn imports(ctx: &mut ImportCtx<'_>) {
29        ctx.import::<crate::build_xtask::Node>();
30        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
31        ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
32        ctx.import::<crate::install_openvmm_rust_build_essential::Node>();
33        ctx.import::<crate::init_cross_build::Node>();
34        ctx.import::<flowey_lib_common::install_rust::Node>();
35        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
36        ctx.import::<flowey_lib_common::run_cargo_clippy::Node>();
37    }
38
39    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
40        let Request {
41            target,
42            profile,
43            done,
44            also_check_misc_nostd_crates,
45        } = request;
46
47        let flowey_platform = ctx.platform();
48        let flowey_arch = ctx.arch();
49
50        let sysroot_arch = CommonArch::from_architecture(target.architecture)?;
51        let (boot_target, uefi_target) = match sysroot_arch {
52            CommonArch::X86_64 => ("x86_64-unknown-none", "x86_64-unknown-uefi"),
53            CommonArch::Aarch64 => ("aarch64-unknown-linux-musl", "aarch64-unknown-uefi"),
54        };
55
56        let mut pre_build_deps = Vec::new();
57
58        // FIXME: this will go away once we have a dedicated cargo .config.toml
59        // for the openhcl _bin_. until we have that, we are building _every_
60        // musl target using the openhcl toolchain...
61
62        if matches!(target.environment, target_lexicon::Environment::Musl) {
63            pre_build_deps.push(
64                ctx.reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request {
65                    arch: sysroot_arch,
66                    path: v,
67                })
68                .into_side_effect(),
69            );
70        }
71
72        ctx.req(flowey_lib_common::install_rust::Request::InstallTargetTriple(target.clone()));
73        if also_check_misc_nostd_crates {
74            ctx.req(
75                flowey_lib_common::install_rust::Request::InstallTargetTriple(
76                    target_lexicon::triple!(uefi_target),
77                ),
78            );
79            ctx.req(
80                flowey_lib_common::install_rust::Request::InstallTargetTriple(
81                    target_lexicon::triple!(boot_target),
82                ),
83            );
84        }
85
86        // TODO: install build tools for other platforms
87        if matches!(
88            ctx.platform(),
89            FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu)
90        ) {
91            pre_build_deps.push(ctx.reqv(|v| {
92                flowey_lib_common::install_dist_pkg::Request::Install {
93                    package_names: vec![
94                        "libssl-dev".into(),
95                        "pkg-config".into(),
96                        "build-essential".into(),
97                    ],
98                    done: v,
99                }
100            }));
101        }
102
103        pre_build_deps.push(ctx.reqv(crate::install_openvmm_rust_build_essential::Request));
104
105        // Cross compiling for MacOS isn't supported, but clippy still works
106        // with no additional dependencies
107        if !matches!(
108            target.operating_system,
109            target_lexicon::OperatingSystem::Darwin(_)
110        ) {
111            pre_build_deps.push(
112                ctx.reqv(|v| crate::init_cross_build::Request {
113                    target: target.clone(),
114                    injected_env: v,
115                })
116                .into_side_effect(),
117            );
118        }
119
120        let xtask_target = CommonTriple::Common {
121            arch: flowey_arch.try_into()?,
122            platform: flowey_platform.try_into()?,
123        };
124
125        let xtask = ctx.reqv(|v| crate::build_xtask::Request {
126            target: xtask_target,
127            xtask: v,
128        });
129
130        let profile = match profile {
131            CommonProfile::Release => CargoBuildProfile::Release,
132            CommonProfile::Debug => CargoBuildProfile::Debug,
133        };
134
135        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
136
137        let exclude = ctx.emit_rust_stepv("determine clippy exclusions", |ctx| {
138            let xtask = xtask.claim(ctx);
139            let repo_path = openvmm_repo_path.clone().claim(ctx);
140            move |rt| {
141                let xtask = rt.read(xtask);
142                let repo_path = rt.read(repo_path);
143
144                // guest_test_uefi is uefi-only, and is handled separately below
145                let mut exclude = vec!["guest_test_uefi".into()];
146
147                // packages depending on libfuzzer-sys are currently x86 only
148                if !(matches!(target.architecture, target_lexicon::Architecture::X86_64)
149                    && matches!(flowey_arch, FlowArch::X86_64))
150                {
151                    let xtask_bin = match xtask {
152                        crate::build_xtask::XtaskOutput::LinuxBin { bin, dbg: _ } => bin,
153                        crate::build_xtask::XtaskOutput::WindowsBin { exe, pdb: _ } => exe,
154                    };
155
156                    rt.sh.change_dir(repo_path);
157                    let output =
158                        flowey::shell_cmd!(rt, "{xtask_bin} fuzz list --crates").output()?;
159                    let output = String::from_utf8(output.stdout)?;
160
161                    let fuzz_crates = output.trim().split('\n').map(|s| s.to_owned());
162                    exclude.extend(fuzz_crates);
163                }
164
165                // packages requiring crypto or openssl support won't cross compile for macos
166                if matches!(
167                    target.operating_system,
168                    target_lexicon::OperatingSystem::Darwin(_)
169                ) {
170                    exclude.extend(
171                        ["openssl_kdf", "vmgs_lib", "disk_crypt", "igvmfilegen"].map(|x| x.into()),
172                    );
173                }
174
175                Ok(Some(exclude))
176            }
177        });
178
179        // On Windows & Mac we can't build with all features since the TPM
180        // requires OpenSSL for crypto, which isn't supported in CI on those
181        // platforms today.
182        //
183        // We don't add the CI feature here, as it's used purely to exclude
184        // tests that can't run in CI. We still want those tests to be linted.
185        let features = if matches!(
186            target.operating_system,
187            target_lexicon::OperatingSystem::Windows | target_lexicon::OperatingSystem::Darwin(_)
188        ) {
189            CargoFeatureSet::None
190        } else {
191            CargoFeatureSet::All
192        };
193
194        let mut reqs = vec![ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
195            in_folder: openvmm_repo_path.clone(),
196            package: CargoPackage::Workspace,
197            profile: profile.clone(),
198            features: features.clone(),
199            target: target.clone(),
200            extra_env: None,
201            exclude,
202            keep_going: true,
203            all_targets: true,
204            pre_build_deps: pre_build_deps.clone(),
205            done: v,
206        })];
207
208        // crypto has non-additive features, we need to ensure full coverage of different backends.
209        // Always test the 'native' backends.
210        reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
211            in_folder: openvmm_repo_path.clone(),
212            package: CargoPackage::Crate("crypto".into()),
213            profile: profile.clone(),
214            features: CargoFeatureSet::Specific(vec!["native".into()]),
215            target: target.clone(),
216            extra_env: None,
217            exclude: ReadVar::from_static(None),
218            keep_going: true,
219            all_targets: true,
220            pre_build_deps: pre_build_deps.clone(),
221            done: v,
222        }));
223
224        // Always test the pure rust backend.
225        reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
226            in_folder: openvmm_repo_path.clone(),
227            package: CargoPackage::Crate("crypto".into()),
228            profile: profile.clone(),
229            features: CargoFeatureSet::Specific(vec!["rust".into()]),
230            target: target.clone(),
231            extra_env: None,
232            exclude: ReadVar::from_static(None),
233            keep_going: true,
234            all_targets: true,
235            pre_build_deps: pre_build_deps.clone(),
236            done: v,
237        }));
238
239        // Then on linux test the openssl & symcrypt backends, and ensure that --all-features works properly.
240        // We could test openssl on non-linux targets too, but setting up builds for them is a pain.
241        if matches!(
242            target.operating_system,
243            target_lexicon::OperatingSystem::Linux
244        ) {
245            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
246                in_folder: openvmm_repo_path.clone(),
247                package: CargoPackage::Crate("crypto".into()),
248                profile: profile.clone(),
249                features: CargoFeatureSet::Specific(vec!["openssl".into()]),
250                target: target.clone(),
251                extra_env: None,
252                exclude: ReadVar::from_static(None),
253                keep_going: true,
254                all_targets: true,
255                pre_build_deps: pre_build_deps.clone(),
256                done: v,
257            }));
258            // Only test the symcrypt backend on musl targets with our prebuilt lib
259            if matches!(target.environment, target_lexicon::Environment::Musl) {
260                reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
261                    in_folder: openvmm_repo_path.clone(),
262                    package: CargoPackage::Crate("crypto".into()),
263                    profile: profile.clone(),
264                    features: CargoFeatureSet::Specific(vec!["symcrypt".into()]),
265                    target: target.clone(),
266                    extra_env: None,
267                    exclude: ReadVar::from_static(None),
268                    keep_going: true,
269                    all_targets: true,
270                    pre_build_deps: pre_build_deps.clone(),
271                    done: v,
272                }));
273            }
274            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
275                in_folder: openvmm_repo_path.clone(),
276                package: CargoPackage::Crate("crypto".into()),
277                profile: profile.clone(),
278                features: CargoFeatureSet::All,
279                target: target.clone(),
280                extra_env: None,
281                exclude: ReadVar::from_static(None),
282                keep_going: true,
283                all_targets: true,
284                pre_build_deps: pre_build_deps.clone(),
285                done: v,
286            }));
287        }
288
289        if also_check_misc_nostd_crates {
290            // don't pass --all-targets, since that pulls in a std dependency
291            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
292                in_folder: openvmm_repo_path.clone(),
293                package: CargoPackage::Crate("openhcl_boot".into()),
294                profile: profile.clone(),
295                features: CargoFeatureSet::All,
296                target: target_lexicon::triple!(boot_target),
297                extra_env: Some(vec![("MINIMAL_RT_BUILD".into(), "1".into())]),
298                exclude: ReadVar::from_static(None),
299                keep_going: true,
300                all_targets: false,
301                pre_build_deps: pre_build_deps.clone(),
302                done: v,
303            }));
304
305            // don't pass --all-targets, since that pulls in a std dependency
306            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
307                in_folder: openvmm_repo_path.clone(),
308                package: CargoPackage::Crate("guest_test_uefi".into()),
309                profile: profile.clone(),
310                features: CargoFeatureSet::All,
311                target: target_lexicon::triple!(uefi_target),
312                extra_env: None,
313                exclude: ReadVar::from_static(None),
314                keep_going: true,
315                all_targets: false,
316                pre_build_deps: pre_build_deps.clone(),
317                done: v,
318            }));
319        }
320
321        ctx.emit_side_effect_step(reqs, [done]);
322
323        Ok(())
324    }
325}