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                Ok(Some(exclude))
173            }
174        });
175
176        // On Windows & Mac we can't build with all features since the TPM
177        // requires OpenSSL for crypto, which isn't supported in CI on those
178        // platforms today.
179        //
180        // We don't add the CI feature here, as it's used purely to exclude
181        // tests that can't run in CI. We still want those tests to be linted.
182        let features = if matches!(
183            target.operating_system,
184            target_lexicon::OperatingSystem::Windows | target_lexicon::OperatingSystem::Darwin(_)
185        ) {
186            CargoFeatureSet::None
187        } else {
188            CargoFeatureSet::All
189        };
190
191        let mut reqs = vec![ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
192            in_folder: openvmm_repo_path.clone(),
193            package: CargoPackage::Workspace,
194            profile: profile.clone(),
195            features: features.clone(),
196            target: target.clone(),
197            extra_env: None,
198            exclude,
199            keep_going: true,
200            all_targets: true,
201            pre_build_deps: pre_build_deps.clone(),
202            done: v,
203        })];
204
205        // crypto has non-additive features, we need to ensure full coverage of different backends.
206        // Always test the 'native' backends.
207        reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
208            in_folder: openvmm_repo_path.clone(),
209            package: CargoPackage::Crate("crypto".into()),
210            profile: profile.clone(),
211            features: CargoFeatureSet::Specific(vec!["native".into()]),
212            target: target.clone(),
213            extra_env: None,
214            exclude: ReadVar::from_static(None),
215            keep_going: true,
216            all_targets: true,
217            pre_build_deps: pre_build_deps.clone(),
218            done: v,
219        }));
220
221        // Always test the pure rust backend.
222        reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
223            in_folder: openvmm_repo_path.clone(),
224            package: CargoPackage::Crate("crypto".into()),
225            profile: profile.clone(),
226            features: CargoFeatureSet::Specific(vec!["rust".into()]),
227            target: target.clone(),
228            extra_env: None,
229            exclude: ReadVar::from_static(None),
230            keep_going: true,
231            all_targets: true,
232            pre_build_deps: pre_build_deps.clone(),
233            done: v,
234        }));
235
236        // Then on linux test the openssl & symcrypt backends, and ensure that --all-features works properly.
237        // We could test openssl on non-linux targets too, but setting up builds for them is a pain.
238        if matches!(
239            target.operating_system,
240            target_lexicon::OperatingSystem::Linux
241        ) {
242            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
243                in_folder: openvmm_repo_path.clone(),
244                package: CargoPackage::Crate("crypto".into()),
245                profile: profile.clone(),
246                features: CargoFeatureSet::Specific(vec!["openssl".into()]),
247                target: target.clone(),
248                extra_env: None,
249                exclude: ReadVar::from_static(None),
250                keep_going: true,
251                all_targets: true,
252                pre_build_deps: pre_build_deps.clone(),
253                done: v,
254            }));
255            // Only test the symcrypt backend on musl targets with our prebuilt lib
256            if matches!(target.environment, target_lexicon::Environment::Musl) {
257                reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
258                    in_folder: openvmm_repo_path.clone(),
259                    package: CargoPackage::Crate("crypto".into()),
260                    profile: profile.clone(),
261                    features: CargoFeatureSet::Specific(vec!["symcrypt".into()]),
262                    target: target.clone(),
263                    extra_env: None,
264                    exclude: ReadVar::from_static(None),
265                    keep_going: true,
266                    all_targets: true,
267                    pre_build_deps: pre_build_deps.clone(),
268                    done: v,
269                }));
270            }
271            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
272                in_folder: openvmm_repo_path.clone(),
273                package: CargoPackage::Crate("crypto".into()),
274                profile: profile.clone(),
275                features: CargoFeatureSet::All,
276                target: target.clone(),
277                extra_env: None,
278                exclude: ReadVar::from_static(None),
279                keep_going: true,
280                all_targets: true,
281                pre_build_deps: pre_build_deps.clone(),
282                done: v,
283            }));
284        }
285
286        if also_check_misc_nostd_crates {
287            // don't pass --all-targets, since that pulls in a std dependency
288            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
289                in_folder: openvmm_repo_path.clone(),
290                package: CargoPackage::Crate("openhcl_boot".into()),
291                profile: profile.clone(),
292                features: CargoFeatureSet::All,
293                target: target_lexicon::triple!(boot_target),
294                extra_env: Some(vec![("MINIMAL_RT_BUILD".into(), "1".into())]),
295                exclude: ReadVar::from_static(None),
296                keep_going: true,
297                all_targets: false,
298                pre_build_deps: pre_build_deps.clone(),
299                done: v,
300            }));
301
302            // don't pass --all-targets, since that pulls in a std dependency
303            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
304                in_folder: openvmm_repo_path.clone(),
305                package: CargoPackage::Crate("guest_test_uefi".into()),
306                profile: profile.clone(),
307                features: CargoFeatureSet::All,
308                target: target_lexicon::triple!(uefi_target),
309                extra_env: None,
310                exclude: ReadVar::from_static(None),
311                keep_going: true,
312                all_targets: false,
313                pre_build_deps: pre_build_deps.clone(),
314                done: v,
315            }));
316
317            reqs.push(ctx.reqv(|v| flowey_lib_common::run_cargo_clippy::Request {
318                in_folder: openvmm_repo_path.clone(),
319                package: CargoPackage::Crate("opentmk_executor".into()),
320                profile: profile.clone(),
321                features: CargoFeatureSet::All,
322                target: target_lexicon::triple!(uefi_target),
323                extra_env: None,
324                exclude: ReadVar::from_static(None),
325                keep_going: true,
326                all_targets: false,
327                pre_build_deps: pre_build_deps.clone(),
328                done: v,
329            }));
330        }
331
332        ctx.emit_side_effect_step(reqs, [done]);
333
334        Ok(())
335    }
336}