A big problem I’ve run into with using nix on darwin is the paucity of macOS-specific applications. I’ve seen a number of other people comment on this as well. However, macOS has pretty common distribution methods for .app files.
I was thinking it might be useful to develop a way to lower the threshold of knowledge needed to add a standard macOS application to nixpkgs. It turns out that jwiegley had already developed something for his config (https://github.com/jwiegley/nix-config/blob/c8f18be4a6beb7c621c411e7b7c3548282ea8abb/overlays/30-apps.nix). With permission, I used it as a base. I’ve modified it further the goal of maximizing simplicity.
A resulting darwin.installApplication
-based expression could look like so:
{ darwin }:
darwin.installApplication rec {
name = "f.lux";
version = "41.1";
app = "Flux.app";
url = "https://justgetflux.com/mac/Flux${version}.zip";
sha256 = "181aycdjvvmzvzm9jdb17593z7rxlcmj1x9xi7064hw6lipylarb";
description = "Reddens computer screen in the evening";
homepage = "https://justgetflux.com/";
maintainers = [ "spease" ];
license = "free";
}
The two places I decided to make tradeoffs from being idiomatic to being more user-friendly:
- I swap out
src
/fetchurl
forurl
andsha256
attributes. I figure this eliminates any need to understand or know offetchurl
or function calls. If it proves to be problematic (lots of packages that need to set src to something other thanfetchurl
), I figure it’s relatively trivial to mechanically convert installApplication calls to usefetchurl
. If there’s a way to make installApplication require eithersrc
orurl
andsha256
, I’d be happy to know. - I inline the
meta
attribute and index themaintainers
andlicense
attributes inside of installApplication. Again, this just reduces the nix-specific syntax involved. There’s no need to bring inlib
or have awith
statement this way, and sincelib.license
andlib.maintainers
do get indexed indarwin.installApplication
, any constraints are just as enforced as if the user had referenced the variables directly. This works fine forlicenses
, although in the case ofmaintainers
, it doesn’t look like you need to enter something that exists.
With both these changes, I feel this reduces the syntactic complexity to <= that of a brew file. For comparison, here’s the brew cask for f.lux:
https://github.com/Homebrew/homebrew-cask/blob/aee7ee24ef6af4e7c2f2e34fb2b67f4d52011490/Casks/flux.rb
Actual implementation of installApplication:
{ fetchurl, lib, stdenvNoCC, undmg, unzip}:
{ app, description, homepage, license, maintainers, name, sha256, url, version, postInstall ? "", ... }:
stdenvNoCC.mkDerivation {
inherit name version;
nativeBuildInputs = [ undmg unzip ];
sourceRoot = app;
src = fetchurl {
inherit url sha256;
};
phases = [ "unpackPhase" "installPhase" ];
installPhase = ''
mkdir -p "$out/Applications/${app}"
mv * "$out/Applications/${app}"
'' + postInstall;
meta = with lib; {
description = description;
homepage = homepage;
license = licenses."${license}";
maintainers = forEach maintainers (x: maintainers."${maintainer}");
platforms = platforms.darwin;
};
}
Note that one other change I make is using mv
instead of cp -pR
- I did this because I figured mv
will be more performant than cp for large files, and will implicitly preserve any file attributes, but if this will break something down the line I’m happy to change it.
Also, would love to know what the current accepted practice is to handle “.” in a package name…
Thanks!