dotfiles/pkgs/spacedrive/nix/default.nix
Daniel Bulant 651fe01e78
tuwunel
2026-08-05 22:34:09 +02:00

410 lines
12 KiB
Nix

{
pkgs,
craneLib,
upstreamRoot,
spacebotRoot,
frontendSrc,
daemonRustSrc,
desktopRustSrc,
cliRustSrc,
}:
let
inherit (pkgs) lib stdenv;
targetTriple = stdenv.hostPlatform.config;
pdfiumVersion = "6666";
archiveToolPackages = with pkgs; [
unzip
gnutar
gzip
bzip2
xz
p7zip
];
daemonRuntimePath = lib.makeBinPath ([ pkgs.ffmpeg_7.bin ] ++ archiveToolPackages);
gstreamerPackages = with pkgs.gst_all_1; [
gstreamer
gst-plugins-base
gst-plugins-good
gst-plugins-bad
gst-plugins-ugly
gst-libav
];
gstreamerPluginPath = lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" gstreamerPackages;
tauriCargoToml = builtins.fromTOML (
builtins.readFile "${upstreamRoot}/apps/tauri/src-tauri/Cargo.toml"
);
version = tauriCargoToml.package.version;
pdfiumUrl =
if stdenv.hostPlatform.isx86_64 then
"https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F${pdfiumVersion}/pdfium-linux-x64.tgz"
else if stdenv.hostPlatform.isAarch64 then
"https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F${pdfiumVersion}/pdfium-linux-arm64.tgz"
else
throw "Unsupported platform for pdfium: ${stdenv.hostPlatform.system}";
pdfiumHash =
if stdenv.hostPlatform.isx86_64 then
"sha256-stUhbZ1ZfqxqpV1eFTEMd+F0xXVP2YE3sqPh6J7yd7I="
else if stdenv.hostPlatform.isAarch64 then
"sha256-cydMLWhrN5C6WxUw6HLpl/51S05QBG/+SudvO/6MNZM="
else
throw "Unsupported platform for pdfium: ${stdenv.hostPlatform.system}";
pdfium = stdenv.mkDerivation {
pname = "pdfium";
version = pdfiumVersion;
src = pkgs.fetchurl {
url = pdfiumUrl;
hash = pdfiumHash;
};
sourceRoot = ".";
dontConfigure = true;
dontBuild = true;
installPhase = ''
runHook preInstall
mkdir -p $out/lib $out/include
cp lib/libpdfium.so $out/lib/
cp -r include/. $out/include/
runHook postInstall
'';
};
bunNix = pkgs.runCommandLocal "spacedrive-bun.nix" { } ''
substitute ${./bun.nix} $out --replace-fail __ROOT__ ${frontendSrc}
'';
frontend = stdenv.mkDerivation {
pname = "spacedrive-frontend";
inherit version;
src = frontendSrc;
nativeBuildInputs = [ pkgs.bun2nix.hook ];
bunDeps = pkgs.bun2nix.fetchBunDeps {
bunNix = bunNix;
};
dontRunLifecycleScripts = true;
dontConfigure = true;
postPatch = ''
cp ${../bun.lock} bun.lock
ln -s ${spacebotRoot} ../spacebot
substituteInPlace apps/tauri/src/contextMenu.ts \
--replace-fail \
"import { Menu, MenuItem, Submenu, PredefinedMenuItem } from '@tauri-apps/api/menu';" \
"import { LogicalPosition } from '@tauri-apps/api/dpi'; import { Menu, MenuItem, Submenu, PredefinedMenuItem } from '@tauri-apps/api/menu';" \
--replace-fail \
'await menu.popup();' \
'await menu.popup(new LogicalPosition(position.x, position.y));'
# The sidebar is z-[65]. Radix portals default to z-50, which puts the
# space switcher menu behind the translucent sidebar stacking context.
# Radix copies the content z-index to its popper wrapper, so raising the
# content also fixes the compositor order of the wrapper.
substituteInPlace packages/interface/src/components/SpacesSidebar/SpaceSwitcher.tsx \
--replace-fail \
'className="min-w-[var(--radix-dropdown-menu-trigger-width)] p-1"' \
'className="z-[100] min-w-[var(--radix-dropdown-menu-trigger-width)] p-1"'
# The published primitives package uses these classes for its dialog
# overlay and content, but Tailwind does not scan the package through its
# node_modules symlink. Define the missing utilities explicitly.
substituteInPlace apps/tauri/src/index.css \
--replace-fail \
'@source "../../../../spaceui/packages/primitives/src";' \
'@source "../../../../spaceui/packages/primitives/src";
/* Utilities referenced by @spacedrive/primitives but absent from this build. */
.z-\[102\] { z-index: 102; }
.z-\[103\] { z-index: 103; }'
'';
buildPhase = ''
runHook preBuild
bun run --filter @sd/tauri build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r apps/tauri/dist/. $out/
runHook postInstall
'';
};
commonNativeBuildInputs = with pkgs; [
cmake
pkg-config
protobuf
];
commonBuildInputs = with pkgs; [
openssl
];
daemonBuildInputs = commonBuildInputs ++ [
pkgs.ffmpeg_7
];
daemonNativeBuildInputs = commonNativeBuildInputs ++ [
pkgs.clang
pkgs.llvmPackages.libclang
];
desktopBuildInputs = commonBuildInputs ++ [
pkgs.dbus
pkgs.glib
pkgs.glib-networking
pkgs.gst_all_1.gstreamer
pkgs.gst_all_1.gst-libav
pkgs.gst_all_1.gst-plugins-bad
pkgs.gst_all_1.gst-plugins-base
pkgs.gst_all_1.gst-plugins-good
pkgs.gst_all_1.gst-plugins-ugly
pkgs.gtk3
pkgs.libayatana-appindicator
pkgs.libsoup_3
pkgs.webkitgtk_4_1
];
commonArgs = {
inherit version;
strictDeps = true;
doCheck = false;
CARGO_PROFILE_RELEASE_CODEGEN_UNITS = "256";
CARGO_PROFILE_RELEASE_LTO = "off";
nativeBuildInputs = commonNativeBuildInputs;
buildInputs = commonBuildInputs;
};
rust197PostPatch = ''
substituteInPlace crates/task-system/src/system.rs \
--replace-fail '.fetch_update(' '.try_update('
'';
daemonCargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// {
pname = "spacedrive-cargo-artifacts";
src = daemonRustSrc;
cargoExtraArgs = "--package sd-core --bin sd-daemon --features ffmpeg";
buildInputs = daemonBuildInputs;
nativeBuildInputs = daemonNativeBuildInputs;
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.ffmpeg_7.dev}/include";
FFMPEG_DIR = "${pkgs.ffmpeg_7.dev}";
}
);
cliCargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// {
pname = "sd-cli-cargo-artifacts";
src = cliRustSrc;
cargoExtraArgs = "--package sd-cli --bin sd-cli";
}
);
desktopCargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// {
pname = "spacedrive-desktop-cargo-artifacts";
src = desktopRustSrc;
cargoExtraArgs = "--package spacedrive --bin Spacedrive";
buildInputs = desktopBuildInputs;
}
);
sd-daemon = craneLib.buildPackage (
commonArgs
// {
pname = "sd-daemon";
src = daemonRustSrc;
cargoArtifacts = daemonCargoArtifacts;
cargoExtraArgs = "--package sd-core --bin sd-daemon --features ffmpeg";
postPatch = rust197PostPatch + ''
substituteInPlace core/src/volume/platform/linux.rs \
--replace-fail \
'.args(["-h", "-T"]) // -T shows filesystem type' \
'.args(["-B1", "-T"]) // Exact bytes; unlike -h, this is locale-independent'
substituteInPlace core/src/volume/utils.rs \
--replace-fail '"/" | "/usr"' '"/usr"' \
--replace-fail \
'assert!(should_hide_by_mount_path(Path::new("/")));' \
'assert!(!should_hide_by_mount_path(Path::new("/")));'
'';
buildInputs = daemonBuildInputs;
nativeBuildInputs = daemonNativeBuildInputs ++ [ pkgs.makeWrapper ];
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.ffmpeg_7.dev}/include";
FFMPEG_DIR = "${pkgs.ffmpeg_7.dev}";
postFixup = ''
wrapProgram $out/bin/sd-daemon \
--prefix PATH : ${daemonRuntimePath} \
--set PDFIUM_LIB_PATH ${pdfium}/lib/libpdfium.so
'';
}
);
sd-cli = craneLib.buildPackage (
commonArgs
// {
pname = "sd-cli";
src = cliRustSrc;
cargoArtifacts = cliCargoArtifacts;
cargoExtraArgs = "--package sd-cli --bin sd-cli";
postPatch = rust197PostPatch;
}
);
desktopItem = pkgs.makeDesktopItem {
name = "spacedrive";
desktopName = "Spacedrive";
exec = "spacedrive";
icon = "spacedrive";
categories = [
"FileManager"
"Utility"
];
terminal = false;
};
spacedrive-desktop = craneLib.buildPackage (
commonArgs
// {
pname = "spacedrive-desktop";
src = desktopRustSrc;
cargoArtifacts = desktopCargoArtifacts;
cargoExtraArgs = "--package spacedrive --bin Spacedrive";
patches = [
../graceful-global-shortcut.patch
];
postPatch = rust197PostPatch + ''
substituteInPlace apps/tauri/src-tauri/src/main.rs \
--replace-fail 'for i in 0..30 {' 'for i in 0..300 {' \
--replace-fail 'connection not available after 3 seconds' 'connection not available after 30 seconds'
substituteInPlace apps/tauri/src-tauri/tauri.conf.json \
--replace-fail '"decorations": true' '"decorations": false'
substituteInPlace apps/tauri/src-tauri/src/windows.rs \
--replace-fail 'true, // decorations' 'false, // decorations'
substituteInPlace apps/tauri/src-tauri/src/main.rs \
--replace-fail \
'// Explicitly remove menu on Windows' \
'// Explicitly remove the native menu bar on Windows and Linux'
sed -i '/Explicitly remove the native menu bar/{
n
s/#\[cfg(target_os = "windows")\]/#[cfg(any(target_os = "windows", target_os = "linux"))]/
}' apps/tauri/src-tauri/src/main.rs
grep -Fq '#[cfg(any(target_os = "windows", target_os = "linux"))]' \
apps/tauri/src-tauri/src/main.rs
'';
nativeBuildInputs = commonNativeBuildInputs ++ [
pkgs.wrapGAppsHook4
];
buildInputs = desktopBuildInputs;
preBuild = ''
mkdir -p apps/tauri/dist
cp -r ${frontend}/. apps/tauri/dist/
mkdir -p target/release
cp ${sd-daemon}/bin/sd-daemon target/release/sd-daemon
cp ${sd-daemon}/bin/sd-daemon target/release/sd-daemon-${targetTriple}
chmod +x target/release/sd-daemon target/release/sd-daemon-${targetTriple}
'';
postInstall = ''
mkdir -p $out/bin $out/share/icons/hicolor/128x128/apps
ln -s ${sd-daemon}/bin/sd-daemon $out/bin/sd-daemon
ln -s ${sd-daemon}/bin/sd-daemon $out/bin/sd-daemon-${targetTriple}
cp apps/tauri/src-tauri/icons/128x128.png $out/share/icons/hicolor/128x128/apps/spacedrive.png
'';
postFixup = ''
wrapProgram $out/bin/Spacedrive \
--set GST_PLUGIN_SYSTEM_PATH_1_0 ${gstreamerPluginPath} \
--set WEBKIT_DMABUF_RENDERER_FORCE_SHM 1 \
--set WEBKIT_FORCE_COMPOSITING_MODE 1
'';
meta = with lib; {
description = "Spacedrive desktop application";
homepage = "https://github.com/spacedriveapp/spacedrive";
license = licenses.agpl3Only;
mainProgram = "Spacedrive";
platforms = platforms.linux;
};
}
);
spacedrive = pkgs.symlinkJoin {
name = "spacedrive";
paths = [
spacedrive-desktop
sd-daemon
desktopItem
];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
ln -s Spacedrive $out/bin/spacedrive
'';
meta = spacedrive-desktop.meta // {
mainProgram = "spacedrive";
};
};
ui-test = pkgs.rustPlatform.buildRustPackage {
pname = "ui-test";
version = "0.1.0";
src = ../tools/ui-test;
cargoLock.lockFile = ../tools/ui-test/Cargo.lock;
meta = with lib; {
description = "Spacedrive UI test scaffold and scenario runner";
homepage = "https://github.com/spacedriveapp/spacedrive";
license = licenses.agpl3Only;
mainProgram = "ui-test";
platforms = platforms.linux;
};
};
test-ui-full = pkgs.writeShellApplication {
name = "test-ui-full";
runtimeInputs = with pkgs; [
chromium
chromedriver
curl
jq
python3
python3Packages.selenium
];
text = ''
export TEST_UI_FULL_PY=${../tools/ui-test/test_ui_full.py}
export CHROME_BIN=${pkgs.chromium}/bin/chromium
export CHROMEDRIVER_BIN=${pkgs.chromedriver}/bin/chromedriver
exec ${pkgs.bash}/bin/bash ${../tools/ui-test/test-ui-full.sh} "$@"
'';
};
in
{
inherit
frontend
sd-cli
sd-daemon
spacedrive-desktop
spacedrive
;
}