mirror of
https://github.com/sonorahq/sonora.git
synced 2026-10-28 08:55:04 +08:00
483 lines
18 KiB
Nix
483 lines
18 KiB
Nix
{
|
|
description = "Sonora - a native music streaming client";
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
rust-overlay = {
|
|
url = "github:oxalica/rust-overlay";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
};
|
|
|
|
outputs =
|
|
{
|
|
self,
|
|
nixpkgs,
|
|
rust-overlay,
|
|
...
|
|
}:
|
|
let
|
|
inherit (nixpkgs) lib;
|
|
|
|
forEachSystem =
|
|
fn:
|
|
lib.genAttrs lib.systems.flakeExposed (
|
|
system:
|
|
let
|
|
pkgs = import nixpkgs {
|
|
inherit system;
|
|
overlays = [ (import rust-overlay) ];
|
|
};
|
|
in
|
|
fn pkgs
|
|
);
|
|
|
|
release = {
|
|
version = "0.45.0";
|
|
assets = {
|
|
x86_64-linux = {
|
|
target = "x86_64-unknown-linux-gnu";
|
|
hash = "sha256-kfErnlqzs95uGgvr2GXC+HpjoRA1qCbXYdITNl88xwA=";
|
|
};
|
|
aarch64-linux = {
|
|
target = "aarch64-unknown-linux-gnu";
|
|
hash = "sha256-oxzuSZ5+FNT0xpWATtSmZ0Mfo9CDayFn8tYLAjKiLkQ=";
|
|
};
|
|
aarch64-darwin = {
|
|
target = "macos";
|
|
hash = "sha256-zkSVNFIYeu1VBOOV8qkCtj7jlYD3jzrxfMF9c7Fhy0E=";
|
|
};
|
|
};
|
|
};
|
|
|
|
# The nightly prerelease, in the same shape as `release` plus its tag. Only the commit the
|
|
# nightly workflow tags `nightly` carries this file, so `github:sonorahq/sonora/nightly` is
|
|
# the one flake ref that installs it.
|
|
nightly = lib.optionalAttrs (builtins.pathExists ./nix/nightly.json) (
|
|
lib.importJSON ./nix/nightly.json
|
|
);
|
|
in
|
|
{
|
|
packages = forEachSystem (
|
|
pkgs:
|
|
let
|
|
runtimeLibraries =
|
|
with pkgs;
|
|
if pkgs.stdenv.hostPlatform.isLinux then
|
|
[
|
|
vulkan-loader
|
|
wayland
|
|
libxkbcommon
|
|
libxcb
|
|
libx11
|
|
libxcursor
|
|
libxi
|
|
fontconfig
|
|
freetype
|
|
alsa-lib
|
|
dbus
|
|
sqlite
|
|
webkitgtk_4_1
|
|
glib-networking
|
|
]
|
|
else
|
|
[ ];
|
|
|
|
inherit (pkgs.stdenv.hostPlatform) system;
|
|
|
|
# WebKit plays a page's media through GStreamer and aborts its web process when no
|
|
# audio sink element exists. The Nix webkitgtk closure carries only core and base, and
|
|
# autoaudiosink lives in good, so the plugin path has to name all three.
|
|
gstPluginPath = pkgs.lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (
|
|
with pkgs.gst_all_1;
|
|
[
|
|
gstreamer
|
|
gst-plugins-base
|
|
gst-plugins-good
|
|
]
|
|
);
|
|
|
|
alsaPluginDirectory = pkgs.symlinkJoin {
|
|
name = "sonora-alsa-plugins";
|
|
paths = [
|
|
"${pkgs.pipewire}/lib/alsa-lib"
|
|
"${pkgs.alsa-plugins}/lib/alsa-lib"
|
|
];
|
|
};
|
|
|
|
# The source build uses the toolchain the devShell and CI pin, not whatever rustc
|
|
# nixpkgs carries that day.
|
|
rustPlatform =
|
|
let
|
|
toolchain =
|
|
pkgs.rust-bin.stable.${(lib.importTOML ./rust-toolchain.toml).toolchain.channel}.minimal;
|
|
in
|
|
pkgs.makeRustPlatform {
|
|
cargo = toolchain;
|
|
rustc = toolchain;
|
|
};
|
|
|
|
# Points the Linux binary at its runtime libraries and environment. Both packages run
|
|
# it, since webkitgtk and the GPU and audio stacks are opened with dlopen and never
|
|
# show up as link-time dependencies.
|
|
linuxFixup = lib.optionalString pkgs.stdenv.hostPlatform.isLinux ''
|
|
patchelf \
|
|
--set-interpreter "${pkgs.stdenv.cc.bintools.dynamicLinker}" \
|
|
--add-rpath "${lib.makeLibraryPath (runtimeLibraries ++ [ pkgs.stdenv.cc.cc.lib ])}" \
|
|
"$out/bin/sonora"
|
|
wrapProgram "$out/bin/sonora" \
|
|
--set ALSA_PLUGIN_DIR ${alsaPluginDirectory} \
|
|
--prefix GIO_EXTRA_MODULES : ${pkgs.glib-networking}/lib/gio/modules \
|
|
--prefix GST_PLUGIN_SYSTEM_PATH_1_0 : ${gstPluginPath}
|
|
'';
|
|
|
|
sonora-src = rustPlatform.buildRustPackage (final: {
|
|
pname = "sonora";
|
|
inherit ((lib.importTOML (final.src + /Cargo.toml)).workspace.package) version;
|
|
|
|
src = ./.;
|
|
cargoLock = {
|
|
lockFile = final.src + /Cargo.lock;
|
|
allowBuiltinFetchGit = true;
|
|
};
|
|
cargoBuildFlags = [
|
|
"--package"
|
|
"sonora"
|
|
];
|
|
doCheck = false;
|
|
|
|
nativeBuildInputs =
|
|
with pkgs;
|
|
lib.flatten [
|
|
cmake
|
|
pkg-config
|
|
(lib.optionals stdenv.hostPlatform.isLinux [
|
|
makeWrapper
|
|
mold
|
|
])
|
|
(lib.optionals stdenv.hostPlatform.isDarwin [
|
|
(
|
|
let
|
|
pkgs' = import nixpkgs {
|
|
inherit (stdenv.hostPlatform) system;
|
|
config.allowUnfree = true;
|
|
};
|
|
in
|
|
runCommandLocal "metal-shader-compiler" { } ''
|
|
mkdir -p "$out/bin"
|
|
ln -s ${pkgs'.darwin.xcode}/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/metal "$out/bin/metal"
|
|
ln -s ${pkgs'.darwin.xcode}/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/metallib "$out/bin/metallib"
|
|
''
|
|
)
|
|
])
|
|
];
|
|
buildInputs =
|
|
with pkgs;
|
|
lib.flatten [
|
|
sqlite
|
|
(lib.optionals stdenv.hostPlatform.isLinux [
|
|
dbus
|
|
fontconfig
|
|
libxcb
|
|
libxkbcommon
|
|
libx11
|
|
pipewire
|
|
stdenv.cc.cc.lib
|
|
(alsa-lib-with-plugins.override {
|
|
plugins = [
|
|
alsa-plugins
|
|
pipewire
|
|
];
|
|
})
|
|
])
|
|
(lib.optionals stdenv.hostPlatform.isDarwin [
|
|
apple-sdk_15
|
|
(darwinMinVersionHook "10.15")
|
|
])
|
|
];
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
|
|
install -Dm755 "target/${pkgs.stdenv.hostPlatform.rust.cargoShortTarget}/$cargoBuildType/sonora" "$out/bin/sonora"
|
|
${
|
|
if pkgs.stdenv.hostPlatform.isDarwin then
|
|
''
|
|
install -Dm755 "target/${pkgs.stdenv.hostPlatform.rust.cargoShortTarget}/$cargoBuildType/sonora" "$out/Applications/Sonora.app/Contents/MacOS/sonora"
|
|
install -Dm444 "$src/assets/macos/sonora.icns" \
|
|
"$out/Applications/Sonora.app/Contents/Resources/sonora.icns"
|
|
|
|
sed \
|
|
"s/@VERSION@/$version/g" \
|
|
"$src/assets/macos/Info.plist" \
|
|
> "$out/Applications/Sonora.app/Contents/Info.plist"
|
|
|
|
LICENSE_DIR="$out/Applications/Sonora.app/Contents/Resources"
|
|
''
|
|
else
|
|
''
|
|
install -Dm444 "$src/assets/linux/sonora.desktop" \
|
|
"$out/share/applications/sonora.desktop"
|
|
install -Dm444 "$src/assets/linux/sonora.svg" \
|
|
"$out/share/icons/hicolor/scalable/apps/sonora.svg"
|
|
for icon in "$src"/assets/linux/icons/hicolor/*/apps/sonora.png; do
|
|
size="$(basename "$(dirname "$(dirname "$icon")")")"
|
|
install -Dm444 "$icon" \
|
|
"$out/share/icons/hicolor/$size/apps/sonora.png"
|
|
done
|
|
|
|
LICENSE_DIR="$out/share/licenses/sonora"
|
|
''
|
|
}
|
|
install -Dm444 "$src/COPYING" "$LICENSE_DIR/LICENSE"
|
|
install -Dm444 "$src/THIRD-PARTY.md" "$LICENSE_DIR/THIRD-PARTY.md"
|
|
install -Dm444 "$src/assets/fonts/LICENSE.txt" \
|
|
"$LICENSE_DIR/sonora/LICENSE.Inter"
|
|
for licence in "$src/assets/icons"/*/LICENSE; do
|
|
pack="$(basename "$(dirname "$licence")")"
|
|
install -Dm444 "$licence" \
|
|
"$LICENSE_DIR/icons/LICENSE.$pack"
|
|
done
|
|
install -Dm444 "$src/assets/icons/LICENSE" \
|
|
"$LICENSE_DIR/icons/LICENSE"
|
|
|
|
runHook postInstall
|
|
'';
|
|
|
|
postFixup = linuxFixup;
|
|
|
|
inherit meta;
|
|
});
|
|
|
|
meta = {
|
|
description = "A native music streaming client, built with Rust and GPUI";
|
|
mainProgram = "sonora";
|
|
license = with lib.licenses; [
|
|
gpl3Plus
|
|
ofl
|
|
isc
|
|
];
|
|
platforms = lib.platforms.linux ++ lib.platforms.darwin;
|
|
};
|
|
|
|
# Installs the binaries a GitHub release publishes under `tag`. Every asset is named
|
|
# `sonora-<tag>-<target>`, and `assets` maps a Nix system to its target and hash.
|
|
prebuilt =
|
|
{
|
|
pname,
|
|
version,
|
|
tag,
|
|
assets,
|
|
}:
|
|
let
|
|
asset = assets.${system};
|
|
in
|
|
pkgs.stdenv.mkDerivation {
|
|
inherit pname version meta;
|
|
|
|
src = pkgs.fetchurl {
|
|
url = "https://github.com/sonorahq/sonora/releases/download/${tag}/sonora-${tag}-${asset.target}${
|
|
if pkgs.stdenv.hostPlatform.isDarwin then ".dmg" else ""
|
|
}";
|
|
inherit (asset) hash;
|
|
};
|
|
|
|
dontUnpack = true;
|
|
dontStrip = true;
|
|
|
|
nativeBuildInputs =
|
|
lib.optionals pkgs.stdenv.hostPlatform.isLinux [ pkgs.makeWrapper ]
|
|
++ lib.optionals pkgs.stdenv.hostPlatform.isDarwin [
|
|
pkgs.makeBinaryWrapper
|
|
pkgs.undmg
|
|
];
|
|
|
|
installPhase =
|
|
if pkgs.stdenv.hostPlatform.isLinux then
|
|
''
|
|
runHook preInstall
|
|
install -Dm755 "$src" "$out/bin/sonora"
|
|
install -Dm444 ${./assets/linux/sonora.desktop} \
|
|
"$out/share/applications/sonora.desktop"
|
|
install -Dm444 ${./assets/linux/sonora.svg} \
|
|
"$out/share/icons/hicolor/scalable/apps/sonora.svg"
|
|
for icon in ${./assets/linux/icons}/hicolor/*/apps/sonora.png; do
|
|
size="$(basename "$(dirname "$(dirname "$icon")")")"
|
|
install -Dm444 "$icon" \
|
|
"$out/share/icons/hicolor/$size/apps/sonora.png"
|
|
done
|
|
install -Dm444 ${./COPYING} "$out/share/licenses/sonora/LICENSE"
|
|
install -Dm444 ${./THIRD-PARTY.md} "$out/share/licenses/sonora/THIRD-PARTY.md"
|
|
install -Dm444 ${./assets/fonts/LICENSE.txt} \
|
|
"$out/share/licenses/sonora/LICENSE.Inter"
|
|
for licence in ${./assets/icons}/*/LICENSE; do
|
|
pack="$(basename "$(dirname "$licence")")"
|
|
install -Dm444 "$licence" \
|
|
"$out/share/licenses/sonora/icons/LICENSE.$pack"
|
|
done
|
|
install -Dm444 ${./assets/icons/LICENSE} \
|
|
"$out/share/licenses/sonora/icons/LICENSE"
|
|
runHook postInstall
|
|
''
|
|
else
|
|
''
|
|
runHook preInstall
|
|
mnt="$(mktemp -d)"
|
|
/usr/bin/hdiutil attach -readonly -nobrowse -mountpoint "$mnt" "$src"
|
|
mkdir -p "$out/Applications" "$out/bin"
|
|
cp -R "$mnt/Sonora.app" "$out/Applications/Sonora.app"
|
|
/usr/bin/hdiutil detach "$mnt"
|
|
makeBinaryWrapper \
|
|
"$out/Applications/Sonora.app/Contents/MacOS/sonora" \
|
|
"$out/bin/sonora"
|
|
runHook postInstall
|
|
'';
|
|
|
|
postFixup = linuxFixup;
|
|
};
|
|
|
|
sonora-bin = prebuilt {
|
|
pname = "sonora-bin";
|
|
inherit (release) version assets;
|
|
tag = "v${release.version}";
|
|
};
|
|
|
|
sonora-nightly = prebuilt {
|
|
pname = "sonora-nightly";
|
|
inherit (nightly) version tag assets;
|
|
};
|
|
in
|
|
# `default` and `sonora` resolve to the release binary wherever one is published and to
|
|
# the source build elsewhere. `sonora-src` and `sonora-bin` always mean one or the other.
|
|
# At the `nightly` tag both resolve to the nightly binary instead.
|
|
{
|
|
inherit sonora-src;
|
|
sonora = sonora-src;
|
|
default = sonora-src;
|
|
}
|
|
// lib.optionalAttrs (builtins.hasAttr system release.assets) {
|
|
inherit sonora-bin;
|
|
sonora = sonora-bin;
|
|
default = sonora-bin;
|
|
}
|
|
// lib.optionalAttrs (nightly ? assets && builtins.hasAttr system nightly.assets) {
|
|
inherit sonora-nightly;
|
|
sonora = sonora-nightly;
|
|
default = sonora-nightly;
|
|
}
|
|
);
|
|
|
|
devShells = forEachSystem (
|
|
pkgs:
|
|
let
|
|
rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
|
|
runtimeLibraries =
|
|
with pkgs;
|
|
if pkgs.stdenv.hostPlatform.isLinux then
|
|
[
|
|
vulkan-loader
|
|
wayland
|
|
libxkbcommon
|
|
libxcb
|
|
libx11
|
|
libxcursor
|
|
libxi
|
|
fontconfig
|
|
freetype
|
|
alsa-lib
|
|
dbus
|
|
sqlite
|
|
openssl
|
|
webkitgtk_4_1
|
|
glib-networking
|
|
]
|
|
else
|
|
[ ];
|
|
# Apple's own xcrun, so `xcrun metal` can reach the Metal toolchain that
|
|
# Xcode 26 mounts outside DEVELOPER_DIR. The xcbuild shim the Apple SDK
|
|
# drags onto PATH cannot, and neither can any xcrun pointed at the Nix SDK.
|
|
xcodeXcrun = pkgs.runCommandLocal "xcode-xcrun" { } ''
|
|
mkdir -p $out/bin
|
|
ln -s /usr/bin/xcrun $out/bin/xcrun
|
|
'';
|
|
in
|
|
{
|
|
default = pkgs.mkShell {
|
|
nativeBuildInputs =
|
|
(with pkgs; [
|
|
pkg-config
|
|
cmake
|
|
rustToolchain
|
|
sccache
|
|
])
|
|
++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ pkgs.mold ];
|
|
|
|
buildInputs = runtimeLibraries;
|
|
|
|
LD_LIBRARY_PATH = lib.makeLibraryPath runtimeLibraries;
|
|
|
|
ALSA_PLUGIN_DIR =
|
|
if pkgs.stdenv.hostPlatform.isLinux then
|
|
"${pkgs.symlinkJoin {
|
|
name = "alsa-plugins-combined";
|
|
paths = [
|
|
"${pkgs.alsa-plugins}/lib/alsa-lib"
|
|
"${pkgs.pipewire}/lib/alsa-lib"
|
|
];
|
|
}}"
|
|
else
|
|
"";
|
|
|
|
shellHook =
|
|
# WebKit composites through EGL, which has to find the same drivers.
|
|
pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isLinux ''
|
|
if [ ! -d /run/opengl-driver ]; then
|
|
export VK_DRIVER_FILES="${pkgs.mesa}/share/vulkan/icd.d"
|
|
export VK_IMPLICIT_LAYER_PATH="${pkgs.mesa}/share/vulkan/implicit_layer.d"
|
|
export __EGL_VENDOR_LIBRARY_DIRS="${pkgs.mesa}/share/glvnd/egl_vendor.d"
|
|
export LIBGL_DRIVERS_PATH="${pkgs.mesa}/lib/dri"
|
|
export GBM_BACKENDS_PATH="${pkgs.mesa}/lib/gbm"
|
|
fi
|
|
export GIO_EXTRA_MODULES="${pkgs.glib-networking}/lib/gio/modules''${GIO_EXTRA_MODULES:+:$GIO_EXTRA_MODULES}"
|
|
export GST_PLUGIN_SYSTEM_PATH_1_0="${
|
|
pkgs.lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (
|
|
with pkgs.gst_all_1;
|
|
[
|
|
gstreamer
|
|
gst-plugins-base
|
|
gst-plugins-good
|
|
]
|
|
)
|
|
}''${GST_PLUGIN_SYSTEM_PATH_1_0:+:$GST_PLUGIN_SYSTEM_PATH_1_0}"
|
|
''
|
|
# gpui_apple compiles its shaders with `xcrun -sdk macosx metal` at build
|
|
# time. The Nix Apple SDK has no Metal toolchain, so hand xcrun back to the
|
|
# installed Xcode. Clang follows DEVELOPER_DIR too and then reads Xcode's C
|
|
# headers beside the Nix libc++, which breaks any C++ a build script compiles,
|
|
# so C++ compiles are pinned to the Nix SDK through CXXFLAGS. Only compiles: the
|
|
# link still has to see Xcode's SDK, which is where libsqlite3 comes from.
|
|
+ lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
|
|
# xcode-select echoes DEVELOPER_DIR back when it is set, so ask with it unset.
|
|
if xcode="$(env -u DEVELOPER_DIR /usr/bin/xcode-select -p 2>/dev/null)"; then
|
|
export DEVELOPER_DIR="$xcode"
|
|
export PATH="${xcodeXcrun}/bin:$PATH"
|
|
export CXXFLAGS="''${CXXFLAGS:-} -isysroot $SDKROOT"
|
|
fi
|
|
'';
|
|
};
|
|
}
|
|
);
|
|
|
|
overlays.default = final: _prev: {
|
|
sonora = self.packages.${final.stdenv.hostPlatform.system}.default;
|
|
};
|
|
|
|
homeManagerModules = {
|
|
default = import ./nix/modules/hm-module.nix self;
|
|
sonora = import ./nix/modules/hm-module.nix self;
|
|
};
|
|
|
|
homeModules = self.homeManagerModules;
|
|
};
|
|
}
|