01bad09eed
This is not the final layout yet, but makes it so that my top-level attribute set is no longer overlaid into nixpkgs itself. This is useful for other people who are importing my monorepo.
81 lines
2.2 KiB
Nix
81 lines
2.2 KiB
Nix
# TODO(tazjin): if there's a default.nix, keep traversing but don't import .nix files?
|
|
# TODO(tazjin): avoid {} by only calling functions *after* checking what they are
|
|
|
|
args: initPath:
|
|
|
|
let
|
|
inherit (builtins)
|
|
attrNames
|
|
filter
|
|
head
|
|
isString
|
|
length
|
|
listToAttrs
|
|
map
|
|
match
|
|
readDir
|
|
split
|
|
tail
|
|
toString;
|
|
|
|
attrsToList = attrs: map (name: {
|
|
inherit name;
|
|
value = attrs."${name}";
|
|
}) (attrNames attrs);
|
|
|
|
isFile = s: s == "regular";
|
|
isDir = s: s == "directory";
|
|
|
|
joinPath = p: f: p + ("/" + f);
|
|
|
|
isNixFile = file:
|
|
let res = match "(.*)\.nix" file;
|
|
in if res == null then null else head res;
|
|
|
|
filterNixFiles = dir:
|
|
let files = filter (e: isFile e.value && e.name != "default.nix") dir;
|
|
nixFiles = map (f: {
|
|
# Name and value are intentionally flipped to get the
|
|
# correct attribute set structure back out
|
|
name = isNixFile f.name;
|
|
value = f.name; # i.e. the path
|
|
}) files;
|
|
in filter (f: isString f.name) nixFiles;
|
|
|
|
# Some packages require that their position in the tree is passed in
|
|
# as an argument. To do this the root directory (i.e. $PWD during
|
|
# imports) is chopped off the front of the path components in
|
|
# imports.
|
|
pathParts = p: tail (filter isString (split "/" (toString p)));
|
|
initLen = length (pathParts ./.);
|
|
drop = n: l:
|
|
if n == 0
|
|
then l
|
|
else if l == []
|
|
then []
|
|
else drop (n - 1) (tail l);
|
|
|
|
argsWithPath = args: parts: args // {
|
|
locatedAt = drop initLen parts;
|
|
};
|
|
|
|
traverse = path: dir:
|
|
let nixFiles = filterNixFiles dir;
|
|
imported = map (f: {
|
|
inherit (f) name;
|
|
value = import (joinPath path f.value) args;
|
|
}) nixFiles;
|
|
dirs = map (d: {
|
|
inherit (d) name;
|
|
value = readTree (joinPath path d.name);
|
|
}) (filter (e: isDir e.value) dir);
|
|
in listToAttrs (imported ++ dirs);
|
|
|
|
importOr = path: dir: f:
|
|
let contents = f path (attrsToList dir);
|
|
in if dir ? "default.nix"
|
|
then import path (argsWithPath args (pathParts path)) // contents
|
|
else contents;
|
|
|
|
readTree = path: importOr path (readDir path) traverse;
|
|
in readTree initPath
|