flowey_lib_hvlite/
init_cross_build.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Install dependencies and set environment variables for cross compiling
5
6use flowey::node::prelude::*;
7use std::collections::BTreeMap;
8use target_lexicon::Architecture;
9
10flowey_request! {
11    pub struct Request {
12        pub target: target_lexicon::Triple,
13        pub injected_env: WriteVar<BTreeMap<String, String>>,
14    }
15}
16
17new_flow_node!(struct Node);
18
19impl FlowNode for Node {
20    type Request = Request;
21
22    fn imports(ctx: &mut ImportCtx<'_>) {
23        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
24    }
25
26    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
27        let host_platform = ctx.platform();
28        let host_arch = ctx.arch();
29
30        let native = |target: &target_lexicon::Triple| -> bool {
31            let platform = match target.operating_system {
32                target_lexicon::OperatingSystem::Windows => FlowPlatform::Windows,
33                target_lexicon::OperatingSystem::Linux => {
34                    FlowPlatform::Linux(FlowPlatformLinuxDistro::Ubuntu)
35                }
36                target_lexicon::OperatingSystem::Darwin(_) => FlowPlatform::MacOs,
37                _ => return false,
38            };
39            let arch = match target.architecture {
40                Architecture::X86_64 => FlowArch::X86_64,
41                Architecture::Aarch64(_) => FlowArch::Aarch64,
42                _ => return false,
43            };
44            host_platform == platform && host_arch == arch
45        };
46
47        for Request {
48            target,
49            injected_env: injected_env_write,
50        } in requests
51        {
52            let mut pre_build_deps = Vec::new();
53            let mut injected_env = BTreeMap::new();
54
55            if !native(&target) {
56                let platform = ctx.platform();
57
58                match (platform, target.operating_system) {
59                    (FlowPlatform::Linux(_), target_lexicon::OperatingSystem::Linux) => {
60                        let (gcc_pkg, bin) = match target.architecture {
61                            Architecture::X86_64 => match platform {
62                                FlowPlatform::Linux(linux_distribution) => {
63                                    let pkg = match linux_distribution {
64                                        FlowPlatformLinuxDistro::Fedora => "gcc-x86_64-linux-gnu",
65                                        FlowPlatformLinuxDistro::Ubuntu => "gcc-x86-64-linux-gnu",
66                                        FlowPlatformLinuxDistro::Arch => {
67                                            match_arch!(host_arch, FlowArch::X86_64, "gcc")
68                                        }
69                                        FlowPlatformLinuxDistro::Unknown => {
70                                            anyhow::bail!("Unknown Linux distribution")
71                                        }
72                                    };
73                                    (pkg.to_string(), "x86_64-linux-gnu-gcc".to_string())
74                                }
75                                _ => anyhow::bail!("Unsupported platform"),
76                            },
77                            Architecture::Aarch64(_) => match platform {
78                                FlowPlatform::Linux(linux_distribution) => {
79                                    let pkg = match linux_distribution {
80                                        FlowPlatformLinuxDistro::Fedora
81                                        | FlowPlatformLinuxDistro::Ubuntu => {
82                                            "gcc-aarch64-linux-gnu"
83                                        }
84                                        FlowPlatformLinuxDistro::Arch => match_arch!(
85                                            host_arch,
86                                            FlowArch::X86_64,
87                                            "aarch64-linux-gnu-gcc"
88                                        ),
89                                        FlowPlatformLinuxDistro::Unknown => {
90                                            anyhow::bail!("Unknown Linux distribution")
91                                        }
92                                    };
93                                    (pkg.to_string(), "aarch64-linux-gnu-gcc".to_string())
94                                }
95                                _ => anyhow::bail!("Unsupported platform"),
96                            },
97                            arch => anyhow::bail!("unsupported arch {arch}"),
98                        };
99
100                        // We use `gcc`'s linker for cross-compiling due to:
101                        //
102                        // * The special baremetal options are the same. These options
103                        //   don't work for the LLVM linker,
104                        // * The compiler team at Microsoft has stated that `rust-lld`
105                        //   is not a production option,
106                        // * The only Rust `aarch64` targets that produce
107                        //   position-independent static ELF binaries with no std are
108                        //   `aarch64-unknown-linux-*`.
109                        pre_build_deps.push(ctx.reqv(|v| {
110                            flowey_lib_common::install_dist_pkg::Request::Install {
111                                package_names: vec![gcc_pkg],
112                                done: v,
113                            }
114                        }));
115
116                        // when cross compiling for gnu linux, explicitly set the
117                        // linker being used.
118                        //
119                        // Note: Don't do this for musl, since for that we use the
120                        // openhcl linker set in the repo's `.cargo/config.toml`
121                        // This isn't ideal because it means _any_ musl code (not just
122                        // code running in VTL2) will use the openhcl-specific musl
123                        if matches!(target.environment, target_lexicon::Environment::Gnu) {
124                            injected_env.insert(
125                                format!(
126                                    "CARGO_TARGET_{}_LINKER",
127                                    target.to_string().replace('-', "_").to_uppercase()
128                                ),
129                                bin,
130                            );
131                        }
132                    }
133                    // Cross compiling for Windows relies on the appropriate
134                    // Visual Studio Build Tools components being installed.
135                    // The necessary libraries can be accessed from WSL,
136                    // allowing for compilation of Windows applications from Linux.
137                    // For now, just silently continue regardless.
138                    // TODO: Detect (and potentially install) these dependencies
139                    (FlowPlatform::Linux(_), target_lexicon::OperatingSystem::Windows) => {}
140                    (FlowPlatform::Windows, target_lexicon::OperatingSystem::Windows) => {}
141                    (_, target_lexicon::OperatingSystem::None_) => {}
142                    (_, target_lexicon::OperatingSystem::Uefi) => {}
143                    (host_os, target_os) => {
144                        anyhow::bail!("cannot cross compile for {target_os} on {host_os}")
145                    }
146                }
147            }
148
149            ctx.emit_minor_rust_step("inject cross env", |ctx| {
150                pre_build_deps.claim(ctx);
151                let injected_env_write = injected_env_write.claim(ctx);
152                move |rt| {
153                    rt.write(injected_env_write, &injected_env);
154                }
155            });
156        }
157
158        Ok(())
159    }
160}