Skip to content

Commit cd54e7e

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

File tree

4 files changed

+159
-170
lines changed

4 files changed

+159
-170
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

+154-41
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,37 @@
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,
26-
}
27-
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!();
19+
_ => {}
3620
}
3721

38-
if target_os.as_str() != "android" {
39-
println!("cargo::rustc-link-lib=static=wg");
40-
} else {
41-
// NOTE: Link dynamically to libwg on Android, as go cannot produce archives
42-
println!("cargo::rustc-link-lib=dylib=wg");
43-
}
44-
declare_libs_dir("../build/lib");
22+
Ok(())
23+
}
4524

46-
println!("cargo::rerun-if-changed=libwg");
25+
#[derive(PartialEq, Eq)]
26+
enum Os {
27+
MacOs,
28+
Linux,
29+
}
4730

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}");
52-
}
31+
#[derive(PartialEq, Eq)]
32+
enum Arch {
33+
Amd64,
34+
Arm64,
5335
}
5436

5537
/// Tell linker to check `base`/$TARGET for shared libraries.
@@ -59,9 +41,140 @@ fn declare_libs_dir(base: &str) {
5941
println!("cargo::rustc-link-search={}", lib_dir.display());
6042
}
6143

44+
/// Compile libwg as a static library and place it in `OUT_DIR`.
45+
fn build_static_lib(target_os: Os, daita: bool) -> anyhow::Result<()> {
46+
let out_dir = env::var("OUT_DIR").context("Missing OUT_DIR")?;
47+
let target_arch =
48+
env::var("CARGO_CFG_TARGET_ARCH").context("Missing 'CARGO_CFG_TARGET_ARCH")?;
49+
50+
let target_arch = match target_arch.as_str() {
51+
"x86_64" => Arch::Amd64,
52+
"aarch64" => Arch::Arm64,
53+
_ => bail!("Unsupported architecture: {target_arch}"),
54+
};
55+
56+
let out_file = format!("{out_dir}/libwg.a");
57+
let mut go_build = Command::new("go");
58+
go_build
59+
.args(["build", "-v", "-o", &out_file])
60+
.args(["-buildmode", "c-archive"])
61+
.args(if daita { &["--tags", "daita"][..] } else { &[] })
62+
.env("CGO_ENABLED", "1")
63+
.current_dir("./libwg");
64+
65+
// compare target of build-script vs library target to figure out of this is cross compilation
66+
// this ugliness is a limitation of rust, where we can't directly access the target triple of
67+
// the build script.
68+
let cross_compiling = (target_arch == Arch::Amd64 && cfg!(not(target_arch = "x86_64")))
69+
|| (target_arch == Arch::Arm64 && cfg!(not(target_arch = "aarch64")))
70+
|| (target_os == Os::Linux && cfg!(not(target_os = "linux")))
71+
|| (target_os == Os::MacOs && cfg!(not(target_os = "macos")));
72+
73+
match target_arch {
74+
Arch::Amd64 => go_build.env("GOARCH", "amd64"),
75+
Arch::Arm64 => go_build.env("GOARCH", "arm64"),
76+
};
77+
78+
match target_os {
79+
Os::Linux => {
80+
go_build.env("GOOS", "linux");
81+
82+
if cross_compiling {
83+
match target_arch {
84+
Arch::Arm64 => go_build.env("CC", "aarch64-linux-gnu-gcc"),
85+
Arch::Amd64 => bail!("cross-compiling to linux x86_64 is not implemented"),
86+
};
87+
}
88+
}
89+
Os::MacOs => {
90+
go_build.env("GOOS", "darwin");
91+
92+
if cross_compiling {
93+
let sdkroot = env::var("SDKROOT").context("Missing 'SDKROOT'")?;
94+
95+
let c_arch = match target_arch {
96+
Arch::Amd64 => "x86_64",
97+
Arch::Arm64 => "arm64",
98+
};
99+
100+
let xcrun_output =
101+
exec(Command::new("xcrun").args(["-sdk", &sdkroot, "--find", "clang"]))?;
102+
go_build.env("CC", xcrun_output);
103+
104+
let cflags = format!("-isysroot {sdkroot} -arch {c_arch} -I{sdkroot}/usr/include");
105+
go_build.env("CFLAGS", cflags);
106+
go_build.env("CGO_CFLAGS", format!("-isysroot {sdkroot} -arch {c_arch}"));
107+
go_build.env("CGO_LDFLAGS", format!("-isysroot {sdkroot} -arch {c_arch}"));
108+
go_build.env("LD_LIBRARY_PATH", format!("{sdkroot}/usr/lib"));
109+
}
110+
}
111+
}
112+
113+
exec(go_build)?;
114+
115+
// make sure to link to the resulting binary
116+
println!("cargo::rustc-link-search={out_dir}");
117+
println!("cargo::rustc-link-lib=static=wg");
118+
119+
// if daita is enabled, also enable the corresponding rust feature flag
120+
if daita {
121+
println!(r#"cargo::rustc-cfg=daita"#);
122+
}
123+
124+
Ok(())
125+
}
126+
127+
/// Compile libwg as a static library and place it in `OUT_DIR`.
128+
fn build_android_dynamic_lib() -> anyhow::Result<()> {
129+
// NOTE: Link dynamically to libwg, as Go cannot produce static binaries for Android.
130+
println!("cargo::rustc-link-lib=dylib=wg");
131+
declare_libs_dir("../build/lib");
132+
133+
exec(Command::new("./libwg/build-android.sh"))?;
134+
135+
Ok(())
136+
}
137+
62138
/// Get the directory containing `Cargo.toml`
63139
fn manifest_dir() -> PathBuf {
64140
env::var("CARGO_MANIFEST_DIR")
65141
.map(PathBuf::from)
66142
.expect("CARGO_MANIFEST_DIR env var not set")
67143
}
144+
145+
/// Execute a command, assert that it succeeds, and return stdout as a string.
146+
fn exec(mut command: impl BorrowMut<Command>) -> anyhow::Result<String> {
147+
let command = command.borrow_mut();
148+
149+
let output = command
150+
.output()
151+
.with_context(|| anyhow!("Failed to execute command: {command:?}"))?;
152+
153+
let stdout = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8");
154+
155+
if !output.status.success() {
156+
let stderr = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8");
157+
158+
eprintln!("Error from {command:?}");
159+
eprintln!();
160+
eprintln!("stdout:");
161+
eprintln!();
162+
eprintln!("{stdout}");
163+
eprintln!();
164+
eprintln!("-------");
165+
eprintln!("stderr:");
166+
eprintln!();
167+
eprintln!("{stderr}");
168+
eprintln!();
169+
eprintln!("-------");
170+
171+
return Err(anyhow!("Failed to execute command: {command:?}")).with_context(|| {
172+
anyhow!(
173+
"Command exited with a non-zero exit code: {}",
174+
output.status
175+
)
176+
});
177+
}
178+
179+
Ok(stdout.to_string())
180+
}

0 commit comments

Comments
 (0)