Overriding incompatible packages
Many existing Gleam packages available over Hex will not work on Nix by default, as they rely on FFI with Gleam's typical targets (Erlang and JavaScript). Most importantly, this includes Gleam's standard library (gleam_stdlib
).
The workaround is to create Nix-compatible forks of those packages. For instance, Glistix maintains its own fork of the standard library. To use them, however, we have the following challenges:
-
Currently, Gleam does not have a proper requirement overriding system (tracked at upstream issue #2899). As such, we have to use those forks as local or Git dependencies. That way, they override transitive Hex dependencies with the same name, thus ensuring the fork is used instead of the Hex version, even if the package appears as a transitive dependency (i.e. a dependency of a dependency, at any level).
-
However, Gleam does not currently support Git dependencies (tracked at upstream issue #1338). As such, we depend on local paths pointing to cloned Git submodules.
-
Finally, each fork needs to have the same name as the package it's patching (due to a compiler requirement, while there is no built-in patching yet) and, as such, cannot be published to Hex (as it can't replace the patched package itself, of course).
It is expected that a proper solution to these problems will be available in the future. For now, the best we have is cloning forks as Git submodules and using them as local dependencies.
Don't worry, though - glistix new
automatically patches gleam_stdlib
for you by setting up gleam.toml
and cloning it as a submodule to external/stdlib
. However, you will have to do that by yourself for any other package patches you might need (e.g. glistix/json
).
Steps to override a package
The gleam_json
package, for example, does not support the Nix target by default, while several Gleam packages depend on it, creating a problem if we want to use them. Luckily, the Glistix project maintains a Nix-compatible fork of this package at https://github.com/glistix/json. Here's how we can use it, ensuring we can use packages which depend on gleam_json
(and also so we can depend on it ourselves):
-
Run the command below to add the repository as a Git submodule of your project. We add submodules to the
external/
folder as a convention:git submodule add --name json -- https://github.com/glistix/json external/json
-
Add
gleam_json
as a local dependency to that submodule at your project'sgleam.toml
:[dependencies] gleam_json = { path = "./external/json" }
-
To ensure your local override of
gleam_json
takes precedence over transitive local dependencies (see explanation at "Dealing with local package conflicts when patching"), you can addgleam_json
tolocal-overrides
at[glistix.preview]
, as below:[glistix.preview] # note that 'gleam_stdlib' is there by default local-overrides = ["gleam_stdlib", "gleam_json"]
-
Additionally, if you intend on publishing your package to Hex, which doesn't support local dependencies, you will have to use the (temporary) workaround below to tell Hex you depend on
gleam_json
from Hex instead, as explained in the next section.[glistix.preview.hex-patch] gleam_json = ">= 1.0.0 and < 2.0.0" # for example
-
Finally, make sure to update your
flake.nix
file so that it will correctly clone the submodule when building your package through Nix. You can do so by adding the fork's repository as a Flake input, and then passing its downloaded source to thesubmodules
list (which is later used as an argument tobuildGlistixPackage
), as below:# At your flake.nix { inputs = { # ... json = { # <-- add this input url = "github:glistix/json"; flake = false; # <-- get the source, not the flake }; }; outputs = inputs@{ # ... json, # <-- add this argument # ... }: let # ... initial definitions here ... submodules = [ { src = stdlib; dest = "external/stdlib"; } { src = json; # <-- add this here dest = "external/json"; } ]; # ... in { # ... }; }
-
Run
nix flake lock
to update yourflake.lock
to include the new submodule input.
Finally, make sure everything is working by running:
-
glistix build
, to update themanifest.toml
and ensure your package builds; -
nix build
, to ensure your package can still be built from the flake.
Publishing to Hex with patches
Normally, patching packages wouldn't let you publish your packages to Hex, since you cannot depend on local packages as a Hex package (you must depend on other Hex packages). Therefore, as a temporary workaround, Glistix added a [glistix.preview.hex-patch]
setting to gleam.toml
, where you can override your package's dependencies once published to Hex. This allows you to replace a local dependency with a Hex dependency, but only when publishing. For example:
# ...
[dependencies]
# While developing, depend on your local patch of the stdlib...
gleam_stdlib = { path = "./external/stdlib" }
[glistix.preview.hex-patch]
# However, once published to Hex, your package will depend
# on the regular stdlib instead, for compatibility with
# other packages.
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
As such, users of your package will be responsible for patching by themselves, but we expect patching to only be truly necessary for core packages (e.g. gleam_stdlib
), which most Glistix users will have to patch anyway (until we get proper requirement overriding).
Dealing with local package conflicts when patching
The procedure above works as local dependencies always override transitive Hex dependencies. However, they do not override other local dependencies with the same name by default.
For example, if you clone both glistix/stdlib
and glistix/json
repositories as submodules to external/stdlib
and external/json
respectively, and depend on them on gleam.toml
via gleam_stdlib = { path = "./external/stdlib" }
and gleam_json = { path = "./external/json" }
respectively, you will get an error because gleam_json
depends on gleam_stdlib
at external/json/external/stdlib
, while you depend on gleam_stdlib
at external/stdlib
. In other words, there are conflicting local dependencies.
To solve this, while a proper requirement overriding system isn't in place, you can, as a temporary workaround, add the setting below to the root package's gleam.toml
(i.e. your project's gleam.toml
) so that its version of gleam_stdlib
is prioritized by the compiler:
[glistix.preview]
# Ensure the root package's local dependency
# on 'gleam_stdlib' is prioritized.
local-overrides = ["gleam_stdlib"]
That way, Glistix will know how to solve the conflict (use gleam_stdlib
from external/stdlib
, not from external/json/external/stdlib
). Note that this setting is ignored for non-root packages (dependencies cannot apply local-overrides
).