1use std::path::Path;
5use std::process::Command;
6
7use crate::CodegenError;
8
9#[cfg(target_os = "macos")]
12pub const RUNTIME_LIBRARY: &str = "libnifty_rt.a";
13#[cfg(windows)]
14pub const RUNTIME_LIBRARY: &str = "nifty_rt.lib";
15
16pub fn link(object: &Path, runtime: &Path, executable: &Path) -> Result<(), CodegenError> {
22 #[cfg(target_os = "macos")]
23 let mut linker = linker(object, runtime, executable);
24 #[cfg(windows)]
25 let mut linker = linker(object, runtime, executable)?;
26 let output = linker.output().map_err(|error| {
27 CodegenError::new(format!("can't run the linker: {error}{LINKER_HINT}"))
28 })?;
29 if output.status.success() {
30 Ok(())
31 } else {
32 Err(CodegenError::new(format!(
33 "linking {} failed:\n{}",
34 executable.display(),
35 String::from_utf8_lossy(&output.stderr).trim_end()
36 )))
37 }
38}
39
40#[cfg(target_os = "macos")]
42const LINKER_HINT: &str = "; install Xcode's command line tools with `xcode-select --install`";
43#[cfg(windows)]
44const LINKER_HINT: &str = "";
45
46#[cfg(target_os = "macos")]
48fn linker(object: &Path, runtime: &Path, executable: &Path) -> Command {
49 let mut command = Command::new("cc");
50 command
51 .args(["-Wl,-dead_strip", "-Wl,-x"])
54 .arg("-o")
55 .arg(executable)
56 .arg(object)
57 .arg(runtime)
58 .args(nifty_rt::NATIVE_LIBS);
59 command
60}
61
62#[cfg(windows)]
64const INSTALL_BUILD_TOOLS: &str = "winget install Microsoft.VisualStudio.2022.BuildTools \
65 --override \"--passive --wait --add Microsoft.VisualStudio.Workload.VCTools \
66 --includeRecommended\"";
67
68#[cfg(windows)]
70fn linker(object: &Path, runtime: &Path, executable: &Path) -> Result<Command, CodegenError> {
71 const MANIFEST: &str = include_str!("../../nifty-cli/windows/nifty.manifest");
73 #[cfg(target_arch = "x86_64")]
74 const TARGET: &str = "x86_64-pc-windows-msvc";
75 #[cfg(target_arch = "aarch64")]
76 const TARGET: &str = "aarch64-pc-windows-msvc";
77
78 let tool = cc::windows_registry::find_tool(TARGET, "link.exe").ok_or_else(|| {
79 CodegenError::new(format!(
80 "building apps needs Microsoft's C++ Build Tools, which link them; install them \
81 with `{INSTALL_BUILD_TOOLS}`"
82 ))
83 })?;
84 let manifest = object.with_file_name("nifty.manifest");
85 std::fs::write(&manifest, MANIFEST).map_err(|error| {
86 CodegenError::new(format!("can't write {}: {error}", manifest.display()))
87 })?;
88 let mut command = tool.to_command();
89 command
90 .arg("/NOLOGO")
91 .arg(format!("/OUT:{}", executable.display()))
92 .arg(object)
93 .arg(runtime)
94 .args(nifty_rt::NATIVE_LIBS)
95 .args([
96 "/SUBSYSTEM:WINDOWS",
97 "/ENTRY:mainCRTStartup",
98 "/MANIFEST:EMBED",
99 ])
100 .arg(format!("/MANIFESTINPUT:{}", manifest.display()));
101 Ok(command)
102}