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: CargoBuildProfile = profile.into();
131
132        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
133
134        let exclude = ctx.emit_rust_stepv("determine clippy exclusions", |ctx| {
135            let xtask = xtask.claim(ctx);
136            let repo_path = openvmm_repo_path.clone().claim(ctx);
137            move |rt| {
138                let xtask = rt.read(xtask);
139                let repo_path = rt.read(repo_path);
140
141                // These crates need to be checked for their UEFI targets below.
142                let mut exclude = vec!["guest_test_uefi".into(), "opentmk_executor".into()];
143
144                // packages depending on libfuzzer-sys are currently x86 only
145                if !(matches!(target.architecture, target_lexicon::Architecture::X86_64)
146                    && matches!(flowey_arch, FlowArch::X86_64))
147                {
148                    let xtask_bin = match xtask {
149                        crate::build_xtask::XtaskOutput::LinuxBin { bin, dbg: _ } => bin,
150                        crate::build_xtask::XtaskOutput::WindowsBin { exe, pdb: _ } => exe,
151                    };
152
153                    rt.sh.change_dir(repo_path);
154                    let output =
155                        flowey::shell_cmd!(rt, "{xtask_bin} fuzz list --crates").output()?;
156                    let output = String::from_utf8(output.stdout)?;
157
158                    let fuzz_crates = output.trim().split('\n').map(|s| s.to_owned());
159                    exclude.extend(fuzz_crates);
160                }
161
162                // packages requiring crypto or openssl support won't cross compile for macos
163                if matches!(
164                    target.operating_system,
165                    target_lexicon::OperatingSystem::Darwin(_)
166                ) {
167                    exclude.extend(
168                        ["openssl_kdf", "vmgs_lib", "disk_crypt", "igvmfilegen"].map(|x| x.into()),
169                    );
170                }
171
172                // vtpm_util unconditionally enables the TPM's OpenSSL backend.
173                if matches!(
174                    target.operating_system,
175                    target_lexicon::OperatingSystem::Windows
176                        | target_lexicon::OperatingSystem::Darwin(_)
177                ) {
178                    exclude.push("vtpm_util".into());
179                }
180
181                Ok(Some(exclude))
182            }
183        });
184
185        // On Windows & Mac we can't build with all features since the TPM
186        // requires OpenSSL for crypto, which isn't supported in CI on those
187        // platforms today.
188        //
189        // We don't add the CI feature here, as it's used purely to exclude
190        // tests that can't run in CI. We still want those tests to be linted.
191        let features = if matches!(
192            target.operating_system,
193            target_lexicon::OperatingSystem::Windows | target_lexicon::OperatingSystem::Darwin(_)
194        ) {
195            CargoFeatureSet::None
196        } else {
197            CargoFeatureSet::All
198        };
199
200        let mut reqs = vec![ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
201            in_folder: openvmm_repo_path.clone(),
202            package: CargoPackage::Workspace,
203            profile: profile.clone(),
204            features: features.clone(),
205            target: target.clone(),
206            extra_env: None,
207            exclude,
208            keep_going: true,
209            all_targets: true,
210            pre_build_deps: pre_build_deps.clone(),
211            done: v,
212        })];
213
214        // crypto has non-additive features, we need to ensure full coverage of different backends.
215        // Always test the 'native' backends.
216        reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
217            in_folder: openvmm_repo_path.clone(),
218            package: CargoPackage::Crate("crypto".into()),
219            profile: profile.clone(),
220            features: CargoFeatureSet::Specific(vec!["native".into()]),
221            target: target.clone(),
222            extra_env: None,
223            exclude: ReadVar::from_static(None),
224            keep_going: true,
225            all_targets: true,
226            pre_build_deps: pre_build_deps.clone(),
227            done: v,
228        }));
229
230        // Always test the pure rust backend.
231        reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
232            in_folder: openvmm_repo_path.clone(),
233            package: CargoPackage::Crate("crypto".into()),
234            profile: profile.clone(),
235            features: CargoFeatureSet::Specific(vec!["rust".into()]),
236            target: target.clone(),
237            extra_env: None,
238            exclude: ReadVar::from_static(None),
239            keep_going: true,
240            all_targets: true,
241            pre_build_deps: pre_build_deps.clone(),
242            done: v,
243        }));
244
245        // Then on linux test the openssl & symcrypt backends, and ensure that --all-features works properly.
246        // We could test openssl on non-linux targets too, but setting up builds for them is a pain.
247        if matches!(
248            target.operating_system,
249            target_lexicon::OperatingSystem::Linux
250        ) {
251            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
252                in_folder: openvmm_repo_path.clone(),
253                package: CargoPackage::Crate("crypto".into()),
254                profile: profile.clone(),
255                features: CargoFeatureSet::Specific(vec!["openssl".into()]),
256                target: target.clone(),
257                extra_env: None,
258                exclude: ReadVar::from_static(None),
259                keep_going: true,
260                all_targets: true,
261                pre_build_deps: pre_build_deps.clone(),
262                done: v,
263            }));
264            // Only test the symcrypt backend on musl targets with our prebuilt lib
265            if matches!(target.environment, target_lexicon::Environment::Musl) {
266                reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
267                    in_folder: openvmm_repo_path.clone(),
268                    package: CargoPackage::Crate("crypto".into()),
269                    profile: profile.clone(),
270                    features: CargoFeatureSet::Specific(vec!["symcrypt".into()]),
271                    target: target.clone(),
272                    extra_env: None,
273                    exclude: ReadVar::from_static(None),
274                    keep_going: true,
275                    all_targets: true,
276                    pre_build_deps: pre_build_deps.clone(),
277                    done: v,
278                }));
279            }
280            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
281                in_folder: openvmm_repo_path.clone(),
282                package: CargoPackage::Crate("crypto".into()),
283                profile: profile.clone(),
284                features: CargoFeatureSet::All,
285                target: target.clone(),
286                extra_env: None,
287                exclude: ReadVar::from_static(None),
288                keep_going: true,
289                all_targets: true,
290                pre_build_deps: pre_build_deps.clone(),
291                done: v,
292            }));
293        }
294
295        if also_check_misc_nostd_crates {
296            // don't pass --all-targets, since that pulls in a std dependency
297            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
298                in_folder: openvmm_repo_path.clone(),
299                package: CargoPackage::Crate("openhcl_boot".into()),
300                profile: profile.clone(),
301                features: CargoFeatureSet::All,
302                target: target_lexicon::triple!(boot_target),
303                extra_env: Some(vec![("MINIMAL_RT_BUILD".into(), "1".into())]),
304                exclude: ReadVar::from_static(None),
305                keep_going: true,
306                all_targets: false,
307                pre_build_deps: pre_build_deps.clone(),
308                done: v,
309            }));
310
311            // don't pass --all-targets, since that pulls in a std dependency
312            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
313                in_folder: openvmm_repo_path.clone(),
314                package: CargoPackage::Crate("guest_test_uefi".into()),
315                profile: profile.clone(),
316                features: CargoFeatureSet::All,
317                target: target_lexicon::triple!(uefi_target),
318                extra_env: None,
319                exclude: ReadVar::from_static(None),
320                keep_going: true,
321                all_targets: false,
322                pre_build_deps: pre_build_deps.clone(),
323                done: v,
324            }));
325
326            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
327                in_folder: openvmm_repo_path.clone(),
328                package: CargoPackage::Crate("opentmk_executor".into()),
329                profile: profile.clone(),
330                features: CargoFeatureSet::All,
331                target: target_lexicon::triple!(uefi_target),
332                extra_env: None,
333                exclude: ReadVar::from_static(None),
334                keep_going: true,
335                all_targets: false,
336                pre_build_deps: pre_build_deps.clone(),
337                done: v,
338            }));
339        }
340
341        ctx.emit_side_effect_step(reqs, [done]);
342
343        Ok(())
344    }
345}