summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2024-08-15 18:06:13 +0200
committerDavid Lönnhager <david.l@mullvad.net>2024-08-15 18:06:13 +0200
commit532b8ade64f47be9d00655844f4b5fb9070f293e (patch)
treeece3d58bffd91e224f15d1168003b26273cca944
parent605e43f2b4aac34a00390cd7afa6e9393c86f665 (diff)
parent18b38cf89a7c1b495edffdc674a769d593a408c4 (diff)
downloadmullvadvpn-532b8ade64f47be9d00655844f4b5fb9070f293e.tar.xz
mullvadvpn-532b8ade64f47be9d00655844f4b5fb9070f293e.zip
Merge branch 'wgrs-constant-rebuild'
-rw-r--r--Cargo.lock1
-rw-r--r--wireguard-go-rs/Cargo.toml5
-rwxr-xr-xwireguard-go-rs/build-wireguard-go.sh128
-rw-r--r--wireguard-go-rs/build.rs212
4 files changed, 174 insertions, 172 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b45747d08e..7683c0e820 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5051,6 +5051,7 @@ dependencies = [
name = "wireguard-go-rs"
version = "0.0.0"
dependencies = [
+ "anyhow",
"log",
"maybenot-ffi",
"thiserror",
diff --git a/wireguard-go-rs/Cargo.toml b/wireguard-go-rs/Cargo.toml
index 63a5e462e1..062c80de34 100644
--- a/wireguard-go-rs/Cargo.toml
+++ b/wireguard-go-rs/Cargo.toml
@@ -4,13 +4,16 @@ description = "Rust bindings to wireguard-go with DAITA support"
edition = "2021"
license.workspace = true
+[build-dependencies]
+anyhow = "1.0"
+
[target.'cfg(unix)'.dependencies]
thiserror.workspace = true
log.workspace = true
zeroize = "1.8.1"
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
-# The app does not depend on maybenot-ffi itself, but adds it as a dependency to expose FFI symbols to wireguard-go.
+# The app does not depend on maybenot-ffi itself, but adds it as a dependency to expose FFI symbols to wireguard-go.
# This is done, instead of using the makefile in wireguard-go to build maybenot-ffi into its archive, to prevent
# name clashes induced by link-time optimization.
# NOTE: the version of maybenot-ffi below must be the same as the version checked into the wireguard-go submodule
diff --git a/wireguard-go-rs/build-wireguard-go.sh b/wireguard-go-rs/build-wireguard-go.sh
deleted file mode 100755
index 35bb9413f2..0000000000
--- a/wireguard-go-rs/build-wireguard-go.sh
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/env bash
-#
-# This script is used to build libwg (wireguard-go as an FFI-friendly library) for different platforms.
-#
-# Supported arguments:
-# * --android
-# If libwg should be built for an Android target.
-# * --daita
-# Build libwg with "DAITA" support.
-#
-
-set -eu
-
-# If the target OS is Android.
-ANDROID="false"
-# If Wireguard-go should be built with DAITA-support.
-DAITA="false"
-
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-BUILD_DIR="$SCRIPT_DIR/../build"
-
-while [[ "$#" -gt 0 ]]; do
- case $1 in
- --android) ANDROID="true";;
- --daita) DAITA="true";;
- *)
- echo "Unknown parameter: $1"
- exit 1
- ;;
- esac
- shift
-done
-
-function unix_target_triple {
- local platform
- platform="$(uname -s)"
- if [[ ("${platform}" == "Linux") ]]; then
- local arch
- arch="$(uname -m)"
- echo "${arch}-unknown-linux-gnu"
- elif [[ ("${platform}" == "Darwin") ]]; then
- local arch
- arch="$(uname -m)"
- if [[ ("${arch}" == "arm64") ]]; then
- arch="aarch64"
- fi
- echo "${arch}-apple-darwin"
- else
- echo "Can't deduce target dir for $platform"
- return 1
- fi
-}
-
-
-function build_unix {
- echo "Building wireguard-go for $1"
-
- # Flags for cross compiling
- if [[ "$(unix_target_triple)" != "$1" ]]; then
- # Linux arm
- if [[ "$1" == "aarch64-unknown-linux-gnu" ]]; then
- export CGO_ENABLED=1
- export GOARCH=arm64
- export CC=aarch64-linux-gnu-gcc
- fi
-
- # Environment flags for cross compiling on macOS
- if [[ "$1" == *-apple-darwin ]]; then
- export CGO_ENABLED=1
- export GOOS=darwin
-
- local arch=x86_64
- export GOARCH=amd64
- if [[ "$1" == aarch64-* ]]; then
- arch=arm64
- export GOARCH=arm64
- fi
-
- CC="$(xcrun -sdk "$SDKROOT" --find clang) -arch $arch -isysroot $SDKROOT"
- export CC
- export CFLAGS="-isysroot $SDKROOT -arch $arch -I$SDKROOT/usr/include"
- export LD_LIBRARY_PATH="$SDKROOT/usr/lib"
- export CGO_CFLAGS="-isysroot $SDKROOT -arch $arch"
- export CGO_LDFLAGS="-isysroot $SDKROOT -arch $arch"
- fi
- fi
-
-
- # Build wireguard-go as a library
- mkdir -p "$BUILD_DIR/lib/$TARGET"
- pushd libwg
-
- if [[ "$DAITA" == "true" ]]; then
- go build -v --tags daita -o "$BUILD_DIR/lib/$TARGET"/libwg.a -buildmode c-archive
- else
- go build -v -o "$BUILD_DIR/lib/$TARGET"/libwg.a -buildmode c-archive
- fi
-
- popd
-}
-
-function build_android {
- echo "Building wireguard-go for android"
-
- ./libwg/build-android.sh
-}
-
-function build_wireguard_go {
- if [[ "$ANDROID" == "true" ]]; then
- build_android "$@"
- return
- fi
-
- local platform
- platform="$(uname -s)";
- case "$platform" in
- Linux*|Darwin*) build_unix "${TARGET:-$(unix_target_triple)}";;
- *)
- echo "Unsupported platform"
- return 1
- ;;
- esac
-}
-
-# Ensure we are in the correct directory for the execution of this script
-script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-cd "$script_dir"
-build_wireguard_go "$@"
diff --git a/wireguard-go-rs/build.rs b/wireguard-go-rs/build.rs
index 2b8e28601d..08905f5e16 100644
--- a/wireguard-go-rs/build.rs
+++ b/wireguard-go-rs/build.rs
@@ -1,68 +1,194 @@
-use core::{panic, str};
-use std::{env, path::PathBuf};
+use std::{borrow::BorrowMut, env, path::PathBuf, process::Command, str};
-fn main() {
- let out_dir = env::var("OUT_DIR").expect("Missing OUT_DIR");
+use anyhow::{anyhow, bail, Context};
- // Add DAITA as a conditional configuration
+fn main() -> anyhow::Result<()> {
+ let target_os = env::var("CARGO_CFG_TARGET_OS").context("Missing 'CARGO_CFG_TARGET_OS")?;
+
+ // Mark "daita" as a conditional configuration flag
println!("cargo::rustc-check-cfg=cfg(daita)");
- let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Missing 'CARGO_CFG_TARGET_OS");
- let mut cmd = std::process::Command::new("bash");
- cmd.arg("./build-wireguard-go.sh");
+ // Rerun build-script if libwg (or wireguard-go) is changed
+ println!("cargo::rerun-if-changed=libwg");
match target_os.as_str() {
- "linux" | "macos" => {
- // Enable DAITA
- println!(r#"cargo::rustc-cfg=daita"#);
- // Tell the build script to build wireguard-go with DAITA support
- cmd.arg("--daita");
- }
- "android" => {
- cmd.arg("--android");
- }
+ "linux" => build_static_lib(Os::Linux, true)?,
+ "macos" => build_static_lib(Os::MacOs, true)?,
+ "android" => build_android_dynamic_lib()?,
// building wireguard-go-rs for windows is not implemented
- _ => return,
+ _ => {}
}
- let output = cmd.output().expect("build-wireguard-go.sh failed");
- if !output.status.success() {
- let stdout = str::from_utf8(&output.stdout).unwrap();
- let stderr = str::from_utf8(&output.stderr).unwrap();
- eprintln!("build-wireguard-go.sh failed.");
- eprintln!("stdout:\n{stdout}");
- eprintln!("stderr:\n{stderr}");
- panic!();
+ Ok(())
+}
+
+#[derive(PartialEq, Eq)]
+enum Os {
+ MacOs,
+ Linux,
+}
+
+#[derive(PartialEq, Eq)]
+enum Arch {
+ Amd64,
+ Arm64,
+}
+
+fn host_os() -> anyhow::Result<Os> {
+ // this ugliness is a limitation of rust, where we can't directly
+ // access the target triple of the build script.
+ if cfg!(target_os = "linux") {
+ Ok(Os::Linux)
+ } else if cfg!(target_os = "macos") {
+ Ok(Os::MacOs)
+ } else {
+ bail!("Unsupported host OS")
}
+}
- if target_os.as_str() != "android" {
- println!("cargo::rustc-link-lib=static=wg");
+fn host_arch() -> anyhow::Result<Arch> {
+ if cfg!(target_arch = "x86_64") {
+ Ok(Arch::Amd64)
+ } else if cfg!(target_arch = "aarch64") {
+ Ok(Arch::Arm64)
} else {
- // NOTE: Link dynamically to libwg on Android, as go cannot produce archives
- println!("cargo::rustc-link-lib=dylib=wg");
+ bail!("Unsupported host architecture")
}
- declare_libs_dir("../build/lib");
+}
- println!("cargo::rerun-if-changed=libwg");
+/// Compile libwg as a static library and place it in `OUT_DIR`.
+fn build_static_lib(target_os: Os, daita: bool) -> anyhow::Result<()> {
+ let out_dir = env::var("OUT_DIR").context("Missing OUT_DIR")?;
+ let target_arch =
+ env::var("CARGO_CFG_TARGET_ARCH").context("Missing 'CARGO_CFG_TARGET_ARCH")?;
+
+ let target_arch = match target_arch.as_str() {
+ "x86_64" => Arch::Amd64,
+ "aarch64" => Arch::Arm64,
+ _ => bail!("Unsupported architecture: {target_arch}"),
+ };
+
+ let out_file = format!("{out_dir}/libwg.a");
+ let mut go_build = Command::new("go");
+ go_build
+ .args(["build", "-v", "-o", &out_file])
+ .args(["-buildmode", "c-archive"])
+ .args(if daita { &["--tags", "daita"][..] } else { &[] })
+ .env("CGO_ENABLED", "1")
+ .current_dir("./libwg");
+
+ // are we cross compiling?
+ let cross_compiling = host_os()? != target_os || host_arch()? != target_arch;
+
+ match target_arch {
+ Arch::Amd64 => go_build.env("GOARCH", "amd64"),
+ Arch::Arm64 => go_build.env("GOARCH", "arm64"),
+ };
+
+ match target_os {
+ Os::Linux => {
+ go_build.env("GOOS", "linux");
+
+ if cross_compiling {
+ match target_arch {
+ Arch::Arm64 => go_build.env("CC", "aarch64-linux-gnu-gcc"),
+ Arch::Amd64 => bail!("cross-compiling to linux x86_64 is not implemented"),
+ };
+ }
+ }
+ Os::MacOs => {
+ go_build.env("GOOS", "darwin");
- // Add `OUT_DIR` to the library search path to facilitate linking of libwg for debug artifacts,
- // such as test binaries.
- if cfg!(debug_assertions) {
- println!("cargo::rustc-link-search={out_dir}");
+ if cross_compiling {
+ let sdkroot = env::var("SDKROOT").context("Missing 'SDKROOT'")?;
+
+ let c_arch = match target_arch {
+ Arch::Amd64 => "x86_64",
+ Arch::Arm64 => "arm64",
+ };
+
+ let xcrun_output =
+ exec(Command::new("xcrun").args(["-sdk", &sdkroot, "--find", "clang"]))?;
+ go_build.env("CC", xcrun_output);
+
+ let cflags = format!("-isysroot {sdkroot} -arch {c_arch} -I{sdkroot}/usr/include");
+ go_build.env("CFLAGS", cflags);
+ go_build.env("CGO_CFLAGS", format!("-isysroot {sdkroot} -arch {c_arch}"));
+ go_build.env("CGO_LDFLAGS", format!("-isysroot {sdkroot} -arch {c_arch}"));
+ go_build.env("LD_LIBRARY_PATH", format!("{sdkroot}/usr/lib"));
+ }
+ }
}
+
+ exec(go_build)?;
+
+ // make sure to link to the resulting binary
+ println!("cargo::rustc-link-search={out_dir}");
+ println!("cargo::rustc-link-lib=static=wg");
+
+ // if daita is enabled, also enable the corresponding rust feature flag
+ if daita {
+ println!(r#"cargo::rustc-cfg=daita"#);
+ }
+
+ Ok(())
}
-/// Tell linker to check `base`/$TARGET for shared libraries.
-fn declare_libs_dir(base: &str) {
- let target_triplet = env::var("TARGET").expect("TARGET is not set");
- let lib_dir = manifest_dir().join(base).join(target_triplet);
- println!("cargo::rerun-if-changed={}", lib_dir.display());
+/// Compile libwg as a dynamic library for android and place it in `../build/lib/$TARGET`.
+// NOTE: We use dynamic linking as Go cannot produce static binaries specifically for Android.
+fn build_android_dynamic_lib() -> anyhow::Result<()> {
+ let target_triplet = env::var("TARGET").context("TARGET is not set")?;
+
+ exec(Command::new("./libwg/build-android.sh"))?;
+
+ // Tell linker to check `base`/$TARGET for the dynamic library.
+ let lib_dir = manifest_dir()?.join("../build/lib").join(target_triplet);
println!("cargo::rustc-link-search={}", lib_dir.display());
+ println!("cargo::rustc-link-lib=dylib=wg");
+
+ Ok(())
}
/// Get the directory containing `Cargo.toml`
-fn manifest_dir() -> PathBuf {
+fn manifest_dir() -> anyhow::Result<PathBuf> {
env::var("CARGO_MANIFEST_DIR")
.map(PathBuf::from)
- .expect("CARGO_MANIFEST_DIR env var not set")
+ .context("CARGO_MANIFEST_DIR env var not set")
+}
+
+/// Execute a command, assert that it succeeds, and return stdout as a string.
+fn exec(mut command: impl BorrowMut<Command>) -> anyhow::Result<String> {
+ let command = command.borrow_mut();
+
+ let output = command
+ .output()
+ .with_context(|| anyhow!("Failed to execute command: {command:?}"))?;
+
+ let stdout = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8");
+
+ if !output.status.success() {
+ let stderr = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8");
+
+ eprintln!("Error from {command:?}");
+ eprintln!();
+ eprintln!("stdout:");
+ eprintln!();
+ eprintln!("{stdout}");
+ eprintln!();
+ eprintln!("-------");
+ eprintln!("stderr:");
+ eprintln!();
+ eprintln!("{stderr}");
+ eprintln!();
+ eprintln!("-------");
+
+ return Err(anyhow!("Failed to execute command: {command:?}")).with_context(|| {
+ anyhow!(
+ "Command exited with a non-zero exit code: {}",
+ output.status
+ )
+ });
+ }
+
+ Ok(stdout.to_string())
}