How to reference my packages from other of my packages

I have created a package as follow:

file: lowbattery-alert.nix

{ pkgs, ... }:
let lowbattery-alert = pkgs.writeShellScriptBin "lowbattery-alert.sh" ''
   # do the thing
'';
in {
   environment.systemPackages = [ lowbattery-alert ];
}

My i3 config (in configuration.nix file) looks exactly as below.

configuration.nix (fragment)

  services.xserver.windowManager.i3 = {
    enable = true;
    package = pkgs.i3-gaps;
    extraPackages = [
      pkgs.dmenu
      pkgs.i3status
      pkgs.i3lock
      pkgs.i3blocks
    ];
    configFile = "/etc/i3.conf";
  };

  environment.etc."i3.conf".text = pkgs.callPackage ./i3-config.nix {};

Then in the i3-config.nix file i want to reference the lowbattery-alert package as follow:

exec --no-startup-id ${lowbattery-alert}/bin/lowbattery-alert.sh

The error

When I rebuild the system it fails with:

~/.nixos (master) > sudo nixos-rebuild switch
building Nix...
building the system configuration...
error: undefined variable 'lowbattery-alert'

       at /etc/nixos/i3-config.nix:236:24:

          235| # run special warning script
          236| exec --no-startup-id ${lowbattery-alert}/bin/lowbattery-alert.sh
             |                        ^
          237|
(use '--show-trace' to show detailed location information)

Note: the lowbattery-alert.sh file is created correctly and the rebuild fails only when i want add the reference in the i3-config-nix file

How can I achieve what I want?
Thank you in advance.

It looks a bit like you are mixing a package definition and a module in your lowbattery-alert.nix. There’s nothing wrong with that but it’s not going to make lowbattery-alert magically available elsewhere. Maybe you can do something along these lines:

file: lowbattery-alert.nix

{ pkgs, ... }:
pkgs.writeShellScriptBin "lowbattery-alert.sh" ''
   # do the thing
'';

file: configuration.nix

let
    lowbattery-alert = pkgs.callPackage ./lowbattery-alert.nix {};
in
    environment.systemPackages = [ lowbattery-alert ];
    environment.etc."i3.conf".text = pkgs.callPackage ./i3-config.nix { lowbattery-alert };

And finally i3-config.nix:

{pkgs, lowbattery-alert, ...}:
[${lowbattery-alert} now available here]

Written from memory and not checked. But something like that should do the trick.

You could also do pkgs.callPackage ./lowbattery-alert.nix {} multiple times if you need to reference the package in multiple places and don’t want to pass it around.

I was not able to make your suggestion to work but you drove me in the right direction with your last tip. I really don’t need to have that package as a systemPackages. So, I simply called in my i3-config.nix as follow:

{ pkgs, dmenu, i3status, i3blocks, ... }:
let
  lowbattery-alert = pkgs.callPackage ./lowbattery-alert.nix {};
in
''
exec --no-startup-id ${lowbattery-alert}/bin/lowbattery-alert.sh
''

Glad you got it working. If you don’t need it in your systemPackages then that is likely the best solution.

I believe in my example, the last line in configuration.nix should have been (mind the inherit):

environment.etc."i3.conf".text = pkgs.callPackage ./i3-config.nix { inherit lowbattery-alert; };
1 Like

Probably not worth it in this case but you may also want to consider using an overlay. Simply add something like the following to your configuration:

nixpkgs.overlays = [
  (final: prev: {
    lowbattery-alert = prev.callPackage ./lowbattery-alert.nix {};
  })
];

And they you will have it as a part of pkgs argument of all the modules, and pkgs.callPackages will be able to fill the lowbattery-alert argument (essentially dependency injection).

3 Likes