Skip to content

Commit 554ba1c

Browse files
committed
Delete build-wireguard-go.sh and put libwg.a in OUT_DIR
1 parent faf1461 commit 554ba1c

File tree

4 files changed

+174
-171
lines changed

4 files changed

+174
-171
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wireguard-go-rs/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ description = "Rust bindings to wireguard-go with DAITA support"
44
edition = "2021"
55
license.workspace = true
66

7+
[build-dependencies]
8+
anyhow = "1.0"
9+
710
[target.'cfg(unix)'.dependencies]
811
thiserror.workspace = true
912
log.workspace = true
1013
zeroize = "1.8.1"
1114

1215
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
13-
# The app does not depend on maybenot-ffi itself, but adds it as a dependency to expose FFI symbols to wireguard-go.
16+
# The app does not depend on maybenot-ffi itself, but adds it as a dependency to expose FFI symbols to wireguard-go.
1417
# This is done, instead of using the makefile in wireguard-go to build maybenot-ffi into its archive, to prevent
1518
# name clashes induced by link-time optimization.
1619
# NOTE: the version of maybenot-ffi below must be the same as the version checked into the wireguard-go submodule

wireguard-go-rs/build-wireguard-go.sh

-128
This file was deleted.

wireguard-go-rs/build.rs

+169-42
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,194 @@
1-
use core::{panic, str};
2-
use std::{env, path::PathBuf};
1+
use std::{borrow::BorrowMut, env, path::PathBuf, process::Command, str};
32

4-
fn main() {
5-
let out_dir = env::var("OUT_DIR").expect("Missing OUT_DIR");
3+
use anyhow::{anyhow, bail, Context};
64

7-
// Add DAITA as a conditional configuration
5+
fn main() -> anyhow::Result<()> {
6+
let target_os = env::var("CARGO_CFG_TARGET_OS").context("Missing 'CARGO_CFG_TARGET_OS")?;
7+
8+
// Mark "daita" as a conditional configuration flag
89
println!("cargo::rustc-check-cfg=cfg(daita)");
910

10-
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Missing 'CARGO_CFG_TARGET_OS");
11-
let mut cmd = std::process::Command::new("bash");
12-
cmd.arg("./build-wireguard-go.sh");
11+
// Rerun build-script if libwg (or wireguard-go) is changed
12+
println!("cargo::rerun-if-changed=libwg");
1313

1414
match target_os.as_str() {
15-
"linux" | "macos" => {
16-
// Enable DAITA
17-
println!(r#"cargo::rustc-cfg=daita"#);
18-
// Tell the build script to build wireguard-go with DAITA support
19-
cmd.arg("--daita");
20-
}
21-
"android" => {
22-
cmd.arg("--android");
23-
}
15+
"linux" => build_static_lib(Os::Linux, true)?,
16+
"macos" => build_static_lib(Os::MacOs, true)?,
17+
"android" => build_android_dynamic_lib()?,
2418
// building wireguard-go-rs for windows is not implemented
25-
_ => return,
19+
_ => {}
2620
}
2721

28-
let output = cmd.output().expect("build-wireguard-go.sh failed");
29-
if !output.status.success() {
30-
let stdout = str::from_utf8(&output.stdout).unwrap();
31-
let stderr = str::from_utf8(&output.stderr).unwrap();
32-
eprintln!("build-wireguard-go.sh failed.");
33-
eprintln!("stdout:\n{stdout}");
34-
eprintln!("stderr:\n{stderr}");
35-
panic!();
22+
Ok(())
23+
}
24+
25+
#[derive(PartialEq, Eq)]
26+
enum Os {
27+
MacOs,
28+
Linux,
29+
}
30+
31+
#[derive(PartialEq, Eq)]
32+
enum Arch {
33+
Amd64,
34+
Arm64,
35+
}
36+
37+
fn host_os() -> anyhow::Result<Os> {
38+
// this ugliness is a limitation of rust, where we can't directly
39+
// access the target triple of the build script.
40+
if cfg!(target_os = "linux") {
41+
Ok(Os::Linux)
42+
} else if cfg!(target_os = "macos") {
43+
Ok(Os::MacOs)
44+
} else {
45+
bail!("Unsupported host OS")
3646
}
47+
}
3748

38-
if target_os.as_str() != "android" {
39-
println!("cargo::rustc-link-lib=static=wg");
49+
fn host_arch() -> anyhow::Result<Arch> {
50+
if cfg!(target_arch = "x86_64") {
51+
Ok(Arch::Amd64)
52+
} else if cfg!(target_arch = "aarch64") {
53+
Ok(Arch::Arm64)
4054
} else {
41-
// NOTE: Link dynamically to libwg on Android, as go cannot produce archives
42-
println!("cargo::rustc-link-lib=dylib=wg");
55+
bail!("Unsupported host architecture")
4356
}
44-
declare_libs_dir("../build/lib");
57+
}
4558

46-
println!("cargo::rerun-if-changed=libwg");
59+
/// Compile libwg as a static library and place it in `OUT_DIR`.
60+
fn build_static_lib(target_os: Os, daita: bool) -> anyhow::Result<()> {
61+
let out_dir = env::var("OUT_DIR").context("Missing OUT_DIR")?;
62+
let target_arch =
63+
env::var("CARGO_CFG_TARGET_ARCH").context("Missing 'CARGO_CFG_TARGET_ARCH")?;
64+
65+
let target_arch = match target_arch.as_str() {
66+
"x86_64" => Arch::Amd64,
67+
"aarch64" => Arch::Arm64,
68+
_ => bail!("Unsupported architecture: {target_arch}"),
69+
};
70+
71+
let out_file = format!("{out_dir}/libwg.a");
72+
let mut go_build = Command::new("go");
73+
go_build
74+
.args(["build", "-v", "-o", &out_file])
75+
.args(["-buildmode", "c-archive"])
76+
.args(if daita { &["--tags", "daita"][..] } else { &[] })
77+
.env("CGO_ENABLED", "1")
78+
.current_dir("./libwg");
79+
80+
// are we cross compiling?
81+
let cross_compiling = host_os()? != target_os || host_arch()? != target_arch;
82+
83+
match target_arch {
84+
Arch::Amd64 => go_build.env("GOARCH", "amd64"),
85+
Arch::Arm64 => go_build.env("GOARCH", "arm64"),
86+
};
87+
88+
match target_os {
89+
Os::Linux => {
90+
go_build.env("GOOS", "linux");
91+
92+
if cross_compiling {
93+
match target_arch {
94+
Arch::Arm64 => go_build.env("CC", "aarch64-linux-gnu-gcc"),
95+
Arch::Amd64 => bail!("cross-compiling to linux x86_64 is not implemented"),
96+
};
97+
}
98+
}
99+
Os::MacOs => {
100+
go_build.env("GOOS", "darwin");
47101

48-
// Add `OUT_DIR` to the library search path to facilitate linking of libwg for debug artifacts,
49-
// such as test binaries.
50-
if cfg!(debug_assertions) {
51-
println!("cargo::rustc-link-search={out_dir}");
102+
if cross_compiling {
103+
let sdkroot = env::var("SDKROOT").context("Missing 'SDKROOT'")?;
104+
105+
let c_arch = match target_arch {
106+
Arch::Amd64 => "x86_64",
107+
Arch::Arm64 => "arm64",
108+
};
109+
110+
let xcrun_output =
111+
exec(Command::new("xcrun").args(["-sdk", &sdkroot, "--find", "clang"]))?;
112+
go_build.env("CC", xcrun_output);
113+
114+
let cflags = format!("-isysroot {sdkroot} -arch {c_arch} -I{sdkroot}/usr/include");
115+
go_build.env("CFLAGS", cflags);
116+
go_build.env("CGO_CFLAGS", format!("-isysroot {sdkroot} -arch {c_arch}"));
117+
go_build.env("CGO_LDFLAGS", format!("-isysroot {sdkroot} -arch {c_arch}"));
118+
go_build.env("LD_LIBRARY_PATH", format!("{sdkroot}/usr/lib"));
119+
}
120+
}
52121
}
122+
123+
exec(go_build)?;
124+
125+
// make sure to link to the resulting binary
126+
println!("cargo::rustc-link-search={out_dir}");
127+
println!("cargo::rustc-link-lib=static=wg");
128+
129+
// if daita is enabled, also enable the corresponding rust feature flag
130+
if daita {
131+
println!(r#"cargo::rustc-cfg=daita"#);
132+
}
133+
134+
Ok(())
53135
}
54136

55-
/// Tell linker to check `base`/$TARGET for shared libraries.
56-
fn declare_libs_dir(base: &str) {
57-
let target_triplet = env::var("TARGET").expect("TARGET is not set");
58-
let lib_dir = manifest_dir().join(base).join(target_triplet);
137+
/// Compile libwg as a dynamic library for android and place it in `../build/lib/$TARGET`.
138+
// NOTE: We use dynamic linking as Go cannot produce static binaries specifically for Android.
139+
fn build_android_dynamic_lib() -> anyhow::Result<()> {
140+
let target_triplet = env::var("TARGET").context("TARGET is not set")?;
141+
142+
exec(Command::new("./libwg/build-android.sh"))?;
143+
144+
// Tell linker to check `base`/$TARGET for the dynamic library.
145+
let lib_dir = manifest_dir()?.join("../build/lib").join(target_triplet);
59146
println!("cargo::rustc-link-search={}", lib_dir.display());
147+
println!("cargo::rustc-link-lib=dylib=wg");
148+
149+
Ok(())
60150
}
61151

62152
/// Get the directory containing `Cargo.toml`
63-
fn manifest_dir() -> PathBuf {
153+
fn manifest_dir() -> anyhow::Result<PathBuf> {
64154
env::var("CARGO_MANIFEST_DIR")
65155
.map(PathBuf::from)
66-
.expect("CARGO_MANIFEST_DIR env var not set")
156+
.context("CARGO_MANIFEST_DIR env var not set")
157+
}
158+
159+
/// Execute a command, assert that it succeeds, and return stdout as a string.
160+
fn exec(mut command: impl BorrowMut<Command>) -> anyhow::Result<String> {
161+
let command = command.borrow_mut();
162+
163+
let output = command
164+
.output()
165+
.with_context(|| anyhow!("Failed to execute command: {command:?}"))?;
166+
167+
let stdout = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8");
168+
169+
if !output.status.success() {
170+
let stderr = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8");
171+
172+
eprintln!("Error from {command:?}");
173+
eprintln!();
174+
eprintln!("stdout:");
175+
eprintln!();
176+
eprintln!("{stdout}");
177+
eprintln!();
178+
eprintln!("-------");
179+
eprintln!("stderr:");
180+
eprintln!();
181+
eprintln!("{stderr}");
182+
eprintln!();
183+
eprintln!("-------");
184+
185+
return Err(anyhow!("Failed to execute command: {command:?}")).with_context(|| {
186+
anyhow!(
187+
"Command exited with a non-zero exit code: {}",
188+
output.status
189+
)
190+
});
191+
}
192+
193+
Ok(stdout.to_string())
67194
}

0 commit comments

Comments
 (0)