Nix FFI for all languages. This is a collection of packages built upon a solid interface for interacting with the nix evaluator in Rust, through the recently added Nix C-Api, and a couple of wrapper libraries so that it can be seamlessly accessed from other languages as well.
It is very much in alpha and constantly changing, so don’t expect the API’s to be stable as of now. Still, the general interface for all libraries should stay relatively intact:
- Build a nix evaluator instance with given settings, where the default is an empty one. You may pass per-evaluator settings to customize behavior; for instance, like
extra-experimental-features="flakes"
is needed in order to callbuiltins.getFlake
, used insidestate.eval_flake
. - Use said nix evaluator to load a nix file, flake or string.
- Interact with said nix value.
If it is a “simple” value, ie. string, number, float, it should translate to a builtin value, while lists, attribute sets and functions will have wrappers around them to implement some niceties, like lazy evaluation by default, special accessors and derivation
build()
helpers.
Currently, only Rust and Python libraries are defined.
This crate uses nocargo to build directly through nix, without the use of cargo. If you want to use cargo, you may drop in the shell (through
nix develop
).
nix_for_py
exposes only one function, nix_evaluator
, that is used to instantiate a nix evaluator, that will be used to evaluate nix code:
from nix_for_py import nix_evaluator
evaluator = nix_evaluator(settings={'experimental-features':'flakes'})
nixpkgs = evaluator.eval_flake('github:nixos/nixpkgs')
hello = nixpkgs.legacyPackages['x86_64-linux'].hello.build()
print(hello)
which should print
{'out': '/nix/store/26xbg1ndr7hbcncrlf9nhx5is2b25d13-hello-2.12.1'}
The best way to utilize it is to use it through the overlay
exposed by this flake, which will add the nix_for_py
package to all python versions of nixpkgs.
pkgs = import nixpkgs {
overlays = [ nix-forall.overlays.${system}.default ];
};
and then create a python instance with it:
pkgs.mkShell {
buildInputs = [
(pkgs.python3.withPackages (p: [ p.nix-for-py ]))
];
};
The main way to invoke an evaluator is through the NixSettings
builder. It lets you set settings which customize the behavior of the evaluator, and to finalize it, you must give it a store path, which will return the instance of the evaluator.
use nix_for_rust::settings::NixSettings;
pub fn main() -> anyhow::Result<()> {
let mut state = NixSettings::default()
.with_setting("experimental-features", "flakes")
.with_default_store()?;
let valid_pkgs = state.eval_flake("github:NixOS/nixpkgs")?
.get("legacyPackages")?
.get("x86_64-linux")?
.items()?
.filter_map(|(_name, term)| term.ok())
.count();
println!("Rejoice! You can build {valid_pkgs} packages from nixpkgs.");
Ok(())
}