Pre-RFC: module system for wrappers in Nixpkgs

I don’t have a complete idea about this at the moment, just some random thoughts.

Create a module system for Nixpkgs, which will be evaluated to a tree of sets (what’s the name of this?) to determine some optional wrapper behaviors. This will be further formed into a function, in which the input is the original derivation of the program to be wrapped, and the output is an overlay with the wrapped derivation. The overlay can override the original derivation, or place the wrapped derivation in a new attribute (which should be preferred?).

In my idea, these optional wrappers are not built in the original derivation itself, just like the *-unwrapped package commonly seen in Qt Apps. Rather, they are added in the derivation the user finally installs, and built on users’ machine.

There are two ways to specify the affected package in a module.

The first is to directly specify a package. This situation applies to configuring a single program, such as neovim or vscode. We can further move the programs.foo type configuration of NixOS modules and Home manager into this Nixpkgs wrapper module, by adding optional wrappers to specify the configuration files and command arguments for them.

The second is to add a list like passthru.optionalWrappers to the derivations of packages that need to be wrapped, and then override the wrappers with null or some sets/derivations (which would be better?). This applies to all programs that look for optional library and resource dependencies on specific paths. For example, to address the problem that OpenGL drivers cannot be used directly on other Linux distributions, we can use wrappers to add drivers to the search path of libraries such as libdrm. Other common apps can also be configured to include optional dependencies and settings, such as Qt plugins, GTK themes, icon themes, electron flags, etc.

The wrapper module system added to Nixpkgs will allow us to reduce closure size more freely and manage parts of our configuration without placing configuration files in a large number of different places (and sometimes it creates havoc with programs installed by other package managers). We can also install several copies of the same program with different configurations at the same time. For example, one vim for writing markdown and another vim for writing Nix!

Obviously, this relies on a series of improvements to wrapper generators, including using a more consistent and structured representation of wrapper, allowing arguments, environment variables and others to be added, overridden and removed (instead of double wrapping). And it would be better to generate wrappers by directly splicing the binaries, or making them read structured configuration files, rather than using a compiler to compile them (or maybe we can do this with some minimal compiler). Hopefully some can be addressed by [RFC 0075] Declarative wrappers.

1 Like

Hello, and thanks for thinking about this! I don’t have the energies to keep work on RFC 75, so I’m glad to see other people feel that it is missing. Here are my comments:

Once you construct a representation of wrappers information in Nixpkgs, you should be able to support all the examples you mention, with overrlays, without needing the module system. Your proposed usages of the wrapping system are cool! However I think we could implement them without using the module system.

While looking up what I wrote in the RFC, I noticed my intentions in the combineWrappersInfo example weren’t clear. Here’s an example of an overlay that would set a GTK theme for libreoffice:

self: super: {
  libreoffice = super.libreoffice.overrideAttrs(old: {
    postInstall = (old.postInstall or "") + super.makeWrapperAuto.combineWrappersInfo {
      envInfo = {
        GTK_THEME = "Adwaita";
      };
    };
  };
}

We could abstract this, but it still doesn’t have to be part of the module system. Also, I noticed that the RFC doesn’t mention support for command line flags you’d want to add. However, that should be also part of the combineWrappersInfo argument attribute set. Like the previous example, I’d imagine this overlay for the Nix for markdown idea:

self: super: {
  mdVim = super.vim.overrideAttrs(old: {
    postInstall = (old.postInstall or "") + super.makeWrapperAuto.combineWrappersInfo {
      extraCommandArgs = [
        "-u ${pkgs.writeText "my-markdown.vimrc" ''
          set ft=markdown
        ''}"
      ];
    };
  };
}
1 Like

Well, module system is just a shiny stuff to create schemas and default values ​​for structured data, and provide flexible override options and easily reusable modules. In the future, both derivation and service abstraction layers may be represented by separate module systems.

I did think about the drawback of representing all wrapped overlays as a single unified module system when I went to bed last night: this module system will become larger and larger, and what’s worse is that the users will feel the same efficiency bottleneck as evaluating nixos configuration. But it might still be a better idea to represent it as a separate module system, or to include other modules as needed (rather than pulling in all the stuff).