Build 64: embed the Nym sidecar into the Linux binary (single-file AppImage)
The Linux release no longer ships a loose nym-socks5-client beside AppRun. It's baked into the goblin binary the same way the Windows build bakes it into goblin.exe, and extracted to ~/.local/share/Goblin at first launch (chmod +x on Unix). The AppImage is now one self-contained file with nothing loose to misplace. - build.rs: generalised the Windows-only GOBLIN_NYM_WIN_BIN embed to a cross-platform path. GOBLIN_NYM_UNIX_BIN embeds the Linux/macOS sidecar; Android never embeds (its sidecar rides in the APK's jniLibs). - src/nym/sidecar.rs: the embedded const, extract_embedded_sidecar, and the binary_path extract branch now cover all non-Android targets, with a Unix chmod +x on the freshly written file. - linux/build_release.sh: rewritten to set GOBLIN_NYM_UNIX_BIN, apply the glibc-2.17 zigbuild + CRoaring-AVX512/vendored-OpenSSL fixes, and assemble an AppDir with no loose sidecar.
This commit is contained in:
@@ -60,18 +60,27 @@ fn main() {
|
||||
// `nym-socks5-client` sidecar (see src/nym/); there is no embedded Tor and
|
||||
// thus no webtunnel pluggable-transport binary to build here anymore.
|
||||
|
||||
// Single-file Windows: when GOBLIN_NYM_WIN_BIN points at the Windows
|
||||
// nym-socks5-client.exe, embed it into goblin.exe. At startup the app
|
||||
// extracts it to %LOCALAPPDATA%\Goblin and runs it hidden (src/nym/sidecar.rs),
|
||||
// so the release ships as one self-contained .exe with no loose sidecar file.
|
||||
// Single-file desktop builds: embed the matching nym-socks5-client into the
|
||||
// goblin binary so the release ships as ONE self-contained file with no loose
|
||||
// sidecar beside it. At startup the app extracts it to a per-user data dir and
|
||||
// runs it (src/nym/sidecar.rs). Windows reads GOBLIN_NYM_WIN_BIN (a .exe);
|
||||
// Linux/macOS read GOBLIN_NYM_UNIX_BIN. Android does NOT embed — its sidecar
|
||||
// rides in the APK's jniLibs (the only exec-allowed location).
|
||||
println!("cargo:rerun-if-env-changed=GOBLIN_NYM_WIN_BIN");
|
||||
println!("cargo:rerun-if-env-changed=GOBLIN_NYM_UNIX_BIN");
|
||||
println!("cargo:rustc-check-cfg=cfg(goblin_embed_nym)");
|
||||
if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
|
||||
if let Ok(src) = env::var("GOBLIN_NYM_WIN_BIN") {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
||||
let embed = match target_os.as_str() {
|
||||
"windows" => Some(("GOBLIN_NYM_WIN_BIN", "nym-socks5-client.exe")),
|
||||
"android" => None,
|
||||
_ => Some(("GOBLIN_NYM_UNIX_BIN", "nym-socks5-client")),
|
||||
};
|
||||
if let Some((var, out_name)) = embed {
|
||||
if let Ok(src) = env::var(var) {
|
||||
if !src.is_empty() {
|
||||
let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join("nym-socks5-client.exe");
|
||||
let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join(out_name);
|
||||
std::fs::copy(&src, &out)
|
||||
.expect("copy GOBLIN_NYM_WIN_BIN into OUT_DIR for embedding");
|
||||
.unwrap_or_else(|e| panic!("copy {var} into OUT_DIR for embedding: {e}"));
|
||||
println!("cargo:rustc-cfg=goblin_embed_nym");
|
||||
println!("cargo:rerun-if-changed={}", src);
|
||||
}
|
||||
|
||||
+49
-20
@@ -1,27 +1,56 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build a portable, single-file Goblin AppImage.
|
||||
#
|
||||
# Usage: linux/build_release.sh [platform]
|
||||
# platform: 'x86_64' (default) or 'arm'
|
||||
#
|
||||
# The nym-socks5-client sidecar is EMBEDDED into the goblin binary (build.rs +
|
||||
# src/nym/sidecar.rs, via GOBLIN_NYM_UNIX_BIN), so the AppImage ships as one
|
||||
# self-contained file with no loose sidecar beside AppRun — matching the
|
||||
# single-file Windows build. Point GOBLIN_NYM_UNIX_BIN at a glibc-2.17 sidecar
|
||||
# (the portable one staged under the project root at nym-dist/) so the embedded
|
||||
# copy is as portable as the host binary.
|
||||
|
||||
case $2 in
|
||||
x86_64|arm)
|
||||
;;
|
||||
*)
|
||||
echo "Usage: release_linux.sh [platform] [version]\n - platform: 'x86_64', 'arm'" >&2
|
||||
exit 1
|
||||
set -euo pipefail
|
||||
|
||||
platform="${1:-x86_64}"
|
||||
case "${platform}" in
|
||||
x86_64) arch="x86_64-unknown-linux-gnu" ;;
|
||||
arm) arch="aarch64-unknown-linux-gnu" ;;
|
||||
*) echo "Usage: build_release.sh [platform] (platform: 'x86_64' | 'arm')" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Setup build directory
|
||||
BASEDIR=$(cd $(dirname $0) && pwd)
|
||||
cd ${BASEDIR}
|
||||
cd ..
|
||||
# Repo root (this script lives in linux/).
|
||||
BASEDIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "${BASEDIR}/.."
|
||||
|
||||
# Setup platform argument
|
||||
[[ $2 == "x86_64" ]] && arch+=(x86_64-unknown-linux-gnu)
|
||||
[[ $2 == "arm" ]] && arch+=(aarch64-unknown-linux-gnu)
|
||||
# Portable, glibc-2.17 sidecar to embed (override with GOBLIN_NYM_UNIX_BIN).
|
||||
: "${GOBLIN_NYM_UNIX_BIN:=$(cd .. && pwd)/nym-dist/nym-socks5-client}"
|
||||
if [[ ! -x "${GOBLIN_NYM_UNIX_BIN}" ]]; then
|
||||
echo "error: sidecar to embed not found/executable: ${GOBLIN_NYM_UNIX_BIN}" >&2
|
||||
echo " set GOBLIN_NYM_UNIX_BIN to a built nym-socks5-client" >&2
|
||||
exit 1
|
||||
fi
|
||||
export GOBLIN_NYM_UNIX_BIN
|
||||
|
||||
rustup target add ${arch}
|
||||
cargo install cargo-zigbuild
|
||||
cargo zigbuild --release --target ${arch}
|
||||
rustup target add "${arch}"
|
||||
command -v cargo-zigbuild >/dev/null || cargo install cargo-zigbuild
|
||||
|
||||
# Create AppImage with https://github.com/AppImage/appimagetool
|
||||
cp target/${arch}/release/goblin linux/Goblin.AppDir/AppRun
|
||||
rm target/${arch}/release/*.AppImage
|
||||
appimagetool linux/Goblin.AppDir target/${arch}/release/goblin-v$2-linux-$1.AppImage
|
||||
# Portable cross-build to glibc 2.17. Two zig-specific fixes:
|
||||
# - CRoaring's AVX512 path won't compile under zig's clang (evex512 error).
|
||||
# - OpenSSL is vendored in Cargo.toml, so no system libssl is needed.
|
||||
export CFLAGS_x86_64_unknown_linux_gnu="-DCROARING_COMPILER_SUPPORTS_AVX512=0"
|
||||
export CXXFLAGS_x86_64_unknown_linux_gnu="-DCROARING_COMPILER_SUPPORTS_AVX512=0"
|
||||
cargo zigbuild --release --target "${arch}.2.17"
|
||||
|
||||
# Assemble the AppDir: AppRun IS the goblin binary (sidecar baked in), plus the
|
||||
# icon + desktop entry. No loose sidecar file.
|
||||
appdir="linux/Goblin.AppDir"
|
||||
cp "target/${arch}/release/goblin" "${appdir}/AppRun"
|
||||
chmod +x "${appdir}/AppRun"
|
||||
|
||||
out="target/${arch}/release/Goblin-${platform}.AppImage"
|
||||
rm -f "target/${arch}/release/"*.AppImage
|
||||
ARCH=x86_64 appimagetool "${appdir}" "${out}"
|
||||
echo "built: ${out}"
|
||||
|
||||
+29
-10
@@ -39,10 +39,18 @@ use super::{SOCKS5_HOST, SOCKS5_PORT};
|
||||
#[cfg(windows)]
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
|
||||
/// The Windows sidecar embedded into goblin.exe for single-file distribution.
|
||||
/// Present only when build.rs was given `GOBLIN_NYM_WIN_BIN` (release builds).
|
||||
/// The sidecar embedded into the goblin binary for single-file distribution.
|
||||
/// Present only when build.rs was given `GOBLIN_NYM_WIN_BIN` (Windows) or
|
||||
/// `GOBLIN_NYM_UNIX_BIN` (Linux/macOS) — i.e. release builds. Android never
|
||||
/// embeds (its sidecar rides in the APK's jniLibs).
|
||||
#[cfg(all(target_os = "windows", goblin_embed_nym))]
|
||||
const EMBEDDED_SIDECAR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/nym-socks5-client.exe"));
|
||||
#[cfg(all(
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "android"),
|
||||
goblin_embed_nym
|
||||
))]
|
||||
const EMBEDDED_SIDECAR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/nym-socks5-client"));
|
||||
|
||||
/// Bundled SOCKS5 client binary name. Windows release archives ship the `.exe`;
|
||||
/// `Command`/`current_exe().parent().join(..)` need the suffix to find it. On
|
||||
@@ -117,10 +125,10 @@ fn binary_path() -> PathBuf {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
// Windows single-file build: the sidecar is baked into goblin.exe — extract
|
||||
// it once to %LOCALAPPDATA%\Goblin and run that. Falls through to a sibling
|
||||
// .exe when not embedded (a plain `cargo build`).
|
||||
#[cfg(all(target_os = "windows", goblin_embed_nym))]
|
||||
// Single-file desktop build: the sidecar is baked into the goblin binary —
|
||||
// extract it once to the per-user data dir and run that. Falls through to a
|
||||
// sibling binary when not embedded (a plain `cargo build`).
|
||||
#[cfg(all(not(target_os = "android"), goblin_embed_nym))]
|
||||
if let Some(p) = extract_embedded_sidecar() {
|
||||
return p;
|
||||
}
|
||||
@@ -143,10 +151,11 @@ fn provider() -> String {
|
||||
.unwrap_or_else(|| NETWORK_REQUESTER.to_string())
|
||||
}
|
||||
|
||||
/// Write the embedded Windows sidecar to `%LOCALAPPDATA%\Goblin` (once, or when
|
||||
/// the bundled copy changed) and return its path. Keeps the release a single
|
||||
/// `goblin.exe` with no loose helper to misplace.
|
||||
#[cfg(all(target_os = "windows", goblin_embed_nym))]
|
||||
/// Write the embedded sidecar to the per-user data dir (`%LOCALAPPDATA%\Goblin`
|
||||
/// on Windows, `~/.local/share/Goblin` on Linux) once, or when the bundled copy
|
||||
/// changed, and return its path. Keeps the release a single self-contained
|
||||
/// binary with no loose helper to misplace.
|
||||
#[cfg(all(not(target_os = "android"), goblin_embed_nym))]
|
||||
fn extract_embedded_sidecar() -> Option<PathBuf> {
|
||||
let dir = dirs::data_local_dir()?.join("Goblin");
|
||||
let _ = std::fs::create_dir_all(&dir);
|
||||
@@ -160,6 +169,16 @@ fn extract_embedded_sidecar() -> Option<PathBuf> {
|
||||
warn!("nym: could not extract embedded sidecar: {e}");
|
||||
return None;
|
||||
}
|
||||
// Unix: a freshly written file isn't executable; the sidecar must be.
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if let Err(e) = std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755))
|
||||
{
|
||||
warn!("nym: could not mark embedded sidecar executable: {e}");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user