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}