pasta - nixpkgs evaluation analysis: init

This commit is contained in:
hsjobeki 2023-11-23 14:10:07 +01:00
parent 2f1aead693
commit eac456913d
No known key found for this signature in database
8 changed files with 296 additions and 12 deletions

View File

@ -16,6 +16,22 @@
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
@ -95,19 +111,58 @@
"type": "github"
}
},
"nixpkgs": {
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1700390070,
"narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb",
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat",
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1700475714,
"narHash": "sha256-8OXrkNaIpdtErfLN4u1Ew1IThTbk6n8POYRQfNatkSA=",
"owner": "hsjobeki",
"repo": "nix",
"rev": "9bf2153e696d88c6beb8e34709bb743af5cdd940",
"type": "github"
},
"original": {
"owner": "hsjobeki",
"ref": "feat/positions",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1695283060,
"narHash": "sha256-CJz71xhCLlRkdFUSQEL0pIAAfcnWFXMzd9vXhPrnrEg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "31ed632c692e6a36cfc18083b88ece892f863ed4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-master": {
@ -125,6 +180,22 @@
"type": "indirect"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1685801374,
@ -141,9 +212,24 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1700390070,
"narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
@ -169,7 +255,8 @@
"inputs": {
"flake-parts": "flake-parts",
"floco": "floco",
"nixpkgs": "nixpkgs",
"nix": "nix",
"nixpkgs": "nixpkgs_2",
"nixpkgs-master": "nixpkgs-master",
"pre-commit-hooks": "pre-commit-hooks",
"treefmt-nix": "treefmt-nix"

View File

@ -4,6 +4,9 @@
nixpkgs.url = "nixpkgs/nixos-unstable";
nixpkgs-master.url = "nixpkgs/master";
# A custom nix verison, to introspect lambda values.
nix.url = "github:hsjobeki/nix/?ref=feat/positions";
pre-commit-hooks = {
url = "github:cachix/pre-commit-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
@ -21,6 +24,11 @@
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } ({ ... }: {
systems = [ "x86_64-linux" ];
imports = [ ./devShell.nix ./preCommit.nix ./website/flake-module.nix ];
imports = [
./devShell.nix
./preCommit.nix
./website/flake-module.nix
./pasta/flake-module.nix
];
});
}

25
pasta/README.md Normal file
View File

@ -0,0 +1,25 @@
# Dough -> Pasta
We have to make our pasta from the Nixpkgs raw dough.
Analyse arbitrary nix expressions.
Contains tools, such as nix functions, that allow us to introspect the nix language.
Evaluating expressions and their metadata allows for precise documentation building
Evaluation requires a custom nix version available via devShell '.#pastaMaker'
Analyses a given path in the expression tree.
- Recursive tool, that works well with e.g., `pkgs.lib` or other sets that don't have hard evaluation errors inside them. Note: All kinds of recursions are reliably avoided.
- Flat tool, that works on attribute sets, without recursing it. Since both tools are not lazy they require at least the analyzed value to have no hard errors.
## Features
- Finding lambdas recursively
- Adding metadata about lambdas
- Attribute Source Position
- Lambda Source position
- Count Partially Applied
- ...

11
pasta/default.nix Normal file
View File

@ -0,0 +1,11 @@
{ pkgs, nixpkgs, nix, ... }:
pkgs.stdenv.mkDerivation {
name = "pasta";
src = ./src;
nativeBuildInputs = [ nix ];
buildPhase = ''
nix-instantiate --eval --strict --json --store $PWD \
eval.nix --arg 'pkgs' 'import ${nixpkgs} {}' -A docs.lib \
> $out
'';
}

12
pasta/flake-module.nix Normal file
View File

@ -0,0 +1,12 @@
{ inputs, ... }: {
perSystem = { self', inputs', pkgs, ... }:
let
nix = inputs'.nix.packages.nix-clangStdenv;
nixpkgs = inputs.nixpkgs-master;
in {
packages = {
pasta = pkgs.callPackage ./default.nix { inherit nixpkgs nix pkgs; };
};
devShells.pastaMaker = pkgs.callPackage ./shell.nix { inherit pkgs nix; };
};
}

7
pasta/shell.nix Normal file
View File

@ -0,0 +1,7 @@
{ pkgs, nix, ... }:
pkgs.mkShell {
buildInputs = [ nix ];
shellHook = ''
echo "using a custom nix build: ${nix}"
'';
}

37
pasta/src/eval.nix Normal file
View File

@ -0,0 +1,37 @@
{ pkgs ?
# import (builtins.fetchTree {
# repo = "nixpkgs";
# ref = "migrate-doc-comments";
# owner = "hsjobeki";
# type = "github";
# }) {},
import (builtins.fetchTree {
repo = "nixpkgs";
ref = "master";
owner = "nixos";
type = "github";
}) { }, }:
let
inherit pkgs;
inherit (pkgs) lib;
tools = import ./tools.nix { inherit lib; };
inherit (tools) getDocsFromSet collectFns toFile;
# Contains seperate sets of metadata.
# which then allows running seperate evaluations. Once at a time for better error tracing and memory management.
docs = {
############# Recusive analysis sets
lib = collectFns lib { initialPath = [ "lib" ]; };
rustTools = collectFns pkgs.pkgs.rustPackages {
initialPath = [ "pkgs" "rustPackages" ];
};
stdenvTools = getDocsFromSet pkgs.stdenv [ "pkgs" "stdenv" ];
############# Non-recursive analysis sets
pkgs = getDocsFromSet pkgs [ "pkgs" ];
dockerTools = getDocsFromSet pkgs.pkgs.dockerTools [ "pkgs" "dockerTools" ];
pythonTools =
getDocsFromSet pkgs.pkgs.pythonPackages [ "pkgs" "pythonPackages" ];
};
in { inherit tools pkgs docs toFile; }

97
pasta/src/tools.nix Normal file
View File

@ -0,0 +1,97 @@
{ lib }:
let
force = v: (builtins.tryEval v).value;
dropBack = l: lib.reverseList (lib.drop 1 (lib.reverseList l));
getDocs = parent: name:
let
lambda = builtins.lambdaMeta parent.${name};
attr = { position = builtins.unsafeGetAttrPos name parent; };
in { inherit lambda attr; };
/* *
Recursively collect documentation for all values
*/
collectFns = set:
{ initialPath ? [ ], limit ? null, }:
let
filterFns = builtins.filter (item: item.type == "lambda");
getFnDocs = map (fn: {
path = initialPath ++ fn.path;
inherit (fn) docs;
});
in getFnDocs (filterFns (builtins.genericClosure {
startSet = [{
__initial = true;
key = [ ];
value = set;
path = [ ];
name = "?";
depth = 0;
type = "?";
parent = null;
docs = null;
}];
operator = item:
let
currVal = force item.value;
# Dont traverse into: "derivations", "option types"
in if lib.isDerivation currVal || lib.isOptionType currVal || currVal
== null then
[ ]
# Doc support for named key value pairs (sets)
else if builtins.typeOf currVal == "set" then
map (name:
# NEXT ITEM
let
nextVal = force item.value.${name};
# calling lib.unique prevents infinite recursion
path = lib.unique (item.key ++ [ name ]);
in if lib.isDerivation nextVal || name == "__functor"
|| (limit != null && item.depth >= limit) then
# skipping some values by
# returning the previous item
item
else {
key = path;
value = item.value.${name};
# Propagate some values.
type = if lib.isFunction nextVal then
"lambda"
else
builtins.typeOf nextVal;
docs = getDocs (lib.attrByPath (dropBack path) null set) name;
inherit name path;
parent = currVal;
depth = item.depth + 1;
}) (builtins.attrNames item.value)
else
[ ];
}));
# Convinient wrapper for debugging.
toFile = thing: builtins.toFile "data.json" (builtins.toJSON thing);
# Non-rcusively collect docs of all functions present in a set
getDocsFromSet = s: path:
let
docs = lib.pipe s [
# Filter out all attributes that are not a function or __functor
(lib.filterAttrs (v: v: lib.isFunction (force v)))
# Call getDocs for each name value pair
(lib.mapAttrs (n: v: getDocs s n))
];
in lib.pipe docs [
# Transform into list
builtins.attrNames
# Collect all values
(builtins.foldl' (res: name:
res ++ [{
path = path ++ [ name ];
docs = docs.${name};
}]) [ ])
];
in { inherit toFile collectFns getDocsFromSet; }