Generate Nix packages information with Nix expressions
System requirement is Nix.
Save this Nix expression code to
or some other file, every function is commented with examples:1
packages.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
let
# set allowBroken and allowUnfree to true, so that we minimize error output later on
pkgs = import <nixpkgs> { config = { allowBroken = true; allowUnfree = true; }; };
# catch exceptions on isDerivation function
tryDrv = a: builtins.tryEval (pkgs.lib.isDerivation a);
isDerivation = a: let t = tryDrv a; in t.success && t.value == true;
# catch exceptions on isAttrs function
tryAttrs = a: builtins.tryEval (pkgs.lib.isAttrs a);
isAttrs = a: let t = tryAttrs a; in t.success && t.value == true;
# iterate through attributeset's names (one-level deep)
# example:
# mapValues (name: value: name) pkgs
# => [ "bash" "zsh" "gitFull" ... ]
mapValues = f: set: (
map (attr: f attr (builtins.getAttr attr set)) (builtins.attrNames set)
);
# recurse into attributeset (search for derivations)
# example #1:
# mapAttrsRecursiveDrv
# (path: value: path) pkgs.pythonPackages ["pkgs" "pythonPackages"] []
# => [ [ "pkgs" "pythonPackages" "searx" ] [ "pkgs" "pythonPackages" "tarman" ] ... ]
# example #2:
# mapAttrsRecursiveDrv (path: value: path) pkgs ["pkgs"] []
# => [ [ "pkgs" "bash" ] [ "pkgs" "zsh" ] [ "pkgs" "pythonPackages" "searx" ] [ "pkgs" "pythonPackages" "tarman" ] ... ]
mapAttrsRecursiveDrv = f: set: path: list:
let
recurse = path: set: visitList:
let
visitedFun = a: path:
let
isAtt = isAttrs a;
isDrv = isDerivation a;
success = if isAtt && !isDrv then pkgs.lib.any (
element: element == a
) visitList else false;
not = !success;
list = if not then (visitList ++ [a]) else visitList;
in
{ inherit list not isAtt isDrv; };
g = name: value:
let
visited = visitedFun value path;
in
if visited.isDrv then
f (path ++ [name]) value
else if (visited.not) && (checkForEnterable value) then
recurse (path ++ [name]) value visited.list
else
{
error = "not derivation or not enterable";
attrPath = pkgs.lib.concatStringsSep "." (path ++ [name]);
};
in mapValues g set;
in (recurse path set list);
# check if attributeste has attribute named "recurseForDerivations"
# therefore has derivations
# examples:
# checkForEnterable pkgs.bash => false
# checkForEnterable pkgs.pythonPackages => true
checkForEnterable = a:
let
t = builtins.tryEval (
(pkgs.lib.isAttrs a) &&
(pkgs.lib.hasAttr "recurseForDerivations" a)
);
in
(t.success && t.value == true);
# main function
# example:
# recurseInto "pkgs.pythonPackages"
# => [
# { attrPath = "pkgs.pythonPackages.tarman"; name = "python2.7-tarman-0.1.3"; out = "/nix/store/<hash>-python2.7-tarman-0.1.3"; }
# { attrPath = "pkgs.pythonPackages.searx"; name = "python2.7-searx-dev"; out = "/nix/store/<hash>-python2.7-searx-dev"; }
# { attrPath = "pkgs.pythonPackages.isPy27"; error = "not derivation or not enterable"; }
# ]
recurseInto = attrPath:
let
path = pkgs.lib.splitString "." attrPath;
attrs = pkgs.lib.getAttrFromPath path pkgs;
in
pkgs.lib.flatten (mapAttrsRecursiveDrv
(path: value:
let
attrPath = pkgs.lib.concatStringsSep "." path;
tOutPath = builtins.tryEval value.outPath;
tName = builtins.tryEval value.name;
in
(if tOutPath.success && tName.success then
{ out = tOutPath.value; name = tName.value; inherit attrPath; }
else
{ error = "tryEval failed"; inherit attrPath; })
)
attrs
path
[]);
# just strips away values with attribute "error"
removeErrors = builtins.filter (x: (if pkgs.lib.hasAttr "error" x then
(builtins.trace "error '${x.error}' at attribute ${x.attrPath}" false)
else true));
in
removeErrors (recurseInto "pkgs")
And run it as:
It should take up to 15 seconds (well that depends on your system).
Output should look like this, but much more of it (around 3MB if you redirect the stdout to file):
Every valid item in list has
which represent attribute path in 1
attrPath
structure.1
pkgs
If there are some errors, they will be in
attribute, always beside 1
error
.
There are two error messages currently:1
attrPath
: when attribute set can not be recursable further.1
not derivation or not enterable
: when there is eval error on attribute.outPath or attribute.name.1
tryEval failed
And if there is no error for that package, following attributes are available:
: path, with 1
attrPath
seperators, this can be used for package id (this never changes per package).1
.
: package name with version.1
name
: out path, this can be used as as unique id further on (can be different per same version of package).1
out
The last line of
code can be changed from1
packages.nix
to
to get JSON encoded string to feed data to some other application.
If you need some other info beside
, 1
attrPath
and 1
name
, just add attributes around the line 1
out
. For example if you want to add 1
96
attribute add 1
meta
like so:1
meta = value.meta;
But beware, this can increase output data size correspondly.
This is it …
.. and feel free to leave a comment.