Generate and install a .desktop file along with an executable

I wanted to create a tiny package for one of my shell scripts for simple distribution on our servers. It can be installed in NixOS configs using (callPackage ./my-script.nix {}).

{ pkgs, ... }: pkgs.writeShellApplication {
  name = "my-script";
  runtimeInputs = [ pkgs.curl pkgs.coreutils ];
  text = builtins.readFile "./my-script.sh";
}

But I also need a .desktop file installed along with the script to run it from a desktop environment.

The .desktop file would be generated with something like:

[Desktop Entry]
Comment=
Terminal=true
Name=my-script
Exec=/path/to/my-script %f
Type=Application

The question is how these two could be combined into one derivation so that the .desktop file would be installed correctly under share/applications/ and point to the correct path of the executable my-script.

Is it a good way, to use something like this:

{ pkgs, ... }:
pkgs.stdenv.mkDerivation {
  name = "my-script";
  buildCommand = ''
    # include the writeShellApplication call from above
    # create $out/bin and $out/share/applications directories
    # copy files into the directories
  '';
  dontBuild = true;
}

Still, the question is, how to fill in the executable path in the .desktop file?
EDIT: oh, it should be just $out/bin/my-script if $out is the final result path.

1 Like

Looks like the following would work. Any comments/feedback are welcome, it’s my first package.


{ pkgs, ... }:
let
  name = "my-script";
  script = pkgs.writeShellApplication {
    name = name;
    runtimeInputs = with pkgs; [ curl coreutils ];
    text = builtins.readFile "./my-script.sh";
  };
in
pkgs.stdenv.mkDerivation {
  name = name;
  buildCommand = ''
    mkdir -p $out/bin
    cp ${script}/bin/${name} $out/bin
    mkdir -p $out/share/applications
    cat <<INI > $out/share/applications/${name}.desktop
    [Desktop Entry]
    Terminal=true
    Name=${name}
    Exec=$out/bin/${name} %f
    Type=Application
    INI
  '';
  dontBuild = true;
}

A shorter solution using pkgs.makeDesktopItem:

{ pkgs, ... }: pkgs.stdenv.mkDerivation rec {
  name = "my-script";
  buildCommand = let
    script = pkgs.writeShellApplication {
      name = name;
      runtimeInputs = with pkgs; [ curl coreutils ];
      text = builtins.readFile "./my-script.sh";
    };
    desktopEntry = pkgs.makeDesktopItem {
      name = name;
      desktopName = name;
      exec = "${script}/bin/${name} %f";
      terminal = true;
    };
  in ''
    mkdir -p $out/bin
    cp ${script}/bin/${name} $out/bin
    mkdir -p $out/share/applications
    cp ${desktopEntry}/share/applications/${name}.desktop $out/share/applications/${name}.desktop
  '';
  dontBuild = true;
}

I’m using pkgs.makeDesktopItem for that. Using https://noogle.dev sometimes helps quickly figuring out if there is already something provided by nixpkgs or the standard libs/builtins.

Here my snippet, checkout GitHub code search path:*.nix pkgs.makeDesktopItem if you need more inspiration or read the source of make-desktopitem/default.nix.

  ocr-scrot-desktop = pkgs.makeDesktopItem
    rec {
      name = "ocr-scrot-desktop";
      desktopName = "ocr-scrot";
      exec = "${ocr-scrot}/bin/ocr-scrot";
    };
2 Likes