394 lines
16 KiB
Nix
394 lines
16 KiB
Nix
# arch package:
|
||
# - <https://gitlab.archlinux.org/archlinux/packaging/packages/signal-desktop/-/blob/main/PKGBUILD?ref_type=heads>
|
||
# - builds with `yarn generate; yarn build`
|
||
# alpine package:
|
||
# - <https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/testing/signal-desktop/APKBUILD?ref_type=heads>
|
||
# - more involved build process, apparently to reuse deps shared with other parts of the OS:
|
||
# - manually build signal's webrtc using ninja
|
||
# - manually build signal's ringrtc using yarn
|
||
# - manually build libsignal with yarn, cargo and cbindgen
|
||
# - yarn build:acknowledgments
|
||
# - yarn patch-package
|
||
# - npm rebuild esbuild # apparently esbuild is to be used later in the build process
|
||
# - yarn build:dev
|
||
# - yarn install
|
||
# - then it `mv`s and `patch`s a bunch of stuff
|
||
# - tasje pack
|
||
#
|
||
#
|
||
### TODO: build more components from source:
|
||
#
|
||
# - the dependencies which alpine builds live over here:
|
||
# - <https://github.com/signalapp/libsignal/archive/refs/tags/v$_libsignalver/libsignal-$_libsignalver.tar.gz>
|
||
# - <https://github.com/signalapp/ringrtc/archive/refs/tags/v$_ringrtcver/ringrtc-$_ringrtcver.tar.gz>
|
||
# - <https://s3.sakamoto.pl/lnl-aports-snapshots/webrtc-$_webrtcver.tar.zst>
|
||
# - <https://github.com/signalapp/Signal-FTS5-Extension/archive/refs/tags/v$_stokenizerver/stokenizer-$_stokenizerver.tar.gz>
|
||
#
|
||
# - nixpkgs packages libsignal-protocol-c, but i think not the actual `libsignal`
|
||
# - <https://github.com/signalapp/libsignal-protocol-c>
|
||
# - flare-signal references <https://github.com/signalapp/libsignal>
|
||
# - same with gurk-rs
|
||
# - neither of these do anything special with it.
|
||
# - however, they do refer to it as `name = "libsignal-protocol"`, so maybe libsignal-protocol-c is quite related?
|
||
# - and also `name = "poksho"`, signal-crypto, zkcredential, zkgroup, etc
|
||
#
|
||
#### better-sqlite
|
||
# try:
|
||
# - npm rebuild @signalapp/better-sqlite3 --offline
|
||
# - npm rebuild @signalapp/better-sqlite3 --offline --nodedir="${nodeSources}" --build-from-source
|
||
# - patchelf --add-needed ${icu}/lib/libicutu.so node_modules/@signalapp/better-sqlite3/build/Release/better_sqlite3.node
|
||
# or:
|
||
# - npm rebuild \
|
||
# @signalapp/better-sqlite3 \
|
||
# sharp spellchecker websocket \
|
||
# utf-8-validate bufferutil fs-xattr \
|
||
# --offline --nodedir="${nodeSources}" --build-from-source
|
||
# or:
|
||
# - npm_config_nodedir="${nodeSources}" npm_config_node_gyp="${buildPackages.nodejs}/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" npm rebuild --verbose --sqlite=${sqlite.dev} @signalapp/better-sqlite3
|
||
#
|
||
# or, with nodeSources = srcOnly nodejs';
|
||
# - pushd node_modules/@signalapp/better-sqlite3
|
||
# - patch -p1 < ${bettersqlitePatch}
|
||
# - npm run build-release --offline
|
||
# - npm run build-release --offline --nodedir="${nodeSources}"
|
||
# - find build -type f -exec \
|
||
# remove-references-to \
|
||
# -t "${nodeSources}" {} \;
|
||
# - popd
|
||
#
|
||
#### ringrtc:
|
||
# - readme says that it's going to download webrtc components -- that's probably why Alpine builds that first
|
||
# - "To build the Node.js module suitable for including in an Electron app, run:"
|
||
# make electron PLATFORM=<platform> NODEJS_ARCH=<arch>
|
||
# - compiling x64 -> arm64 is supported, but arm64 -> arm64 is *not*
|
||
# - the referenced file is a 13 MB ELF: /nix/store/7hb6840x2f10zpm3d5ccj3kr3vaf93n5-signal-desktop-6.36.0/lib/Signal/resources/app.asar.unpacked/node_modules/@signalapp/ringrtc/build/linux/libringrtc-x64.node
|
||
# - not in crates.io
|
||
# - TODO: simplest test would be to just copy this out of nixpkgs' signal-desktop
|
||
#
|
||
#### webrtc:
|
||
# - nothing much in nixpkgs cites "signalapp", so i probably have to do this manually
|
||
# - nothing named `webrtc` linked from the signal-desktop appimage
|
||
# - it's like 900MB?? https://s3.sakamoto.pl/lnl-aports-snapshots/webrtc-5845h.tar.zst
|
||
# - not in crates.io
|
||
#
|
||
#
|
||
# HOW TO UPDATE
|
||
# - `./scripts/update sane.signal-desktop-from-src`
|
||
# - check signal-desktop's package.json for new ringrtc/nodejs <repo:signalapp/Signal-Desktop:package.json>
|
||
# - if sqlcipher fails then update sqlcipherTarball url/hash (rare)
|
||
# errors which can be safely ignored:
|
||
# - "Error: Could not detect abi for version 30.1.1 and runtime electron. Updating "node-abi" might help solve this issue if it is a new release of electron"
|
||
# - <https://github.com/signalapp/Signal-Desktop/pull/6889>
|
||
|
||
|
||
{ lib
|
||
, alsa-lib
|
||
, asar
|
||
, at-spi2-atk
|
||
, at-spi2-core
|
||
, atk
|
||
, autoPatchelfHook
|
||
, bash
|
||
, buildNpmPackage
|
||
, buildPackages
|
||
, cups
|
||
, electron-bin
|
||
, fetchurl
|
||
, fetchFromGitHub
|
||
, flac
|
||
, gdk-pixbuf
|
||
, git
|
||
, gitUpdater
|
||
, gnused
|
||
, gtk3
|
||
, icu
|
||
, libpulseaudio
|
||
, libwebp
|
||
, libxslt
|
||
, makeShellWrapper
|
||
, mesa
|
||
, nspr
|
||
, nss
|
||
, pango
|
||
, python3
|
||
# , sqlite
|
||
# , sqlcipher
|
||
, stdenv
|
||
, wrapGAppsHook
|
||
, xdg-utils
|
||
}:
|
||
let
|
||
version = "7.26.0";
|
||
|
||
ringrtcPrebuild = fetchurl {
|
||
# version is found in signal-desktop's package.json as "@signalapp/ringrtc"
|
||
url = "https://build-artifacts.signal.org/libraries/ringrtc-desktop-build-v2.48.0.tar.gz";
|
||
hash = "sha256-WfD6831Y65KrNG6nGyutffVBajyw1ODQ6OwJ3dE+nKg=";
|
||
};
|
||
sqlcipherTarball = fetchurl {
|
||
# this is a dependency of better-sqlite3.
|
||
# version/url is found in <repo:signalapp/better-sqlite3:deps/download.js>
|
||
# - checkout the better-sqlite3 tag which matches signal-dekstop's package.json "@signalapp/better-sqlite3" key.
|
||
url = let
|
||
HASH = "7c30de8bcb5d21127803f5ab19a097e1b508d05744aca3ac6166352506b5f451";
|
||
SQLCIPHER_VERSION = "4.6.1";
|
||
OPENSSL_VERSION = "3.0.7";
|
||
TOKENIZER_VERSION = "0.2.1";
|
||
in "https://build-artifacts.signal.org/desktop/sqlcipher-${SQLCIPHER_VERSION}--${OPENSSL_VERSION}--${TOKENIZER_VERSION}-${HASH}.tar.gz";
|
||
hash = "sha256-fDDei8tdIRJ4A/WrGaCX4bUI0FdErKOsYWY1JQa19FE=";
|
||
};
|
||
|
||
# signal-fts5-extension = callPackage ./fts5-extension { };
|
||
# bettersqlitePatch = substituteAll {
|
||
# # this patch does more than just use the system sqlcipher.
|
||
# # it also tells bettersqlite to link against its needed runtime dependencies,
|
||
# # since there's a few statically linked things which aren't called out.
|
||
# # it also tells bettersqlite not to download sqlcipher when we `npm rebuild`
|
||
# src = ./bettersqlite-use-system-sqlcipher.patch;
|
||
# inherit sqlcipher;
|
||
# inherit (nodejs') libv8;
|
||
# signal_fts5_extension = signal-fts5-extension;
|
||
# };
|
||
|
||
src = fetchFromGitHub {
|
||
owner = "signalapp";
|
||
repo = "Signal-Desktop";
|
||
leaveDotGit = true; # signal calculates the release date via `git`
|
||
rev = "v${version}";
|
||
hash = "sha256-Dfq8JbECTUNy93cel/5mJ5RAc0+pisqi6RqWFhm3eYY=";
|
||
};
|
||
|
||
# note that `package.json` locks the electron version, but we seem to not be strictly beholden to that.
|
||
# prefer to use the same electron version as everywhere else, and a `-bin` version to avoid 4hr rebuilds.
|
||
# the non-bin varieties *seem* to ship the wrong `electron.headers` property.
|
||
# - maybe they can work if i manually DL and ship the corresponding headers
|
||
electron' = electron-bin;
|
||
|
||
buildNpmArch = if stdenv.buildPlatform.isAarch64 then "arm64" else "x64";
|
||
hostNpmArch = if stdenv.hostPlatform.isAarch64 then "arm64" else "x64";
|
||
crossNpmArchExt = if buildNpmArch == hostNpmArch then "" else "-${hostNpmArch}";
|
||
in
|
||
buildNpmPackage rec {
|
||
pname = "signal-desktop-from-src";
|
||
inherit src version;
|
||
|
||
npmDepsHash = "sha256-efiJu3mv2fYQFqAHFU83lmlq4jqdAsaiACE9LlyffKY=";
|
||
|
||
patches = [
|
||
# ./debug.patch
|
||
# fix bug that signal launches in the background on wayland
|
||
# - <https://github.com/signalapp/Signal-Desktop/issues/6368>
|
||
# - without this, signal can be started with `signal-desktop & ; sleep 5; signal-desktop`
|
||
# - the second instance wakes the first one, and then exits
|
||
./show-on-launch.patch
|
||
# XXX(2024-09-27): not sure what no-mac-screen-share.patch was needed for? build fix? it was introduced by me in 2024-07-xx
|
||
# ./no-mac-screen-share.patch
|
||
];
|
||
|
||
postPatch = ''
|
||
# unpin nodejs. i should probably *try* to keep these vaguely in sync, but it seems to work decently with these out of sync too (at least, if the major versions match?)
|
||
sed -i 's/"node": .*/"node": "*"/' package.json
|
||
# don't populate fallback DNS mappings, and don't try to install electron-builder deps during build:
|
||
substituteInPlace package.json \
|
||
--replace-fail \
|
||
'"build:dns-fallback": "node ts/scripts/generate-dns-fallback.js"' \
|
||
'"build:dns-fallback": "true"' \
|
||
--replace-fail \
|
||
'"electron:install-app-deps": "electron-builder install-app-deps"' \
|
||
'"electron:install-app-deps": "true"'
|
||
|
||
# fixes build failure:
|
||
# > Fusing electron at /build/source/release/linux-unpacked/signal-desktop inspect-arguments=false
|
||
# > ⨯ EACCES: permission denied, open '/build/source/release/linux-unpacked/signal-desktop' failedTask=build stackTrace=Error: EACCES: permission denied, open '/build/source/release/linux-unpacked/signal-desktop'
|
||
# electron "fusing" (electron.flipFuses) seems to be configuring which functionality electron will support at runtime.
|
||
# notably: ELECTRON_RUN_AS_NODE, cookie encryption, NODE_OPTIONS env var, --inspect-* CLI args, app.asar validation
|
||
# skipping the fuse process seems relatively inconsequential
|
||
substituteInPlace ts/scripts/after-pack.ts \
|
||
--replace-fail 'await fuseElectron' '//await fuseElectron'
|
||
'';
|
||
|
||
nativeBuildInputs = [
|
||
asar # used during fixup
|
||
autoPatchelfHook
|
||
git # to calculate build date
|
||
gnused
|
||
makeShellWrapper
|
||
python3
|
||
wrapGAppsHook
|
||
];
|
||
|
||
buildInputs = [
|
||
alsa-lib
|
||
at-spi2-atk
|
||
at-spi2-core
|
||
atk
|
||
cups
|
||
electron'
|
||
flac
|
||
gdk-pixbuf
|
||
gtk3
|
||
libpulseaudio
|
||
libwebp
|
||
libxslt
|
||
mesa # for libgbm
|
||
nspr
|
||
nss
|
||
pango
|
||
# so that bettersqlite may link against sqlcipher (see patch)
|
||
# but i don't know if it actually needs to. just copied this from alpine.
|
||
# sqlcipher
|
||
];
|
||
|
||
strictDeps = true;
|
||
# disallowedReferences = [ buildPackages.nodejs ]; #< TODO: set when cross compiling
|
||
|
||
# env.SIGNAL_ENV = "production";
|
||
# env.NODE_ENV = "production"; #< XXX setting this causes `node_modules/protobufjs-cli/bin/pbjs` to not be fetched...
|
||
# env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
|
||
# env.npm_config_arch = buildNpmArch;
|
||
# env.npm_config_target_arch = hostNpmArch;
|
||
|
||
dontWrapGApps = true;
|
||
# dontStrip = false;
|
||
# makeCacheWritable = true;
|
||
|
||
npmRebuildFlags = [
|
||
# "--offline"
|
||
"--ignore-scripts"
|
||
];
|
||
|
||
# NIX_DEBUG = 6;
|
||
|
||
postConfigure = ''
|
||
# XXX: Signal does not let clients connect if they're running a version that's > 90d old.
|
||
# to calculate the build date, it uses SOURCE_DATE_EPOCH (if set), else `git log`.
|
||
# nixpkgs sets SOURCE_DATE_EPOCH to 1980/01/01 by default, so unset it so Signal falls back to git date.
|
||
# see: Signal-Desktop/ts/scripts/get-expire-time.ts
|
||
export SOURCE_DATE_EPOCH=
|
||
|
||
# apparently electron projects aren't "stock" node.
|
||
# so subprojects which want to use node internals (i.e. call C functions provided by node)
|
||
# need to build against electron's versions of the node headers, or something.
|
||
# without patching this, Signal can build, but will fail with `undefined symbol: ...` errors at runtime.
|
||
# see: <https://www.electronjs.org/docs/latest/tutorial/using-native-node-modules>
|
||
tar xzf ${electron'.headers}
|
||
export npm_config_nodedir=$(pwd)/node_headers
|
||
|
||
# patchShebangs --build --update node_modules/{bufferutil/node_modules/node-gyp-build/,node-gyp-build,utf-8-validate/node_modules/node-gyp-build}
|
||
# patch these out to remove a runtime reference back to the build bash
|
||
# (better, perhaps, would be for these build scripts to not be included in the asar...)
|
||
sed -i 's:#!.*/bin/bash:#!/bin/sh:g' node_modules/@swc/helpers/scripts/gen.sh
|
||
sed -i 's:#!.*/bin/bash:#!/bin/sh:g' node_modules/@swc/helpers/scripts/generator.sh
|
||
substituteInPlace node_modules/dashdash/etc/dashdash.bash_completion.in --replace-fail '#!/bin/bash' '#!/bin/sh'
|
||
|
||
# provide necessities which were skipped as part of --ignore-scripts
|
||
tar -xzf ${ringrtcPrebuild} --directory node_modules/@signalapp/ringrtc/
|
||
|
||
cp ${sqlcipherTarball} node_modules/@signalapp/better-sqlite3/deps/sqlcipher.tar.gz
|
||
# pushd node_modules/@signalapp/better-sqlite3
|
||
# # node-gyp isn't consistently linked into better-sqlite's `node_modules` (maybe due to version mismatch with signal-desktop's node-gyp?)
|
||
# PATH="$PATH:$(pwd)/../../.bin" npm --offline run build-release
|
||
# popd
|
||
|
||
# pushd node_modules/@signalapp/libsignal-client
|
||
# npx node-gyp rebuild
|
||
# popd
|
||
|
||
# run signal's own `postinstall`:
|
||
# - npm run build:acknowledgments
|
||
# - npm exec patch-package
|
||
# - npm run electron:install-app-deps
|
||
npm run postinstall
|
||
'';
|
||
|
||
# excerpts from package.json:
|
||
# - "build": "run-s --print-label generate build:esbuild:prod build:release"
|
||
# - "generate": "npm-run-all build-protobuf build:esbuild build:dns-fallback build:icu-types build:compact-locales sass get-expire-time copy-components",
|
||
# - "build-protobuf": "npm run build-module-protobuf",
|
||
# - "build-module-protobuf": "pbjs --target static-module --force-long --no-typeurl --no-verify --no-create --no-convert --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --no-comments --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
||
# - "build:esbuild": "node scripts/esbuild.js",
|
||
# - "build:dns-fallback": "node ts/scripts/generate-dns-fallback.js",
|
||
# - "build:icu-types": "node ts/scripts/generate-icu-types.js",
|
||
# - "build:compact-locales": "node ts/scripts/generate-compact-locales.js",
|
||
# - "sass": "sass stylesheets/manifest.scss:stylesheets/manifest.css stylesheets/manifest_bridge.scss:stylesheets/manifest_bridge.css",
|
||
# - "get-expire-time": "node ts/scripts/get-expire-time.js",
|
||
# - "copy-components": "node ts/scripts/copy.js",
|
||
# - "build:esbuild:prod": "node scripts/esbuild.js --prod",
|
||
# - "build:release": "cross-env SIGNAL_ENV=production npm run build:electron -- --config.directories.output=release",
|
||
# - "build:electron": "electron-builder --config.extraMetadata.environment=$SIGNAL_ENV",
|
||
#
|
||
# i can't call toplevel `build` because some steps fail (e.g. dns-fallback) and can be skipped instead,
|
||
# while other steps fail (electron-builder) and need patching, but npm doesn't plumb the necessary flags through.
|
||
# so instead i call each step individually.
|
||
|
||
buildPhase = ''
|
||
runHook preBuild
|
||
|
||
npm run generate
|
||
|
||
npm run build:esbuild:prod --offline --frozen-lockfile
|
||
|
||
npm run build:release -- \
|
||
--${hostNpmArch} \
|
||
--config.electronDist=${electron'}/libexec/electron \
|
||
--config.electronVersion=${electron'.version} \
|
||
--dir
|
||
|
||
runHook postBuild
|
||
'';
|
||
|
||
installPhase = ''
|
||
runHook preInstall
|
||
|
||
# directory structure follows the original `signal-desktop` nix package
|
||
mkdir -p $out/lib
|
||
cp -R release/linux${crossNpmArchExt}-unpacked $out/lib/Signal
|
||
# cp -R release/linux-unpacked/resources $out/lib/Signal/resources
|
||
# cp -R release/linux-unpacked/locales $out/lib/Signal/locales
|
||
|
||
mkdir $out/bin
|
||
|
||
runHook postInstall
|
||
'';
|
||
|
||
preFixup = ''
|
||
# fixup the app.asar to use host nodejs
|
||
asar extract $out/lib/Signal/resources/app.asar unpacked
|
||
rm $out/lib/Signal/resources/app.asar
|
||
patchShebangs --host --update unpacked
|
||
asar pack unpacked $out/lib/Signal/resources/app.asar
|
||
|
||
# XXX: add --ozone-platform-hint=auto to make it so that NIXOS_OZONE_WL isn't *needed*.
|
||
# electron should auto-detect x11 v.s. wayland: launching with `NIXOS_OZONE_WL=1` is an optional way to force it when debugging.
|
||
# xdg-utils: needed for ozone-platform-hint=auto to work
|
||
# else `LaunchProcess: failed to execvp: xdg-settings`
|
||
makeShellWrapper ${lib.getExe electron'} $out/bin/signal-desktop \
|
||
"''${gappsWrapperArgs[@]}" \
|
||
--add-flags $out/lib/Signal/resources/app.asar \
|
||
--suffix PATH : ${lib.makeBinPath [ xdg-utils ]} \
|
||
--add-flags --ozone-platform-hint=auto \
|
||
--add-flags "\''${WAYLAND_DISPLAY:+--ozone-platform=wayland --enable-features=WaylandWindowDecorations}" \
|
||
--inherit-argv0
|
||
'';
|
||
|
||
passthru = {
|
||
# inherit bettersqlitePatch signal-fts5-extension;
|
||
updateScript = gitUpdater {
|
||
rev-prefix = "v";
|
||
ignoredVersions = "beta";
|
||
};
|
||
updateWithSuper = false; #< TODO: enable auto-update, but i need a real updateScript for that instead of one which only updates top-level deps
|
||
};
|
||
|
||
meta = {
|
||
description = "Private, simple, and secure messenger";
|
||
longDescription = ''
|
||
Signal Desktop is an Electron application that links with your
|
||
"Signal Android" or "Signal iOS" app.
|
||
'';
|
||
homepage = "https://signal.org/";
|
||
changelog = "https://github.com/signalapp/Signal-Desktop/releases/tag/v${version}";
|
||
license = lib.licenses.agpl3Only;
|
||
};
|
||
}
|