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 let gcc_arch_str = match target.architecture {
58 Architecture::X86_64 => match platform {
59 FlowPlatform::Linux(linux_distribution) => match linux_distribution {
60 FlowPlatformLinuxDistro::Fedora => "x86_64",
61 FlowPlatformLinuxDistro::Ubuntu => "x86-64",
62 FlowPlatformLinuxDistro::Unknown => {
63 anyhow::bail!("Unknown Linux distribution")
64 }
65 },
66 _ => anyhow::bail!("Unsupported platform"),
67 },
68 Architecture::Aarch64(_) => "aarch64",
69 arch => anyhow::bail!("unsupported arch {arch}"),
70 };
71
72 match (platform, target.operating_system) {
73 (FlowPlatform::Linux(_), target_lexicon::OperatingSystem::Linux) => {
74 let (gcc_pkg, bin) = match target.architecture {
75 Architecture::Aarch64(_) => (
76 format!("gcc-{gcc_arch_str}-linux-gnu"),
77 "aarch64-linux-gnu-gcc".to_string(),
78 ),
79 Architecture::X86_64 => (
80 format!("gcc-{gcc_arch_str}-linux-gnu"),
81 "x86_64-linux-gnu-gcc".to_string(),
82 ),
83 arch => anyhow::bail!("unsupported arch {arch}"),
84 };
85
86 // We use `gcc`'s linker for cross-compiling due to:
87 //
88 // * The special baremetal options are the same. These options
89 // don't work for the LLVM linker,
90 // * The compiler team at Microsoft has stated that `rust-lld`
91 // is not a production option,
92 // * The only Rust `aarch64` targets that produce
93 // position-independent static ELF binaries with no std are
94 // `aarch64-unknown-linux-*`.
95 pre_build_deps.push(ctx.reqv(|v| {
96 flowey_lib_common::install_dist_pkg::Request::Install {
97 package_names: vec![gcc_pkg],
98 done: v,
99 }
100 }));
101
102 // when cross compiling for gnu linux, explicitly set the
103 // linker being used.
104 //
105 // Note: Don't do this for musl, since for that we use the
106 // openhcl linker set in the repo's `.cargo/config.toml`
107 // This isn't ideal because it means _any_ musl code (not just
108 // code running in VTL2) will use the openhcl-specific musl
109 if matches!(target.environment, target_lexicon::Environment::Gnu) {
110 injected_env.insert(
111 format!(
112 "CARGO_TARGET_{}_LINKER",
113 target.to_string().replace('-', "_").to_uppercase()
114 ),
115 bin,
116 );
117 }
118 }
119 // Cross compiling for Windows relies on the appropriate
120 // Visual Studio Build Tools components being installed.
121 // The necessary libraries can be accessed from WSL,
122 // allowing for compilation of Windows applications from Linux.
123 // For now, just silently continue regardless.
124 // TODO: Detect (and potentially install) these dependencies
125 (FlowPlatform::Linux(_), target_lexicon::OperatingSystem::Windows) => {}
126 (FlowPlatform::Windows, target_lexicon::OperatingSystem::Windows) => {}
127 (_, target_lexicon::OperatingSystem::None_) => {}
128 (_, target_lexicon::OperatingSystem::Uefi) => {}
129 (host_os, target_os) => {
130 anyhow::bail!("cannot cross compile for {target_os} on {host_os}")
131 }
132 }
133 }
134
135 ctx.emit_minor_rust_step("inject cross env", |ctx| {
136 pre_build_deps.claim(ctx);
137 let injected_env_write = injected_env_write.claim(ctx);
138 move |rt| {
139 rt.write(injected_env_write, &injected_env);
140 }
141 });
142 }
143
144 Ok(())
145 }
146}