Trying to create a derivation from a string

Short version:

How do I get this code snippet to work:

let                           
  pkgs = import <nixpkgs> {};
  packageName = "python312Packages.grip";
in
  pkgs.${packageName}

If you replace packageName value to something like python3, it would work as expected.

nix-repl> let                           
            pkgs = import <nixpkgs> {};
            packageName = "python312Packages.grip";                                   in
          pkgs.${packageName}
error: attribute '"python312Packages.grip"' missing
       at «string»:5:1:
            4| in
            5| pkgs.${packageName}
             | ^

nix-repl> let                                       
            pkgs = import <nixpkgs> {};             
            packageName = "python3";                
          in
          pkgs.${packageName}
«derivation /nix/store/nfqzgvmajgyf3h5hchs03ngs99h0zaqn-python3-3.12.7.drv»
lib.attrByPath ["python312Packages" "grip"] null pkgs

The long version to the same question is as follows:

I have a JSON file in a folder:

{
  "packages": [
    "ocamlPackages.z3",
    "ocaml"
  ]
}

and have this Flake

{
  description = "Testing";

  inputs = {
    nixpkgs = {
      # This revision: 24th Nov 2024
      # From branch: nixpkgs-unstable
      url = "github:nixos/nixpkgs?rev=8edf06bea5bcbee082df1b7369ff973b91618b8d";
    };

    flake-utils = {
      url = "github:numtide/flake-utils";
    };
  };

  outputs = inputs @
    {
      self,
      nixpkgs,
      flake-utils,
      ...
    }: 
  flake-utils.lib.eachDefaultSystem (system: let
    pkgs = import nixpkgs {inherit system;};
    basePath = builtins.getEnv("PWD");
    packageConfig = builtins.fromJSON (builtins.readFile "${basePath}/packages.json");
  in rec {
    dependencies = builtins.map (pkg: pkgs."${pkg}") packageConfig.packages;

    packages.my_zsh = with pkgs;
      stdenv.mkDerivation rec {
        version = "1.0.0";
        name = "my_zsh";
        inherit system;
        src = self;
        buildInputs = [zsh] ++ dependencies;
        nativeBuildInputs = [makeWrapper];
        phases = ["installPhase"];

        installPhase = ''
          mkdir -p "$out/bin"
          mkdir -p "$out/config"

          makeWrapper "${zsh}/bin/zsh" "$out/bin/my_zsh" \
            --set PWZSH 1 \
            --prefix PATH : "$out/bin:"${
              pkgs.lib.makeBinPath dependencies
            }
          '';
      };

    apps.my_zsh = flake-utils.lib.mkApp {
      drv = packages.my_zsh;
      name = "my_zsh";
      exePath = "/bin/my_zsh";
    };

    packages.default = packages.my_zsh;
    apps.default = apps.my_zsh;
  });
}

I want to pick the package names from the settings file and populate a shell session using those packages. I run this flake using nix run --impure .

I think this problem and the above listed problem are equivalent. Removing the "ocamlPackages.z3", line from the JSON will successfully run this flake.

Play around with the lib.splitString and lib.attrByPath functions.

This worked!

let
  pkgs = import <nixpkgs> {};
  lib = pkgs.lib; packageName = "python312Packages.grip";
in
  lib.attrByPath (lib.strings.splitString "." packageName) null pkgs 

Thanks Philip!

1 Like

No worries. Note that the null there is the default if the thing can’t be looked up. Another good default is (throw "Cannot find ${packageName}") or something similar.

nix-repl> f = packageName: lib.attrByPath (lib.splitString "." packageName) (throw "Cannot find ${packageName}") pkgs

nix-repl> f "python312Packages.grip"
«derivation /nix/store/y62ck6lif4njn7bv5dxd4gn2p81w1c60-python3.12-grip-4.6.1.drv»

nix-repl> f "python312Packages.grip2"
error:
       … while calling the 'throw' builtin
         at «string»:1:65:
            1|  packageName: lib.attrByPath (lib.splitString "." packageName) (throw "Cannot find ${packageName}") pkgs
             |                                                                 ^

       error: Cannot find python312Packages.grip2
1 Like