# Feedback: darwin.installApplication

**URL:** https://discourse.nixos.org/t/feedback-darwin-installapplication/11324
**Category:** Development
**Created:** [February 1, 2021, 2:35am UTC](https://discourse.nixos.org/t/feedback-darwin-installapplication/11324 "2021-02-01T02:35:02Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![spease](https://discourse.nixos.org/user_avatar/discourse.nixos.org/spease/32/3506_2.png) [@spease](https://discourse.nixos.org/u/spease)
#### Post date: [February 1, 2021, 2:35am UTC](https://discourse.nixos.org/t/feedback-darwin-installapplication/11324/1 "2021-02-01T02:35:02Z")

</div>

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](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:

```auto
{ 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` for `url` and `sha256` attributes. I figure this eliminates any need to understand or know of `fetchurl` or function calls. If it proves to be problematic (lots of packages that need to set src to something other than `fetchurl`), I figure it’s relatively trivial to mechanically convert installApplication calls to use `fetchurl`. If there’s a way to make installApplication require either `src` or `url` and `sha256`, I’d be happy to know.
- I inline the `meta` attribute and index the `maintainers` and `license` attributes inside of installApplication. Again, this just reduces the nix-specific syntax involved. There’s no need to bring in `lib` or have a `with` statement this way, and since `lib.license` and `lib.maintainers` do get indexed in `darwin.installApplication`, any constraints are just as enforced as if the user had referenced the variables directly. This works fine for `licenses`, although in the case of `maintainers`, 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:

```auto
{ 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!

---

<div class="post-metadata">

### Author: ![domenkozar](https://discourse.nixos.org/user_avatar/discourse.nixos.org/domenkozar/32/38357_2.png) [@domenkozar](https://discourse.nixos.org/u/domenkozar)
#### Post date: [February 1, 2021, 10:21am UTC](https://discourse.nixos.org/t/feedback-darwin-installapplication/11324/2 "2021-02-01T10:21:21Z")

</div>

Don’t see a reason not to have this in nixpkgs.

---

<div class="post-metadata">

### Author: ![spease](https://discourse.nixos.org/user_avatar/discourse.nixos.org/spease/32/3506_2.png) [@spease](https://discourse.nixos.org/u/spease)
#### Post date: [February 15, 2021, 11:37pm UTC](https://discourse.nixos.org/t/feedback-darwin-installapplication/11324/3 "2021-02-15T23:37:52Z")

</div>

This has been working pretty well so far.

I’ve refined it a bit to deal with some (but not all) spaces issues, and .pkg files.

```auto
{ cpio, fetchurl, fixDarwinDylibNames, lib, stdenvNoCC, undmg, unpkg, unzip }:
s@{ description, homepage, license, maintainers, name, version, postInstall ? "", ... }:

stdenvNoCC.mkDerivation rec {
  inherit name version;

  nativeBuildInputs = [cpio fixDarwinDylibNames undmg unpkg unzip];

  sourceRoot = ".";
  src = if builtins.hasAttr "src" s then s.src else fetchurl {
    name = builtins.replaceStrings ["%20"] ["-"] (builtins.head (builtins.match ".*/([^/]+)" s.url));
    url=s.url;
    sha256=s.sha256;
  };

  phases = ["unpackPhase" "installPhase"];

  installPhase = if builtins.hasAttr "installPhase" s then s.installPhase else ''
      # .dmg files or compressed Applications
      app=( ./*.app )
      if [! -z "$app"]; then
        mkdir -p $out/Applications
        mv -n "$app" $out/Applications/
      fi

      # .pkg files
      if [-d "./usr/local"]; then
        mv -n ./usr/local/* $out/
      fi

      if [! -L "./Applications"] && [-d "./Applications"]; then
        mkdir -p $out/Applications
        mv -n ./Applications/* $out/Applications/
      fi
    '' + postInstall;

  meta = with lib; {
    description = description;
    homepage = homepage;
    license = licenses."${license}";
    maintainers = forEach maintainers (x: maintainers."${maintainer}");
    platforms = platforms.darwin;
  };
}

```

where unpkg takes after undmg:

```auto
{ lib, stdenvNoCC, xar }:

stdenvNoCC.mkDerivation rec {
  version = "1.0.0";
  pname = "unpkg";

  nativeBuildInputs = [xar];
  setupHook = ./setup-hook.sh;

  src = ./.;

  installPhase = ''
    mkdir -p $out/bin
    ln -s $(command -v xar) $out/bin/xar
  '';

  meta = with lib; {
    description = "Extract a pkg file";
    license = licenses.gpl3;
    platforms = platforms.all;
    maintainers = with maintainers; [spease];
  };
}

```

```auto
unpackCmdHooks+=(_tryUnpackPkg)
_tryUnpackPkg() {
  if ! [["${curSrc}" =~ \.pkg$]]; then return 1; fi
  xar --dump-header -f "${curSrc}" | grep -q "^magic:\s\+[0-9a-z]\+\s\+(OK)$"
  [$? -ne 0] && return 1
  xar -tf "${curSrc}" | grep -q "/Payload$"
  dir="$(mktemp -d)"
  xar -xf "${curSrc}" -C "${dir}"
  zcat ${dir}/*/Payload | cpio -idm --no-absolute-filenames
  rm -rf --preserve-root "${dir}"
}

```

I’m not happy with the tmpdir since if zcat / cpio fails, I’m concerned it will leave the tmpdir hanging. But I don’t know of a good way to fix this.

Another thing I’ve been considering is whether to split it into ‘installApplication’ and ‘installPackage’ (for .pkg files)? The .pkg handling could hypothetically be a lot more sophisticated.

Finally, I wonder if for some packages it might make sense to run install\_name\_tool. eg for one package which hardcoded a path to a library, I had to do:

```auto
preFixup = lib.optional targetPlatform.isDarwin ''
    install_name_tool -add_rpath "${jlink.outPath}/lib" $out/bin/nrfjprog
  '';

```

which could be automated to iterate through the binaries in the final `bin` directory and add the `lib` directories of all the buildInputs to the rpaths.

The issue in that case was that the library needed to be in a path discoverable by dlopen (according to the documentation), and the fixDylib package wasn’t sufficient. This was decidedly non-discoverable and took me awhile to figure out.

EDIT: Also note that the `app` attribute is no longer needed.

---

<div class="post-metadata">

### Author: ![dramforever](https://discourse.nixos.org/user_avatar/discourse.nixos.org/dramforever/32/773_2.png) [@dramforever](https://discourse.nixos.org/u/dramforever)
#### Post date: [February 16, 2021, 3:59am UTC](https://discourse.nixos.org/t/feedback-darwin-installapplication/11324/4 "2021-02-16T03:59:21Z")

</div>

To allow either `url` or `src`, you can use optional arguments like `{ ... src ? null ...}: ...` and use `assert` to ensure that there’s nothing missing/conflicting.

I think `meta` should be done this way too.

---

<div class="post-metadata">

### Author: ![shamrocklee](https://discourse.nixos.org/user_avatar/discourse.nixos.org/shamrocklee/32/28815_2.png) [@shamrocklee](https://discourse.nixos.org/u/shamrocklee)
#### Post date: [January 14, 2022, 5:19am UTC](https://discourse.nixos.org/t/feedback-darwin-installapplication/11324/5 "2022-01-14T05:19:18Z")

</div>

It would be wonderful to have an Mac OS App builder in Nixpkgs. There can be some syntax sugar (as @dramforever have mentioned) aside from the formal `meta` attribute to make it more user-friendly.

The `name` attribute may cause confusion with the `name="$pname-$version"` convention. `pname` would be more backward compatible.
