From cfc0421d17a819d422559b33b2ad5de7be22f400 Mon Sep 17 00:00:00 2001 From: 7elia <176603882+7elia@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:42:41 +0200 Subject: [PATCH] feat(nix): start mac support --- crates/extendify-native/src/unix/dyld.rs | 47 +++++++++++++++++++ crates/extendify-native/src/unix/mod.rs | 5 ++ flake.nix | 54 +++++++++++++++------- nix/cef.nix | 50 ++++++++++++++++++-- nix/native.nix | 6 ++- nix/spotify-extendify.nix | 59 +++++++++++++++--------- 6 files changed, 177 insertions(+), 44 deletions(-) create mode 100644 crates/extendify-native/src/unix/dyld.rs diff --git a/crates/extendify-native/src/unix/dyld.rs b/crates/extendify-native/src/unix/dyld.rs new file mode 100644 index 0000000..5ddede9 --- /dev/null +++ b/crates/extendify-native/src/unix/dyld.rs @@ -0,0 +1,47 @@ +#[allow(unused_macros)] +macro_rules! extern_c_overrides { + (unsafe fn $c_api:ident/$real_api:ident($($param_name:ident : $param_type:ty),*) -> $return_type:ty $override_body:block $($more_tokens:tt)*) => { + + pub unsafe fn $real_api($($param_name: $param_type),*) -> $return_type { + use std::ffi::{c_char, c_void}; + use std::sync::OnceLock; + + unsafe extern "C" { + fn dlsym(handle: *const c_void, symbol: *const c_char) -> *const c_void; + } + + #[allow(non_camel_case_types)] + type $c_api = unsafe fn($($param_type),*) -> $return_type; + + #[allow(non_upper_case_globals)] + static _dl_resolver: OnceLock<$c_api> = OnceLock::new(); + + let real_fn = _dl_resolver.get_or_init(|| { + unsafe { + const RTLD_NEXT: *const c_void = -1isize as *const c_void; + let sym = dlsym( + RTLD_NEXT, + concat!(stringify!($c_api), "\0").as_ptr() as *const c_char, + ); + if sym.is_null() { + panic!("dlsym(RTLD_NEXT): cannot find {}", stringify!($c_api)); + } + core::mem::transmute(sym) + } + }); + + unsafe { real_fn($($param_name),*) } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn $c_api($($param_name: $param_type),*) -> $return_type { + unsafe { + let $c_api = $real_api; + $override_body + } + } + + extern_c_overrides! { $($more_tokens)* } + }; + () => {}; +} diff --git a/crates/extendify-native/src/unix/mod.rs b/crates/extendify-native/src/unix/mod.rs index b92e310..4682e2a 100644 --- a/crates/extendify-native/src/unix/mod.rs +++ b/crates/extendify-native/src/unix/mod.rs @@ -8,8 +8,13 @@ use crate::vtable_hooks::GET_REQ_HANDLER_OG; use crate::{log, vtable_hooks}; #[macro_use] +#[cfg(target_os = "linux")] mod preload; +#[macro_use] +#[cfg(target_os = "macos")] +mod dyld; + extern_c_overrides! { unsafe fn cef_browser_view_create/real_cef_browser_view_create( client: *mut _cef_client_t, diff --git a/flake.nix b/flake.nix index 0535b04..2353843 100644 --- a/flake.nix +++ b/flake.nix @@ -9,24 +9,46 @@ self, nixpkgs, }: let - # Spotify only ships on this so it's fine - system = "x86_64-linux"; - pkgs = import nixpkgs { - inherit system; - config.allowUnfree = true; - }; + systems = [ + "x86_64-linux" + "aarch64-darwin" + "x86_64-darwin" + ]; + forEachSystem = nixpkgs.lib.genAttrs systems; in { - devShells.${system} = { - default = pkgs.callPackage ./nix/shell.nix {inherit self;}; - }; + devShells = forEachSystem ( + system: let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + }; + in { + default = pkgs.callPackage ./nix/shell.nix {inherit self;}; + } + ); - packages.${system} = { - cef = pkgs.callPackage ./nix/cef.nix {}; - extendify-native = pkgs.callPackage ./nix/native.nix {inherit self;}; - spotify-extendify = pkgs.callPackage ./nix/spotify-extendify.nix {inherit self;}; - default = self.packages.${system}.spotify-extendify; - }; + packages = forEachSystem ( + system: let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + }; + in { + cef = pkgs.callPackage ./nix/cef.nix {}; + extendify-native = pkgs.callPackage ./nix/native.nix {inherit self;}; + spotify-extendify = pkgs.callPackage ./nix/spotify-extendify.nix {inherit self;}; + default = self.packages.${system}.spotify-extendify; + } + ); - formatter.${system} = pkgs.callPackage ./nix/fmt.nix {}; + formatter = forEachSystem ( + system: let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + }; + in + pkgs.callPackage ./nix/fmt.nix {} + ); }; } diff --git a/nix/cef.nix b/nix/cef.nix index 2e84727..d7d0352 100644 --- a/nix/cef.nix +++ b/nix/cef.nix @@ -1,10 +1,52 @@ { cef-binary, + fetchurl, stdenv, -}: -cef-binary.override { + stdenvNoCC, +}: let + selectSystem = attrs: + attrs.${stdenv.hostPlatform.system} or (throw "Unsupported system ${stdenv.hostPlatform.system}"); + + platformString = selectSystem { + aarch64-linux = "linuxarm64"; + x86_64-linux = "linux64"; + aarch64-darwin = "macosarm64"; + x86_64-darwin = "macosx64"; + }; + version = "144.0.11"; gitRevision = "e135be2"; chromiumVersion = "144.0.7559.97"; - srcHashes.${stdenv.hostPlatform.system} = "sha256-almKJ18g//Wi2CDRX1yibODvR6TF0DlUl1EnDNlIHBY="; -} + srcHashes = { + x86_64-linux = "sha256-almKJ18g//Wi2CDRX1yibODvR6TF0DlUl1EnDNlIHBY="; + aarch64-darwin = "sha256-Q94fht0yAkwAIqv29I1ZkpaS7WiYvJIcHWFcmBSvqHw="; + }; +in + if stdenv.hostPlatform.isLinux + then + cef-binary.override { + inherit version gitRevision chromiumVersion srcHashes; + } + else if stdenv.hostPlatform.isDarwin + then + stdenvNoCC.mkDerivation { + name = "darwin-cef"; + + src = fetchurl { + url = "https://cef-builds.spotifycdn.com/cef_binary_${version}+g${gitRevision}+chromium-${chromiumVersion}_${platformString}_minimal.tar.bz2"; + hash = selectSystem srcHashes; + }; + + dontBuild = true; + dontConfigure = true; + + unpackPhase = '' + tar --strip-components=1 -xjf $src + ''; + + installPhase = '' + mkdir -p $out + cp -r ./* $out/ + ''; + } + else throw "Unsupported system: ${stdenv.hostPlatform.system}" diff --git a/nix/native.nix b/nix/native.nix index 2ece199..2401710 100644 --- a/nix/native.nix +++ b/nix/native.nix @@ -45,6 +45,10 @@ rustPlatform.buildRustPackage { # wtf is the license # license = lib.licenses.gpl3; maintainers = [lib.maintainers.fazzi]; - platforms = ["x86_64-linux"]; + platforms = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; }; } diff --git a/nix/spotify-extendify.nix b/nix/spotify-extendify.nix index c9b0bcc..c9863e8 100644 --- a/nix/spotify-extendify.nix +++ b/nix/spotify-extendify.nix @@ -5,30 +5,43 @@ makeWrapper, self, lib, -}: -symlinkJoin { - name = "spotify-extendify"; - inherit (spotify) version; +}: let + exe = + if stdenv.isDarwin + then "Applications/Spotify.app/MacOS/Spotify" + else "bin/spotify"; + preloadVar = + if stdenv.isDarwin + then "DYLD_INSERT_LIBRARIES" + else "LD_PRELOAD"; +in + symlinkJoin { + name = "spotify-extendify"; + inherit (spotify) version; - paths = [ - spotify - self.packages.${stdenv.hostPlatform.system}.extendify-native - ]; + paths = [ + spotify + self.packages.${stdenv.hostPlatform.system}.extendify-native + ]; - buildInputs = [makeWrapper]; + buildInputs = [makeWrapper]; - postBuild = '' - wrapProgram $out/bin/spotify \ - --set LD_PRELOAD $out/lib/libextendify_native.so - ''; + postBuild = '' + wrapProgram $out/${exe} \ + --set ${preloadVar} $out/lib/libextendify_native.so + ''; - meta = { - mainProgram = "spotify"; - description = spotify.meta.description + "(patched with Extendify)"; - homepage = "https://github.com/extendify-mod/extendify"; - # wtf is the license - # license = lib.licenses.gpl3; - maintainers = [lib.maintainers.fazzi]; - platforms = ["x86_64-linux"]; - }; -} + meta = { + mainProgram = "spotify"; + description = spotify.meta.description + "(patched with Extendify)"; + homepage = "https://github.com/extendify-mod/extendify"; + # wtf is the license + # license = lib.licenses.gpl3; + maintainers = [lib.maintainers.fazzi]; + platforms = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + }; + }