mirror of
https://github.com/hsjobeki/noogle.git
synced 2024-12-18 19:31:37 +03:00
Merge pull request #53 from nix-community/feat/nix-plugin
Refactoring into flake modules
This commit is contained in:
commit
9f22813c7f
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
with:
|
||||
name: nix-community
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- run: nix build
|
||||
- run: nix build .#ui -L
|
||||
- name: Publish to Cloudflare Pages
|
||||
if: github.event_name == 'push'
|
||||
uses: cloudflare/pages-action@1
|
||||
@ -29,7 +29,7 @@ jobs:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: ${{ env.cloudflare_project }}
|
||||
directory: ./result/static
|
||||
directory: ./result/lib/node_modules/noogle/out
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Publish to Cloudflare Pages
|
||||
id: publish
|
||||
@ -39,7 +39,7 @@ jobs:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: noogle
|
||||
directory: ./result/static
|
||||
directory: ./result/lib/node_modules/noogle/out
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: pr-${{ github.event.pull_request.number }}
|
||||
- uses: peter-evans/create-or-update-comment@v3
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,18 +1,11 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# nix
|
||||
.floco
|
||||
.direnv/
|
||||
result
|
||||
result-*
|
||||
|
||||
.pre-commit-config.yaml
|
||||
|
||||
# dream2nix
|
||||
|
||||
## dream2nix internal cache
|
||||
.dream2nix
|
10
devShell.nix
Normal file
10
devShell.nix
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
perSystem = { pkgs, self', config, ... }: {
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [ pkgs.treefmt ];
|
||||
shellHook = ''
|
||||
${self'.checks.pre-commit-check.shellHook}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
388
flake.lock
388
flake.lock
@ -1,109 +1,22 @@
|
||||
{
|
||||
"nodes": {
|
||||
"all-cabal-json": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1665552503,
|
||||
"narHash": "sha256-r14RmRSwzv5c+bWKUDaze6pXM7nOsiz1H8nvFHJvufc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "all-cabal-json",
|
||||
"rev": "d7c0434eebffb305071404edcf9d5cd99703878e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "hackage",
|
||||
"repo": "all-cabal-json",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1670900067,
|
||||
"narHash": "sha256-VXVa+KBfukhmWizaiGiHRVX/fuk66P8dgSFfkVN4/MY=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "59b31b41a589c0a65e4a1f86b0e5eac68081468b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devshell": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1663445644,
|
||||
"narHash": "sha256-+xVlcK60x7VY1vRJbNUEAHi17ZuoQxAIH4S4iUFUGBA=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "e3dc3e21594fe07bdb24bdf1c8657acaa4cb8f66",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"dream2nix": {
|
||||
"inputs": {
|
||||
"all-cabal-json": "all-cabal-json",
|
||||
"crane": "crane",
|
||||
"devshell": "devshell",
|
||||
"drv-parts": "drv-parts",
|
||||
"flake-parts": "flake-parts",
|
||||
"flake-utils-pre-commit": "flake-utils-pre-commit",
|
||||
"ghc-utils": "ghc-utils",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"mach-nix": "mach-nix",
|
||||
"nix-pypi-fetcher": "nix-pypi-fetcher",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgsV1": "nixpkgsV1",
|
||||
"poetry2nix": "poetry2nix",
|
||||
"pre-commit-hooks": "pre-commit-hooks",
|
||||
"pruned-racket-catalog": "pruned-racket-catalog"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1679378030,
|
||||
"narHash": "sha256-ThQCgw1YGcftpWv5hbhxLRCWENWvUBXPMzNUF0XLzQQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "dream2nix",
|
||||
"rev": "f04f032418555119f1c9c6c5cb60809b48305191",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "dream2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"drv-parts": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": [
|
||||
"dream2nix",
|
||||
"flake-parts"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"dream2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1679210066,
|
||||
"narHash": "sha256-0t6UZrLmIEsH3R8Jk8mo2XFGIPJmSWLsCR6HlSp3va8=",
|
||||
"owner": "davhau",
|
||||
"repo": "drv-parts",
|
||||
"rev": "38f59f9eb2dbcf9ad6b939ec47c005033445f13e",
|
||||
"lastModified": 1700327093,
|
||||
"narHash": "sha256-OgYvlBABxJYWhZ/HBd0bPVcIEkT+xDhDCpRYqtVhYWY=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "ae7cd510e508ee03d792005c2f1c0a3ff25ecb80",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "davhau",
|
||||
"repo": "drv-parts",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@ -142,16 +55,15 @@
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"dream2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1675933616,
|
||||
"narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=",
|
||||
"lastModified": 1698882062,
|
||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "47478a4a003e745402acf63be7f9a092d51b83d7",
|
||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -161,12 +73,15 @@
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"lastModified": 1685518550,
|
||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -175,37 +90,26 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils-pre-commit": {
|
||||
"floco": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1644229661,
|
||||
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
|
||||
"lastModified": 1694873346,
|
||||
"narHash": "sha256-Uvh03bg0a6ZnNWiX1Gb8g+m343wSJ/wb8ryUASt0loc=",
|
||||
"owner": "aakropotkin",
|
||||
"repo": "floco",
|
||||
"rev": "d16bd444ab9d29a6640f52ee4e43a66528e07515",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"owner": "aakropotkin",
|
||||
"repo": "floco",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"ghc-utils": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1662774800,
|
||||
"narHash": "sha256-1Rd2eohGUw/s1tfvkepeYpg8kCEXiIot0RijapUjAkE=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "bb3a2d3dc52ff0253fb9c2812bd7aa2da03e0fea",
|
||||
"revCount": 1072,
|
||||
"type": "git",
|
||||
"url": "https://gitlab.haskell.org/bgamari/ghc-utils"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://gitlab.haskell.org/bgamari/ghc-utils"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@ -227,75 +131,67 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gomod2nix": {
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1627572165,
|
||||
"narHash": "sha256-MFpwnkvQpauj799b4QTBJQFEddbD02+Ln5k92QyHOSk=",
|
||||
"owner": "tweag",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "67f22dd738d092c6ba88e420350ada0ed4992ae8",
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tweag",
|
||||
"repo": "gomod2nix",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mach-nix": {
|
||||
"flake": false,
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1634711045,
|
||||
"narHash": "sha256-m5A2Ty88NChLyFhXucECj6+AuiMZPHXNbw+9Kcs7F6Y=",
|
||||
"owner": "DavHau",
|
||||
"repo": "mach-nix",
|
||||
"rev": "4433f74a97b94b596fa6cd9b9c0402104aceef5d",
|
||||
"lastModified": 1700475714,
|
||||
"narHash": "sha256-8OXrkNaIpdtErfLN4u1Ew1IThTbk6n8POYRQfNatkSA=",
|
||||
"owner": "hsjobeki",
|
||||
"repo": "nix",
|
||||
"rev": "9bf2153e696d88c6beb8e34709bb743af5cdd940",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "mach-nix",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nix-pypi-fetcher": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1669065297,
|
||||
"narHash": "sha256-UStjXjNIuIm7SzMOWvuYWIHBkPUKQ8Id63BMJjnIDoA=",
|
||||
"owner": "DavHau",
|
||||
"repo": "nix-pypi-fetcher",
|
||||
"rev": "a9885ac6a091576b5195d547ac743d45a2a615ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "DavHau",
|
||||
"repo": "nix-pypi-fetcher",
|
||||
"owner": "hsjobeki",
|
||||
"ref": "feat/positions",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1665580254,
|
||||
"narHash": "sha256-hO61XPkp1Hphl4HGNzj1VvDH5URt7LI6LaY/385Eul4=",
|
||||
"lastModified": 1695283060,
|
||||
"narHash": "sha256-CJz71xhCLlRkdFUSQEL0pIAAfcnWFXMzd9vXhPrnrEg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f634d427b0224a5f531ea5aa10c3960ba6ec5f0f",
|
||||
"rev": "31ed632c692e6a36cfc18083b88ece892f863ed4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-master": {
|
||||
"locked": {
|
||||
"lastModified": 1695278169,
|
||||
"narHash": "sha256-0j9Rf6vLOueasqDaG/tTNZ3sDNrYRMxLxLDwbygxgWI=",
|
||||
"lastModified": 1701287633,
|
||||
"narHash": "sha256-F0V7p4hXvubyF9vhkVwB1rWGUh7bfg0IC9GM7ID1FQM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "53204fe466f8eb622686d881287111396d97e8e9",
|
||||
"rev": "2c01cd06afc2e327343b047e27e172ba488d98f8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -304,44 +200,61 @@
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"nixpkgs-migrated": {
|
||||
"locked": {
|
||||
"lastModified": 1678872516,
|
||||
"narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
|
||||
"owner": "NixOS",
|
||||
"lastModified": 1699884649,
|
||||
"narHash": "sha256-HF1iNm+SqZJtUgoi57Mk21jDsgeybIcopDwaNFLqexc=",
|
||||
"owner": "hsjobeki",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8",
|
||||
"rev": "047dce513a20231fde99b1e9b950ab6b562b27b0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-22.11",
|
||||
"owner": "hsjobeki",
|
||||
"ref": "migrate-doc-comments",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgsV1": {
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1678500271,
|
||||
"narHash": "sha256-tRBLElf6f02HJGG0ZR7znMNFv/Uf7b2fFInpTHiHaSE=",
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5eb98948b66de29f899c7fe27ae112a47964baf8",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-22.11",
|
||||
"type": "indirect"
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1685801374,
|
||||
"narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1679262748,
|
||||
"narHash": "sha256-DQCrrAFrkxijC6haUzOC5ZoFqpcv/tg2WxnyW3np1Cc=",
|
||||
"lastModified": 1700390070,
|
||||
"narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "60c1d71f2ba4c80178ec84523c2ca0801522e0a6",
|
||||
"rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -350,49 +263,7 @@
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"poetry2nix": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1666918719,
|
||||
"narHash": "sha256-BkK42fjAku+2WgCOv2/1NrPa754eQPV7gPBmoKQBWlc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"rev": "289efb187123656a116b915206e66852f038720e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "1.36.0",
|
||||
"repo": "poetry2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"dream2nix",
|
||||
"flake-utils-pre-commit"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"dream2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1646153636,
|
||||
"narHash": "sha256-AlWHMzK+xJ1mG267FdT8dCq/HvLCA6jwmx2ZUy5O8tY=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "b6bc0b21e1617e2b07d8205e7fae7224036dfa4b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks_2": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-utils": "flake-utils",
|
||||
@ -403,11 +274,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1678976941,
|
||||
"narHash": "sha256-skNr08frCwN9NO+7I77MjOHHAw+L410/37JknNld+W4=",
|
||||
"lastModified": 1700064067,
|
||||
"narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "32b1dbedfd77892a6e375737ef04d8efba634e9e",
|
||||
"rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -416,29 +287,52 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pruned-racket-catalog": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1672537287,
|
||||
"narHash": "sha256-SuOvXVcLfakw18oJB/PuRMyvGyGG1+CQD3R+TGHIv44=",
|
||||
"owner": "nix-community",
|
||||
"repo": "pruned-racket-catalog",
|
||||
"rev": "c8b89557fb53b36efa2ee48a769c7364df0f6262",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "catalog",
|
||||
"repo": "pruned-racket-catalog",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"dream2nix": "dream2nix",
|
||||
"crane": "crane",
|
||||
"flake-parts": "flake-parts",
|
||||
"floco": "floco",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-master": "nixpkgs-master",
|
||||
"pre-commit-hooks": "pre-commit-hooks_2"
|
||||
"nixpkgs-migrated": "nixpkgs-migrated",
|
||||
"pre-commit-hooks": "pre-commit-hooks",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1699786194,
|
||||
"narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
144
flake.nix
144
flake.nix
@ -1,127 +1,43 @@
|
||||
{
|
||||
description = "Noogle.dev | Discover the nix api surface";
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
nixpkgs-master.url = "nixpkgs/master";
|
||||
nixpkgs-migrated.url = "github:hsjobeki/nixpkgs/?ref=migrate-doc-comments";
|
||||
|
||||
dream2nix.url = "github:nix-community/dream2nix";
|
||||
# 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";
|
||||
};
|
||||
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
crane.url = "github:ipetkov/crane";
|
||||
crane.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
floco.url = "github:aakropotkin/floco";
|
||||
floco.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
outputs = { self, nixpkgs, pre-commit-hooks, dream2nix, nixpkgs-master }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
websiteName = (builtins.fromJSON (builtins.readFile ./website/package.json)).name;
|
||||
inherit (self.packages.${system}) indexer nixpkgs-data builtins-data;
|
||||
|
||||
prepareData = prefix: ''
|
||||
cp -f ${nixpkgs-data.lib} ${prefix}/lib.json
|
||||
cp -f ${nixpkgs-data.trivial_builders} ${prefix}/trivial-builders.json
|
||||
cp -f ${nixpkgs-data.build_support} ${prefix}/build_support.json
|
||||
cp -f ${builtins-data}/lib/data.json ${prefix}/builtins.json
|
||||
'';
|
||||
|
||||
dream2nixOutput = dream2nix.lib.makeFlakeOutputs {
|
||||
systems = [ system ];
|
||||
projects = ./projects.toml;
|
||||
config.projectRoot = ./.;
|
||||
source = ./.;
|
||||
|
||||
packageOverrides = {
|
||||
${websiteName}.staticPage = {
|
||||
preBuild = prepareData "models/data";
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/static
|
||||
cp -r ./out/* $out/static/
|
||||
cp -r ./ $lib
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
tests.run = {
|
||||
installPhase = "";
|
||||
preBuild = ''
|
||||
ls -la
|
||||
mkdir -p data
|
||||
${prepareData "data"}
|
||||
|
||||
'';
|
||||
doCheck = true;
|
||||
checkPhase = ''
|
||||
ls -la
|
||||
npm run test -- --ci
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.${system} = dream2nixOutput.packages.${system} // {
|
||||
nixpkgs-data = pkgs.stdenv.mkDerivation {
|
||||
pname = "data";
|
||||
version = "0.1.0";
|
||||
description = ''
|
||||
wrapper around the indexer.
|
||||
|
||||
Calls the indexer with '<nixpkgs>'/path.
|
||||
and defines one output for every specified input path
|
||||
|
||||
currently this list is manually maintained below.
|
||||
'';
|
||||
src = nixpkgs-master;
|
||||
outputs = [ "out" "lib" "trivial_builders" "build_support" ];
|
||||
nativeBuildInputs = [ indexer ];
|
||||
buildPhase = ''
|
||||
echo "running nix metadata collect in nixpkgs/lib"
|
||||
${indexer}/bin/indexer --dir ./lib
|
||||
${indexer}/bin/indexer --dir ./pkgs/build-support/trivial-builders
|
||||
${indexer}/bin/indexer --dir ./pkgs/build-support
|
||||
'';
|
||||
installPhase = ''
|
||||
cat lib.json > $lib
|
||||
cat trivial-builders.json > $trivial_builders
|
||||
cat build-support.json > $build_support
|
||||
|
||||
mkdir $out
|
||||
ln -s $lib $out/lib
|
||||
ln -s $trivial_builders $out/trivial_builders
|
||||
ln -s $build_support $out/build_support
|
||||
'';
|
||||
};
|
||||
|
||||
default = self.packages.${system}.noogle;
|
||||
};
|
||||
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [ nodejs-18_x rustfmt rustc cargo clippy ];
|
||||
inputsFrom = [ indexer ];
|
||||
shellHook = ''
|
||||
${prepareData "website/models/data"}
|
||||
${prepareData "tests/data"}
|
||||
${self.checks.${system}.pre-commit-check.shellHook}
|
||||
'';
|
||||
};
|
||||
|
||||
checks.${system} = {
|
||||
pre-commit-check = pre-commit-hooks.lib.${system}.run {
|
||||
src = ./.;
|
||||
hooks = {
|
||||
nixpkgs-fmt.enable = true;
|
||||
statix.enable = true;
|
||||
markdownlint.enable = true;
|
||||
};
|
||||
excludes = [ "indexer/test" ".github" "scripts/data" ];
|
||||
settings = {
|
||||
statix.ignore = [ "indexer/test" ];
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } ({ ... }: {
|
||||
systems = [ "x86_64-linux" ];
|
||||
imports = [
|
||||
./devShell.nix
|
||||
./preCommit.nix
|
||||
./website/flake-module.nix
|
||||
./pasta/flake-module.nix
|
||||
./pesto/flake-module.nix
|
||||
# Deprecated. Will be removed.
|
||||
./indexer/flake-module.nix
|
||||
./scripts/flake-module.nix
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
|
65
indexer/flake-module.nix
Normal file
65
indexer/flake-module.nix
Normal file
@ -0,0 +1,65 @@
|
||||
{ inputs, ... }: {
|
||||
perSystem = { self', inputs', pkgs, system, ... }:
|
||||
let
|
||||
craneLib = inputs.crane.lib.${system};
|
||||
src = craneLib.cleanCargoSource (craneLib.path ./.);
|
||||
|
||||
commonArgs = {
|
||||
inherit src;
|
||||
strictDeps = true;
|
||||
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
|
||||
};
|
||||
|
||||
indexer = craneLib.buildPackage commonArgs;
|
||||
checks = {
|
||||
inherit indexer;
|
||||
indexer-clippy = craneLib.cargoClippy (commonArgs // {
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
});
|
||||
indexer-fmt = craneLib.cargoFmt { inherit src; };
|
||||
indexer-nextest = craneLib.cargoNextest (commonArgs // {
|
||||
partitions = 1;
|
||||
partitionType = "count";
|
||||
});
|
||||
};
|
||||
nixpkgs-data = pkgs.stdenv.mkDerivation {
|
||||
pname = "data";
|
||||
version = "0.1.0";
|
||||
description = ''
|
||||
wrapper around the indexer.
|
||||
|
||||
Calls the indexer with '<nixpkgs>'/path.
|
||||
and defines one output for every specified input path
|
||||
|
||||
currently this list is manually maintained below.
|
||||
'';
|
||||
src = inputs.nixpkgs-master;
|
||||
outputs = [ "out" "lib" "trivial_builders" "build_support" ];
|
||||
nativeBuildInputs = [ indexer ];
|
||||
buildPhase = ''
|
||||
echo "running nix metadata collect in nixpkgs/lib"
|
||||
${indexer}/bin/indexer --dir ./lib
|
||||
${indexer}/bin/indexer --dir ./pkgs/build-support/trivial-builders
|
||||
${indexer}/bin/indexer --dir ./pkgs/build-support
|
||||
'';
|
||||
installPhase = ''
|
||||
cat lib.json > $lib
|
||||
cat trivial-builders.json > $trivial_builders
|
||||
cat build-support.json > $build_support
|
||||
|
||||
mkdir $out
|
||||
ln -s $lib $out/lib
|
||||
ln -s $trivial_builders $out/trivial_builders
|
||||
ln -s $build_support $out/build_support
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = { inherit indexer nixpkgs-data; };
|
||||
inherit checks;
|
||||
devShells.indexer = craneLib.devShell {
|
||||
# Inherit inputs from checks.
|
||||
inherit checks;
|
||||
};
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
10
pasta/.envrc
Normal file
10
pasta/.envrc
Normal file
@ -0,0 +1,10 @@
|
||||
source_up
|
||||
|
||||
files=(../../flake.nix flake-module.nix)
|
||||
if type nix_direnv_watch_file &>/dev/null; then
|
||||
nix_direnv_watch_file "${files[@]}"
|
||||
else
|
||||
watch_file "${files[@]}"
|
||||
fi
|
||||
|
||||
use flake .#pasta --builders ''
|
25
pasta/README.md
Normal file
25
pasta/README.md
Normal 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
11
pasta/default.nix
Normal 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
|
||||
'';
|
||||
}
|
13
pasta/flake-module.nix
Normal file
13
pasta/flake-module.nix
Normal file
@ -0,0 +1,13 @@
|
||||
{ inputs, ... }: {
|
||||
perSystem = { self', inputs', pkgs, ... }:
|
||||
let
|
||||
nix = inputs'.nix.packages.nix-clangStdenv;
|
||||
nixpkgs = inputs.nixpkgs-migrated;
|
||||
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
7
pasta/shell.nix
Normal file
@ -0,0 +1,7 @@
|
||||
{ pkgs, nix, ... }:
|
||||
pkgs.mkShell {
|
||||
buildInputs = [ nix ];
|
||||
shellHook = ''
|
||||
echo "using a custom nix build: ${nix}"
|
||||
'';
|
||||
}
|
46
pasta/src/eval.nix
Normal file
46
pasta/src/eval.nix
Normal file
@ -0,0 +1,46 @@
|
||||
{ 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" ];
|
||||
};
|
||||
|
||||
# generate test_data for pesto
|
||||
test_data = {
|
||||
attrsets = getDocsFromSet lib.attrsets [ "lib" "attrsets" ];
|
||||
};
|
||||
|
||||
in
|
||||
{ inherit tools pkgs docs toFile test_data; }
|
129
pasta/src/tools.nix
Normal file
129
pasta/src/tools.nix
Normal file
@ -0,0 +1,129 @@
|
||||
{ lib }:
|
||||
let
|
||||
force = v: (builtins.tryEval v).value;
|
||||
|
||||
dropBack = l: lib.reverseList (lib.drop 1 (lib.reverseList l));
|
||||
|
||||
/* *
|
||||
# Returns
|
||||
|
||||
We use the rust notation, since a pasta struct is needed for pesto anyways.
|
||||
|
||||
struct {
|
||||
lambda: {
|
||||
position: Position
|
||||
...
|
||||
}
|
||||
attr: {
|
||||
position: Position
|
||||
}
|
||||
}
|
||||
*/
|
||||
getDocs = parent: name:
|
||||
let
|
||||
lambda =
|
||||
if lib.isFunction parent.${name} then
|
||||
builtins.lambdaMeta parent.${name}
|
||||
else
|
||||
null;
|
||||
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.docs != null
|
||||
# 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 all more nested 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; }
|
10
pesto/.envrc
Normal file
10
pesto/.envrc
Normal file
@ -0,0 +1,10 @@
|
||||
source_up
|
||||
|
||||
files=(../../flake.nix flake-module.nix Cargo.lock)
|
||||
if type nix_direnv_watch_file &>/dev/null; then
|
||||
nix_direnv_watch_file "${files[@]}"
|
||||
else
|
||||
watch_file "${files[@]}"
|
||||
fi
|
||||
|
||||
use flake .#pesto --builders ''
|
1
pesto/.gitignore
vendored
Normal file
1
pesto/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target
|
819
pesto/Cargo.lock
generated
Normal file
819
pesto/Cargo.lock
generated
Normal file
@ -0,0 +1,819 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "countme"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dissimilar"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "expect-test"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "pesto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"expect-test",
|
||||
"regex",
|
||||
"rnix",
|
||||
"rowan",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"textwrap",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rnix"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb35cedbeb70e0ccabef2a31bcff0aebd114f19566086300b8f42c725fc2cb5f"
|
||||
dependencies = [
|
||||
"rowan",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rowan"
|
||||
version = "0.15.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "906057e449592587bf6724f00155bf82a6752c868d78a8fb3aa41f4e6357cfe8"
|
||||
dependencies = [
|
||||
"countme",
|
||||
"hashbrown 0.12.3",
|
||||
"memoffset",
|
||||
"rustc-hash",
|
||||
"text-size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "text-size"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.51.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
25
pesto/Cargo.toml
Normal file
25
pesto/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "pesto"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
rnix = "0.11.0"
|
||||
# version defined by rnix
|
||||
rowan = { version = "*" }
|
||||
regex = "1.9.5"
|
||||
textwrap = "0.16.0"
|
||||
walkdir = "2.4.0"
|
||||
clap = { version = "4.4.4", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_json = "1.0"
|
||||
expect-test = "1.4.0"
|
||||
serde_with = "3.4.0"
|
||||
|
||||
|
||||
# [dev-dependencies]
|
21
pesto/README.md
Normal file
21
pesto/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Pesto
|
||||
|
||||
Pesto gives you the extra sauce on top of your lambda noodles.
|
||||
|
||||
From a given lambda position extract doc comments and some metadata.
|
||||
|
||||
## Plugin for Noogle
|
||||
|
||||
Extract 'nixdoc style comments' or doc-comment (RFC145) from a given file position.
|
||||
|
||||
Usage:
|
||||
|
||||
```sh
|
||||
pesto --file "attrsets.nix" --line "11" --column "3"
|
||||
->
|
||||
{
|
||||
"doc": "content",
|
||||
"arguments": {},
|
||||
"countApplied": 3
|
||||
}
|
||||
```
|
34
pesto/flake-module.nix
Normal file
34
pesto/flake-module.nix
Normal file
@ -0,0 +1,34 @@
|
||||
{ inputs, ... }: {
|
||||
perSystem = { self', inputs', pkgs, system, ... }:
|
||||
let
|
||||
craneLib = inputs.crane.lib.${system};
|
||||
src = craneLib.cleanCargoSource (craneLib.path ./.);
|
||||
|
||||
commonArgs = {
|
||||
inherit src;
|
||||
strictDeps = true;
|
||||
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
|
||||
};
|
||||
|
||||
pesto = craneLib.buildPackage commonArgs;
|
||||
checks = {
|
||||
inherit pesto;
|
||||
pesto-clippy = craneLib.cargoClippy (commonArgs // {
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
});
|
||||
pesto-fmt = craneLib.cargoFmt { inherit src; };
|
||||
pesto-nextest = craneLib.cargoNextest (commonArgs // {
|
||||
partitions = 1;
|
||||
partitionType = "count";
|
||||
});
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = { inherit pesto; };
|
||||
inherit checks;
|
||||
devShells.pesto = craneLib.devShell {
|
||||
# Inherit inputs from checks.
|
||||
inherit checks;
|
||||
};
|
||||
};
|
||||
}
|
309
pesto/src/bulk.rs
Normal file
309
pesto/src/bulk.rs
Normal file
@ -0,0 +1,309 @@
|
||||
use std::{collections::HashMap, path::PathBuf, println, rc::Rc, time::Instant, vec};
|
||||
|
||||
use crate::{
|
||||
pasta::{Docs, Files, LambdaMeta, Pasta},
|
||||
position::{DocComment, DocIndex, FilePosition, NixDocComment},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LookupReason<'a> {
|
||||
// docs: &'a Docs,
|
||||
position: &'a FilePosition,
|
||||
// field: FieldType,
|
||||
}
|
||||
|
||||
pub trait BulkProcessing {
|
||||
fn new(path: &PathBuf) -> Self;
|
||||
}
|
||||
|
||||
fn insert_position<'a>(
|
||||
mut file_map: HashMap<&'a PathBuf, Vec<LookupReason<'a>>>,
|
||||
position: &'a FilePosition,
|
||||
item: LookupReason<'a>,
|
||||
) -> HashMap<&'a PathBuf, Vec<LookupReason<'a>>> {
|
||||
match file_map.get_mut(&position.file) {
|
||||
Some(list) => {
|
||||
list.push(item);
|
||||
}
|
||||
None => {
|
||||
file_map.insert(&position.file, vec![item]);
|
||||
}
|
||||
};
|
||||
file_map
|
||||
}
|
||||
|
||||
fn build_file_map(data: &Vec<Docs>) -> HashMap<&PathBuf, Vec<LookupReason>> {
|
||||
let mut file_map: HashMap<&PathBuf, Vec<LookupReason>> = HashMap::new();
|
||||
for doc_item in data.iter() {
|
||||
if let Some(position) = &doc_item.docs.attr.position {
|
||||
file_map = insert_position(
|
||||
file_map,
|
||||
position,
|
||||
LookupReason {
|
||||
// docs: doc_item,
|
||||
position: position,
|
||||
// field: FieldType::Attr,
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(lambda) = &doc_item.docs.lambda {
|
||||
if let Some(position) = &lambda.position {
|
||||
file_map = insert_position(
|
||||
file_map,
|
||||
position,
|
||||
LookupReason {
|
||||
// docs: doc_item,
|
||||
position: position,
|
||||
// field: FieldType::Lambda,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
file_map
|
||||
}
|
||||
|
||||
/// Collect all positions that need to be looked up in advance
|
||||
/// This saves time afterwards, since we can iterate over the whole file only once and save all interesting ast positions
|
||||
fn collect_file_positions(lookups: &Vec<LookupReason>) -> HashMap<usize, Vec<usize>> {
|
||||
let mut positions: HashMap<usize, Vec<usize>> = HashMap::new();
|
||||
for lookup in lookups {
|
||||
match positions.get_mut(&lookup.position.line) {
|
||||
Some(cols) => {
|
||||
cols.push(lookup.position.column);
|
||||
}
|
||||
None => {
|
||||
positions.insert(lookup.position.line, vec![lookup.position.column]);
|
||||
}
|
||||
}
|
||||
}
|
||||
positions
|
||||
}
|
||||
|
||||
fn fill_docs(
|
||||
data: &Vec<Docs>,
|
||||
pos_doc_map: &HashMap<&FilePosition, Option<NixDocComment>>,
|
||||
) -> Vec<Docs> {
|
||||
let mut filled_docs = data.clone();
|
||||
for item in filled_docs.iter_mut() {
|
||||
if let Some(position) = &item.docs.attr.position {
|
||||
if let Some(Some(doc_comment)) = pos_doc_map.get(&position) {
|
||||
item.docs.attr.content = doc_comment.content.clone();
|
||||
}
|
||||
}
|
||||
if let Some(lambda) = item.docs.lambda.as_mut() {
|
||||
if let Some(position) = &lambda.position {
|
||||
if let Some(Some(doc_comment)) = pos_doc_map.get(&position) {
|
||||
lambda.content = doc_comment.content.clone();
|
||||
lambda.countApplied = doc_comment.count_applied;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
filled_docs
|
||||
}
|
||||
|
||||
/// Build categories for efficiently finding aliases. (This is very expensive O(n^2). )
|
||||
/// Aliases can only exist within one subgroup, iterating over other items is a waste of time.
|
||||
/// With the current value introspection, any value that is an alias of a builtin, also inherits the builtins docs and the isPrimop flag set.
|
||||
///
|
||||
/// Group docs into the following subgroups
|
||||
/// 1. primop_lambdas
|
||||
/// e.g, lib.add, builtins.add
|
||||
///
|
||||
/// 2.non_primop_lambdas
|
||||
/// e.g, lib.attrByPath
|
||||
///
|
||||
/// 3.partially_applied lambdas
|
||||
/// e.g., concatLines (is concatMapStrings applied with f := Lambda<(s: s + "\n");>)
|
||||
/// This is a special case, it is very hard, to properly detect aliases at this level. Although the alias must also be found in this subgroup.
|
||||
///
|
||||
fn categorize(data: &Vec<Docs>) -> (Vec<&Docs>, Vec<&Docs>, Vec<&Docs>) {
|
||||
// For finding aliases.
|
||||
// Group docs into these subgroups.
|
||||
// Aliases can only exist within one subgroup, iterating over other items is a waste of time.
|
||||
let mut primop_lambdas: Vec<&Docs> = vec![];
|
||||
let mut non_primop_lambdas: Vec<&Docs> = vec![];
|
||||
let mut partially_applieds: Vec<&Docs> = vec![];
|
||||
|
||||
for item in data.iter() {
|
||||
if let Some(lambda) = &item.docs.lambda {
|
||||
match lambda.countApplied {
|
||||
Some(0) | None => {
|
||||
if lambda.isPrimop {
|
||||
primop_lambdas.push(&item);
|
||||
}
|
||||
if !lambda.isPrimop {
|
||||
non_primop_lambdas.push(&item);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// #
|
||||
partially_applieds.push(&item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(primop_lambdas, non_primop_lambdas, partially_applieds)
|
||||
}
|
||||
|
||||
fn init_alias_map(
|
||||
data: &Vec<Docs>,
|
||||
categories: (Vec<&Docs>, Vec<&Docs>, Vec<&Docs>),
|
||||
) -> HashMap<Rc<Vec<String>>, Vec<Rc<Vec<String>>>> {
|
||||
let (primop_lambdas, non_primop_lambdas, partially_applieds) = categories;
|
||||
|
||||
let mut primops: Vec<&Docs> = vec![];
|
||||
primops.extend(primop_lambdas.iter());
|
||||
primops.extend(partially_applieds.iter());
|
||||
|
||||
let mut non_primops: Vec<&Docs> = vec![];
|
||||
non_primops.extend(non_primop_lambdas.iter());
|
||||
non_primops.extend(partially_applieds.iter());
|
||||
|
||||
let mut alias_map: HashMap<Rc<Vec<String>>, Vec<Rc<Vec<String>>>> = HashMap::new();
|
||||
for item in data.iter() {
|
||||
if let Some(lambda) = &item.docs.lambda {
|
||||
match lambda.countApplied {
|
||||
Some(0) => {
|
||||
if lambda.isPrimop {
|
||||
alias_map.insert(item.path.clone(), find_aliases(&item, &primop_lambdas));
|
||||
}
|
||||
if !lambda.isPrimop {
|
||||
alias_map
|
||||
.insert(item.path.clone(), find_aliases(&item, &non_primop_lambdas));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if lambda.isPrimop {
|
||||
alias_map.insert(item.path.clone(), find_aliases(&item, &primops));
|
||||
}
|
||||
if !lambda.isPrimop {
|
||||
alias_map.insert(item.path.clone(), find_aliases(&item, &non_primops));
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
alias_map.insert(item.path.clone(), find_aliases(&item, &partially_applieds));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
alias_map
|
||||
}
|
||||
impl<'a> BulkProcessing for Pasta {
|
||||
fn new(path: &PathBuf) -> Self {
|
||||
let start_time = Instant::now();
|
||||
let data = Pasta::from_file(path);
|
||||
|
||||
let file_map = build_file_map(&data);
|
||||
|
||||
let mut pos_doc_map: HashMap<&FilePosition, Option<NixDocComment>> = HashMap::new();
|
||||
for (path, lookups) in file_map.iter() {
|
||||
let positions = collect_file_positions(lookups);
|
||||
println!(
|
||||
"{:?}: Lookups {:?}",
|
||||
path.file_name().unwrap(),
|
||||
positions.len()
|
||||
);
|
||||
|
||||
let doc_index = DocIndex::new(path, positions);
|
||||
|
||||
for lookup in lookups {
|
||||
pos_doc_map.insert(
|
||||
lookup.position,
|
||||
doc_index.get_docs(lookup.position.line, lookup.position.column),
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut filled_docs = fill_docs(&data, &pos_doc_map);
|
||||
|
||||
let categories = categorize(&filled_docs);
|
||||
let alias_map = init_alias_map(&data, categories);
|
||||
|
||||
let mut doc_map: HashMap<Rc<Vec<String>>, Docs> = HashMap::new();
|
||||
for item in filled_docs.iter_mut() {
|
||||
item.aliases = alias_map.get(&item.path).map(|i| i.to_owned());
|
||||
doc_map.insert(Rc::clone(&item.path), item.clone());
|
||||
}
|
||||
|
||||
let end_time = Instant::now();
|
||||
println!(
|
||||
"parsed: {} doc comments / AST Positions, from {} files in {:?}",
|
||||
pos_doc_map.len(),
|
||||
file_map.len(),
|
||||
end_time - start_time
|
||||
);
|
||||
|
||||
Self {
|
||||
docs: filled_docs,
|
||||
doc_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How to find aliases:
|
||||
/// Match
|
||||
/// partially applied functions -> special case, don't know how it is "correct". Would need access to the upvalues?
|
||||
/// Simple lambdas (not partially applied)
|
||||
/// Match primop: (Doesnt have source position)
|
||||
/// Eq countApplied,
|
||||
/// Eq content
|
||||
/// Other isPrimop,
|
||||
/// Content not empty
|
||||
/// Match Non-Primop
|
||||
/// Eq position
|
||||
fn find_aliases(item: &Docs, list: &Vec<&Docs>) -> Vec<Rc<Vec<String>>> {
|
||||
// println!("find aliases for {:?} \n\n in {:?}", item, list);
|
||||
let res: Vec<Rc<Vec<String>>> = list
|
||||
.iter()
|
||||
.filter_map(|other| {
|
||||
if let (Some(s_meta), Some(o_meta)) = (&item.docs.lambda, &other.docs.lambda) {
|
||||
// Avoid creating an alias for the same item.
|
||||
if item.path == other.path {
|
||||
return None;
|
||||
}
|
||||
if count_applied(s_meta) != 0
|
||||
// Use less accurate name aliases. This can lead to false positives
|
||||
// TODO: figure out the proper way
|
||||
&& count_applied(o_meta) == count_applied(s_meta)
|
||||
&& item.path.last().unwrap() == other.path.last().unwrap()
|
||||
{
|
||||
return Some(other.path.clone());
|
||||
}
|
||||
return match s_meta.isPrimop {
|
||||
true => {
|
||||
let is_empty = match &s_meta.content {
|
||||
Some(c) => c.is_empty(),
|
||||
None => true,
|
||||
};
|
||||
|
||||
if o_meta.isPrimop
|
||||
&& o_meta.content == s_meta.content
|
||||
&& !is_empty
|
||||
&& count_applied(s_meta) == 0
|
||||
&& count_applied(o_meta) == 0
|
||||
{
|
||||
return Some(other.path.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
false => {
|
||||
if s_meta.position == o_meta.position
|
||||
&& count_applied(s_meta) == 0
|
||||
&& count_applied(o_meta) == 0
|
||||
{
|
||||
return Some(other.path.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
res
|
||||
}
|
||||
|
||||
fn count_applied(meta: &LambdaMeta) -> usize {
|
||||
meta.countApplied.unwrap_or(0)
|
||||
}
|
113
pesto/src/comment.rs
Normal file
113
pesto/src/comment.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use rnix::ast::{self, AstToken};
|
||||
use rnix::{match_ast, SyntaxNode};
|
||||
use rowan::ast::AstNode;
|
||||
|
||||
/// Implements functions for doc-comments according to rfc145.
|
||||
pub trait DocComment {
|
||||
fn doc_text(&self) -> Option<&str>;
|
||||
}
|
||||
|
||||
impl DocComment for ast::Comment {
|
||||
/// Function returns the contents of the doc-comment, if the [ast::Comment] is a
|
||||
/// doc-comment, or None otherwise.
|
||||
///
|
||||
/// Note: [ast::Comment] holds both the single-line and multiline comment.
|
||||
///
|
||||
/// /**{content}*/
|
||||
/// -> {content}
|
||||
///
|
||||
/// It is named `doc_text` to complement [ast::Comment::text].
|
||||
fn doc_text(&self) -> Option<&str> {
|
||||
let text = self.syntax().text();
|
||||
// Check whether this is a doc-comment
|
||||
if text.starts_with(r#"/**"#) && self.text().starts_with('*') {
|
||||
match text.strip_prefix(r#"/**"#) {
|
||||
Some(t) => t.strip_suffix(r#"*/"#),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Function retrieves a doc-comment from the [ast::Expr]
|
||||
///
|
||||
/// Returns an [Option<String>] of the first suitable doc-comment.
|
||||
/// Returns [None] in case no suitable comment was found.
|
||||
///
|
||||
/// Doc-comments can appear in two places for any expression
|
||||
///
|
||||
/// ```nix
|
||||
/// # (1) directly before the expression (anonymous)
|
||||
/// /** Doc */
|
||||
/// bar: bar;
|
||||
///
|
||||
/// # (2) when assigning a name.
|
||||
/// {
|
||||
/// /** Doc */
|
||||
/// foo = bar: bar;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If the doc-comment is not found in place (1) the search continues at place (2)
|
||||
/// More precisely before the NODE_ATTRPATH_VALUE (ast)
|
||||
/// If no doc-comment was found in place (1) or (2) this function returns None.
|
||||
pub fn get_expr_docs(expr: &SyntaxNode) -> Option<String> {
|
||||
if let Some(doc) = get_doc_comment(expr) {
|
||||
// Found in place (1)
|
||||
doc.doc_text().map(|v| v.to_owned())
|
||||
} else if let Some(ref parent) = expr.parent() {
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::AttrpathValue(_) => {
|
||||
if let Some(doc_comment) = get_doc_comment(parent) {
|
||||
doc_comment.doc_text().map(|v| v.to_owned())
|
||||
}else{
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Yet unhandled ast-nodes
|
||||
None
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// None
|
||||
} else {
|
||||
// There is no parent;
|
||||
// No further places where a doc-comment could be.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks backwards from the given expression
|
||||
/// Only whitespace or non-doc-comments are allowed in between an expression and the doc-comment.
|
||||
/// Any other Node or Token stops the peek.
|
||||
fn get_doc_comment(expr: &SyntaxNode) -> Option<ast::Comment> {
|
||||
let mut prev = expr.prev_sibling_or_token();
|
||||
loop {
|
||||
match prev {
|
||||
Some(rnix::NodeOrToken::Token(ref token)) => {
|
||||
match_ast! { match token {
|
||||
ast::Whitespace(_) => {
|
||||
prev = token.prev_sibling_or_token();
|
||||
},
|
||||
ast::Comment(it) => {
|
||||
if it.doc_text().is_some() {
|
||||
break Some(it);
|
||||
}else{
|
||||
//Ignore non-doc comments.
|
||||
prev = token.prev_sibling_or_token();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
break None;
|
||||
}
|
||||
}}
|
||||
}
|
||||
_ => break None,
|
||||
};
|
||||
}
|
||||
}
|
54
pesto/src/main.rs
Normal file
54
pesto/src/main.rs
Normal file
@ -0,0 +1,54 @@
|
||||
mod bulk;
|
||||
mod comment;
|
||||
mod pasta;
|
||||
mod position;
|
||||
mod tests;
|
||||
|
||||
use clap::Parser;
|
||||
use std::{collections::HashMap, path::PathBuf, println};
|
||||
|
||||
use crate::{
|
||||
bulk::BulkProcessing,
|
||||
pasta::Pasta,
|
||||
position::{DocComment, DocIndex},
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, version, about)]
|
||||
struct Options {
|
||||
/// Json file containing a list of file positions.
|
||||
/// Format: [{ file: String, line: Number, column: Number }]
|
||||
#[arg(long, conflicts_with_all=["line", "column", "file"])]
|
||||
pos_file: Option<PathBuf>,
|
||||
|
||||
/// Path to the *.nix file that should be inspected.
|
||||
/// If provided, --line and --column must also be set.
|
||||
#[arg(long, requires_all=["line", "column", "file"])]
|
||||
file: Option<PathBuf>,
|
||||
/// Line of the expression.
|
||||
#[arg(short, long, requires_all=["line", "column", "file"])]
|
||||
line: Option<usize>,
|
||||
/// Column of the expression.
|
||||
#[arg(short, long, requires_all=["line", "column", "file"])]
|
||||
column: Option<usize>,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// let mut output = io::stdout();
|
||||
let opts = Options::parse();
|
||||
|
||||
if let Some(nix_file) = opts.file {
|
||||
let mut positions = HashMap::new();
|
||||
positions.insert(opts.line.unwrap(), vec![opts.column.unwrap()]);
|
||||
|
||||
let pos = DocIndex::new(&nix_file, positions);
|
||||
|
||||
if let Some(docs) = pos.get_docs(opts.line.unwrap(), opts.column.unwrap()) {
|
||||
println!("{:?}", docs);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos_file) = opts.pos_file {
|
||||
let data = Pasta::new(&pos_file);
|
||||
}
|
||||
}
|
83
pesto/src/pasta.rs
Normal file
83
pesto/src/pasta.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
println,
|
||||
process::exit,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::position::FilePosition;
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct LambdaMeta {
|
||||
#[allow(non_snake_case)]
|
||||
pub isPrimop: bool,
|
||||
pub name: Option<String>,
|
||||
pub position: Option<FilePosition>,
|
||||
pub args: Option<Vec<String>>,
|
||||
pub experimental: Option<bool>,
|
||||
pub arity: Option<usize>,
|
||||
|
||||
// I want to potentially overwrite those two
|
||||
pub content: Option<String>,
|
||||
#[allow(non_snake_case)]
|
||||
pub countApplied: Option<usize>,
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct AttrMeta {
|
||||
pub position: Option<FilePosition>,
|
||||
/// I want to add this
|
||||
pub content: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct DocsMeta {
|
||||
pub lambda: Option<LambdaMeta>,
|
||||
pub attr: AttrMeta,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Docs {
|
||||
pub docs: DocsMeta,
|
||||
pub aliases: Option<Vec<Rc<Vec<String>>>>,
|
||||
pub path: Rc<Vec<String>>,
|
||||
}
|
||||
|
||||
pub struct Pasta {
|
||||
pub docs: Vec<Docs>,
|
||||
pub doc_map: HashMap<Rc<Vec<String>>, Docs>,
|
||||
}
|
||||
|
||||
pub trait Files {
|
||||
fn from_file(path: &PathBuf) -> Vec<Docs>;
|
||||
fn to_file(self, file_name: &str) -> Result<(), std::io::Error>;
|
||||
}
|
||||
|
||||
impl<'a> Files for Pasta {
|
||||
fn from_file(path: &PathBuf) -> Vec<Docs> {
|
||||
let raw = fs::read_to_string(&path);
|
||||
match raw {
|
||||
Ok(content) => match serde_json::from_str(&content) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
println!("Error could not parse data. {}", e.to_string());
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Could not read input file: {}", e.to_string());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_file(self, file_name: &str) -> Result<(), std::io::Error> {
|
||||
let mut file = File::create(file_name).unwrap();
|
||||
|
||||
file.write_all(serde_json::to_string_pretty(&self.docs).unwrap().as_bytes())
|
||||
}
|
||||
}
|
224
pesto/src/position.rs
Normal file
224
pesto/src/position.rs
Normal file
@ -0,0 +1,224 @@
|
||||
use rnix::ast::{self};
|
||||
use rnix::{match_ast, SyntaxNode};
|
||||
use rowan::TextSize;
|
||||
use rowan::{ast::AstNode, WalkEvent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::process::exit;
|
||||
use std::rc::Rc;
|
||||
use std::time::Instant;
|
||||
|
||||
use std::{format, fs, path::PathBuf, println};
|
||||
|
||||
use crate::comment::get_expr_docs;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TextPosition {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct FilePosition {
|
||||
pub file: PathBuf,
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DocIndex<'a> {
|
||||
file: &'a PathBuf,
|
||||
pos_idx: HashMap<(usize, usize), TextSize>,
|
||||
node_idx: HashMap<TextSize, Option<SyntaxNode>>,
|
||||
}
|
||||
|
||||
pub trait DocComment<'a> {
|
||||
fn new(file: &'a PathBuf, positions: HashMap<usize, Vec<usize>>) -> Self;
|
||||
fn get_docs(&self, line: usize, column: usize) -> Option<NixDocComment>;
|
||||
}
|
||||
|
||||
fn get_src(path: &PathBuf) -> String {
|
||||
if let Ok(src) = fs::read_to_string(path) {
|
||||
return src;
|
||||
}
|
||||
println!("could not read file: {}", path.to_str().unwrap());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/// Initializes a HashMap for lookup operation between L:C and absolute position.
|
||||
/// Returns both
|
||||
/// Position HashMap from l:c -> abs
|
||||
/// Reverse Position HashMap from abs -> l:c
|
||||
fn init_pos_idx(
|
||||
path: &PathBuf,
|
||||
positions: HashMap<usize, Vec<usize>>,
|
||||
) -> (
|
||||
HashMap<(usize, usize), TextSize>,
|
||||
HashMap<TextSize, (usize, usize)>,
|
||||
) {
|
||||
let mut res = HashMap::new();
|
||||
let mut inverse: HashMap<TextSize, (usize, usize)> = HashMap::new();
|
||||
|
||||
let file = File::open(path).unwrap();
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
let mut curr_position = 0;
|
||||
for (curr_line, line) in reader.lines().enumerate() {
|
||||
match line {
|
||||
Ok(line) => {
|
||||
if let Some(cols) = positions.get(&(curr_line + 1)) {
|
||||
cols.iter().for_each(|col| {
|
||||
let lc_tuple = (curr_line + 1, *col);
|
||||
let absolute =
|
||||
TextSize::from(u32::try_from(curr_position + col - 1).unwrap());
|
||||
res.insert(lc_tuple, absolute);
|
||||
inverse.insert(absolute, lc_tuple);
|
||||
});
|
||||
}
|
||||
curr_position += line.len() + 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(res, inverse)
|
||||
}
|
||||
|
||||
// Take a list of lookup operations
|
||||
// Since iterating over the AST can be expensive
|
||||
fn init_node_idx(
|
||||
ast: &SyntaxNode,
|
||||
pos: &HashMap<TextSize, (usize, usize)>,
|
||||
) -> HashMap<TextSize, Option<SyntaxNode>> {
|
||||
let mut res: HashMap<TextSize, Option<SyntaxNode>> = HashMap::new();
|
||||
|
||||
for ev in ast.preorder() {
|
||||
match ev {
|
||||
WalkEvent::Enter(node) => {
|
||||
let cursor = node.text_range().start();
|
||||
if let Some(_) = pos.get(&cursor) {
|
||||
if res.get(&cursor).is_none() {
|
||||
res.insert(cursor, Some(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
WalkEvent::Leave(node) => {
|
||||
let cursor = node.text_range().end();
|
||||
if let Some(_) = pos.get(&cursor) {
|
||||
if res.get(&cursor).is_none() {
|
||||
res.insert(cursor, Some(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
impl<'a> DocComment<'a> for DocIndex<'a> {
|
||||
fn new(file: &'a PathBuf, positions: HashMap<usize, Vec<usize>>) -> Self {
|
||||
let src = get_src(file);
|
||||
let rc: Rc<String> = Rc::new(src);
|
||||
let mut start_time = Instant::now();
|
||||
let ast = rnix::Root::parse(Rc::clone(&rc).as_str()).syntax();
|
||||
let mut end_time = Instant::now();
|
||||
// println!("{:?} - Parsed ast", end_time - start_time);
|
||||
|
||||
start_time = Instant::now();
|
||||
let (pos_idx, inverse_pos_idx) = init_pos_idx(&file, positions);
|
||||
end_time = Instant::now();
|
||||
// println!(
|
||||
// "{:?} - Translated col,line into abs positions",
|
||||
// end_time - start_time
|
||||
// );
|
||||
|
||||
// Call your function here
|
||||
start_time = Instant::now();
|
||||
let node_idx = init_node_idx(&ast, &inverse_pos_idx);
|
||||
end_time = Instant::now();
|
||||
|
||||
// println!(
|
||||
// "{:?} - Find all ast nodes for positions",
|
||||
// end_time - start_time
|
||||
// );
|
||||
|
||||
return Self {
|
||||
file,
|
||||
pos_idx,
|
||||
node_idx,
|
||||
};
|
||||
}
|
||||
|
||||
fn get_docs(&self, line: usize, column: usize) -> Option<NixDocComment> {
|
||||
let idx = self.pos_idx.get(&(line, column));
|
||||
if idx.is_none() {
|
||||
let msg = format!(
|
||||
"Position {} {} may not exist in file {:?}",
|
||||
line, column, self.file
|
||||
);
|
||||
println!("{:?} @ {}", self.file, msg);
|
||||
exit(1);
|
||||
}
|
||||
if let Some(idx) = idx {
|
||||
let expr = self.node_idx.get(idx);
|
||||
// println!("L{}:C{}, expr: {:?}", line, column, expr);
|
||||
if let Some(Some(expr)) = expr {
|
||||
let doc = match expr.kind() {
|
||||
rnix::SyntaxKind::NODE_LAMBDA => {
|
||||
let (outer_lambda, count_applied) = get_parent_lambda(&expr);
|
||||
NixDocComment {
|
||||
content: get_expr_docs(&outer_lambda),
|
||||
count_applied: Some(count_applied),
|
||||
}
|
||||
}
|
||||
_ => NixDocComment {
|
||||
content: get_expr_docs(&expr),
|
||||
count_applied: None,
|
||||
},
|
||||
};
|
||||
return Some(doc);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NixDocComment {
|
||||
pub content: Option<String>,
|
||||
pub count_applied: Option<usize>,
|
||||
}
|
||||
|
||||
fn get_parent_lambda(expr: &SyntaxNode) -> (SyntaxNode, usize) {
|
||||
let mut count_outer_lambda = 0;
|
||||
let mut lambda_parent = peek_parent_lambda(expr);
|
||||
let mut res = expr.to_owned();
|
||||
loop {
|
||||
match lambda_parent {
|
||||
Some(ref node) => {
|
||||
count_outer_lambda += 1;
|
||||
res = node.clone();
|
||||
lambda_parent = peek_parent_lambda(node);
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
return (res, count_outer_lambda);
|
||||
}
|
||||
|
||||
fn peek_parent_lambda(expr: &SyntaxNode) -> Option<SyntaxNode> {
|
||||
match expr.parent() {
|
||||
Some(parent) => match_ast! {
|
||||
match parent {
|
||||
ast::Lambda(_) => {
|
||||
Some(parent)
|
||||
},
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
73
pesto/src/tests.rs
Normal file
73
pesto/src/tests.rs
Normal file
@ -0,0 +1,73 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::{collections::HashMap, ffi::OsStr, format, fs, path::PathBuf, println, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
bulk::BulkProcessing,
|
||||
pasta::Pasta,
|
||||
position::{DocComment, DocIndex, TextPosition},
|
||||
};
|
||||
|
||||
use expect_test::expect_file;
|
||||
|
||||
fn dir_tests<F>(dir: &str, ext: &str, get_actual: F)
|
||||
where
|
||||
F: Fn(&PathBuf) -> String,
|
||||
{
|
||||
println!("{:?}", env!("CARGO_MANIFEST_DIR"));
|
||||
let base_path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "test_data", dir]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
let entries = base_path.read_dir().unwrap();
|
||||
|
||||
for entry in entries {
|
||||
let path = entry.unwrap().path();
|
||||
|
||||
if path.extension() != Some(OsStr::new(ext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("testing: {}", path.display());
|
||||
|
||||
let actual = get_actual(&path);
|
||||
|
||||
expect_file![path.with_extension("expect")].assert_eq(&actual);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_main() {
|
||||
dir_tests("atom", "nix", |path| {
|
||||
let mut pos_path = path.clone();
|
||||
pos_path.set_extension("pos");
|
||||
let pos_str = fs::read_to_string(&pos_path).unwrap();
|
||||
|
||||
let test_position: TextPosition = serde_json::from_str(&pos_str).unwrap();
|
||||
let line = test_position.line;
|
||||
let column = test_position.column;
|
||||
|
||||
let mut positions: HashMap<usize, Vec<usize>> = HashMap::new();
|
||||
positions.insert(line, vec![column]);
|
||||
let pos = DocIndex::new(path, positions);
|
||||
format!("{:?}", pos.get_docs(line, column))
|
||||
})
|
||||
}
|
||||
#[test]
|
||||
fn test_aliases() {
|
||||
dir_tests("aliases", "json", |path| {
|
||||
let data: Pasta = Pasta::new(&PathBuf::from(path));
|
||||
serde_json::to_string_pretty(&data.docs).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bulk() {
|
||||
dir_tests("bulk", "json", |path| {
|
||||
let data: Pasta = Pasta::new(&PathBuf::from(path));
|
||||
|
||||
serde_json::to_string_pretty(&data.docs[0..10]).unwrap()
|
||||
})
|
||||
}
|
||||
}
|
106
pesto/test_data/aliases/add.expect
Normal file
106
pesto/test_data/aliases/add.expect
Normal file
@ -0,0 +1,106 @@
|
||||
[
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": true,
|
||||
"name": "add",
|
||||
"args": [
|
||||
"e1",
|
||||
"e2"
|
||||
],
|
||||
"experimental": false,
|
||||
"arity": 2,
|
||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n "
|
||||
},
|
||||
"attr": {
|
||||
"position": {
|
||||
"file": "test_data/assets/default.nix",
|
||||
"line": 68,
|
||||
"column": 23
|
||||
},
|
||||
"content": null
|
||||
}
|
||||
},
|
||||
"aliases": [
|
||||
[
|
||||
"lib",
|
||||
"trivial",
|
||||
"add"
|
||||
],
|
||||
[
|
||||
"builtins",
|
||||
"add"
|
||||
]
|
||||
],
|
||||
"path": [
|
||||
"lib",
|
||||
"add"
|
||||
]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": true,
|
||||
"name": "add",
|
||||
"args": [
|
||||
"e1",
|
||||
"e2"
|
||||
],
|
||||
"experimental": false,
|
||||
"arity": 2,
|
||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n "
|
||||
},
|
||||
"attr": {
|
||||
"position": {
|
||||
"file": "test_data/assets/trivial.nix",
|
||||
"line": 269,
|
||||
"column": 21
|
||||
},
|
||||
"content": null
|
||||
}
|
||||
},
|
||||
"aliases": [
|
||||
[
|
||||
"lib",
|
||||
"add"
|
||||
],
|
||||
[
|
||||
"builtins",
|
||||
"add"
|
||||
]
|
||||
],
|
||||
"path": [
|
||||
"lib",
|
||||
"trivial",
|
||||
"add"
|
||||
]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": true,
|
||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||
"countApplied": 0
|
||||
},
|
||||
"attr": {
|
||||
"position": null,
|
||||
"content": ""
|
||||
}
|
||||
},
|
||||
"aliases": [
|
||||
[
|
||||
"lib",
|
||||
"add"
|
||||
],
|
||||
[
|
||||
"lib",
|
||||
"trivial",
|
||||
"add"
|
||||
]
|
||||
],
|
||||
"path": [
|
||||
"builtins",
|
||||
"add"
|
||||
]
|
||||
}
|
||||
]
|
56
pesto/test_data/aliases/add.json
Normal file
56
pesto/test_data/aliases/add.json
Normal file
@ -0,0 +1,56 @@
|
||||
[
|
||||
{
|
||||
"docs": {
|
||||
"attr": {
|
||||
"position": {
|
||||
"column": 23,
|
||||
"file": "test_data/assets/default.nix",
|
||||
"line": 68
|
||||
}
|
||||
},
|
||||
"lambda": {
|
||||
"args": ["e1", "e2"],
|
||||
"arity": 2,
|
||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||
"experimental": false,
|
||||
"isPrimop": true,
|
||||
"name": "add",
|
||||
"position": null
|
||||
}
|
||||
},
|
||||
"path": ["lib", "add"]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"attr": {
|
||||
"position": {
|
||||
"column": 21,
|
||||
"file": "test_data/assets/trivial.nix",
|
||||
"line": 269
|
||||
}
|
||||
},
|
||||
"lambda": {
|
||||
"args": ["e1", "e2"],
|
||||
"arity": 2,
|
||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||
"experimental": false,
|
||||
"isPrimop": true,
|
||||
"name": "add",
|
||||
"position": null
|
||||
}
|
||||
},
|
||||
"path": ["lib", "trivial", "add"]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"attr": { "content": "", "position": null },
|
||||
"lambda": {
|
||||
"content": "\n Return the sum of the numbers *e1* and *e2*.\n ",
|
||||
"countApplied": 0,
|
||||
"isPrimop": true,
|
||||
"position": null
|
||||
}
|
||||
},
|
||||
"path": ["builtins", "add"]
|
||||
}
|
||||
]
|
93
pesto/test_data/aliases/foldl.expect
Normal file
93
pesto/test_data/aliases/foldl.expect
Normal file
@ -0,0 +1,93 @@
|
||||
[
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"file": "test_data/assets/lists.nix",
|
||||
"line": 204,
|
||||
"column": 5
|
||||
},
|
||||
"content": "\n The binary operation to run, where the two arguments are:\n 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n 2. `x`: The corresponding list element for this iteration\n ",
|
||||
"countApplied": 0
|
||||
},
|
||||
"attr": {
|
||||
"position": {
|
||||
"file": "test_data/assets/default.nix",
|
||||
"line": 92,
|
||||
"column": 25
|
||||
},
|
||||
"content": null
|
||||
}
|
||||
},
|
||||
"aliases": [
|
||||
[
|
||||
"lib",
|
||||
"lists",
|
||||
"foldl'"
|
||||
]
|
||||
],
|
||||
"path": [
|
||||
"lib",
|
||||
"foldl'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"file": "test_data/assets/lists.nix",
|
||||
"line": 204,
|
||||
"column": 5
|
||||
},
|
||||
"content": "\n The binary operation to run, where the two arguments are:\n 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n 2. `x`: The corresponding list element for this iteration\n ",
|
||||
"countApplied": 0
|
||||
},
|
||||
"attr": {
|
||||
"position": {
|
||||
"file": "test_data/assets/lists.nix",
|
||||
"line": 198,
|
||||
"column": 3
|
||||
},
|
||||
"content": "\n Reduce a list by applying a binary operator from left to right,\n starting with an initial accumulator.\n Before each application of the operator, the accumulator value is evaluated.\n This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl).\n Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'),\n the initial accumulator argument is evaluated before the first iteration.\n A call like\n ```nix\n foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ]\n ```\n is (denotationally) equivalent to the following,\n but with the added benefit that `foldl'` itself will never overflow the stack.\n ```nix\n let\n acc₁ = builtins.seq acc₀ (op acc₀ x₀ );\n acc₂ = builtins.seq acc₁ (op acc₁ x₁ );\n acc₃ = builtins.seq acc₂ (op acc₂ x₂ );\n ...\n accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁);\n accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ );\n in\n accₙ₊₁\n # Or ignoring builtins.seq\n op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ\n ```\n\n # Example\n\n ```nix\n foldl' (acc: x: acc + x) 0 [1 2 3]\n => 6\n ```\n\n # Type\n\n ```\n foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc\n ```\n\n # Arguments\n\n - [op] The binary operation to run, where the two arguments are:\n\n1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration\n2. `x`: The corresponding list element for this iteration\n - [acc] The initial accumulator value\n - [list] The list to fold\n\n "
|
||||
}
|
||||
},
|
||||
"aliases": [
|
||||
[
|
||||
"lib",
|
||||
"foldl'"
|
||||
]
|
||||
],
|
||||
"path": [
|
||||
"lib",
|
||||
"lists",
|
||||
"foldl'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": true,
|
||||
"name": "foldl'",
|
||||
"args": [
|
||||
"op",
|
||||
"nul",
|
||||
"list"
|
||||
],
|
||||
"experimental": false,
|
||||
"arity": 3,
|
||||
"content": "\n Reduce a list by applying a binary operator, from left to right,\n e.g. `foldl' op nul [x0 x1 x2 ...] : op (op (op nul x0) x1) x2)\n ...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.\n The return value of each application of `op` is evaluated immediately,\n even for intermediate values.\n "
|
||||
},
|
||||
"attr": {
|
||||
"position": null,
|
||||
"content": null
|
||||
}
|
||||
},
|
||||
"aliases": [],
|
||||
"path": [
|
||||
"builtins",
|
||||
"foldl'"
|
||||
]
|
||||
}
|
||||
]
|
57
pesto/test_data/aliases/foldl.json
Normal file
57
pesto/test_data/aliases/foldl.json
Normal file
@ -0,0 +1,57 @@
|
||||
[
|
||||
{
|
||||
"docs": {
|
||||
"attr": {
|
||||
"position": {
|
||||
"column": 25,
|
||||
"file": "test_data/assets/default.nix",
|
||||
"line": 92
|
||||
}
|
||||
},
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"column": 5,
|
||||
"file": "test_data/assets/lists.nix",
|
||||
"line": 204
|
||||
}
|
||||
}
|
||||
},
|
||||
"path": ["lib", "foldl'"]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"attr": {
|
||||
"position": {
|
||||
"column": 3,
|
||||
"file": "test_data/assets/lists.nix",
|
||||
"line": 198
|
||||
}
|
||||
},
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"column": 5,
|
||||
"file": "test_data/assets/lists.nix",
|
||||
"line": 204
|
||||
}
|
||||
}
|
||||
},
|
||||
"path": ["lib", "lists", "foldl'"]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"attr": { "position": null },
|
||||
"lambda": {
|
||||
"args": ["op", "nul", "list"],
|
||||
"arity": 3,
|
||||
"content": "\n Reduce a list by applying a binary operator, from left to right,\n e.g. `foldl' op nul [x0 x1 x2 ...] : op (op (op nul x0) x1) x2)\n ...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.\n The return value of each application of `op` is evaluated immediately,\n even for intermediate values.\n ",
|
||||
"experimental": false,
|
||||
"isPrimop": true,
|
||||
"name": "foldl'",
|
||||
"position": null
|
||||
}
|
||||
},
|
||||
"path": ["builtins", "foldl'"]
|
||||
}
|
||||
]
|
57
pesto/test_data/aliases/strings.expect
Normal file
57
pesto/test_data/aliases/strings.expect
Normal file
@ -0,0 +1,57 @@
|
||||
[
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"file": "test_data/assets/strings.nix",
|
||||
"line": 84,
|
||||
"column": 25
|
||||
},
|
||||
"content": "\n Map a function over a list and concatenate the resulting strings.\n\n # Example\n\n ```nix\n concatMapStrings (x: \"a\" + x) [\"foo\" \"bar\"]\n => \"afooabar\"\n ```\n\n # Type\n\n ```\n concatMapStrings :: (a -> string) -> [a] -> string\n ```\n\n # Arguments\n\n - [f] \n - [list] \n\n ",
|
||||
"countApplied": 1
|
||||
},
|
||||
"attr": {
|
||||
"position": {
|
||||
"file": "test_data/assets/strings.nix",
|
||||
"line": 243,
|
||||
"column": 3
|
||||
},
|
||||
"content": "\n Concatenate a list of strings, adding a newline at the end of each one.\n Defined as `concatMapStrings (s: s + \"\\n\")`.\n\n # Example\n\n ```nix\n concatLines [ \"foo\" \"bar\" ]\n => \"foo\\nbar\\n\"\n ```\n\n # Type\n\n ```\n concatLines :: [string] -> string\n ```\n "
|
||||
}
|
||||
},
|
||||
"aliases": [],
|
||||
"path": [
|
||||
"lib",
|
||||
"strings",
|
||||
"concatLines"
|
||||
]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"file": "test_data/assets/strings.nix",
|
||||
"line": 84,
|
||||
"column": 25
|
||||
},
|
||||
"content": "\n Map a function over a list and concatenate the resulting strings.\n\n # Example\n\n ```nix\n concatMapStrings (x: \"a\" + x) [\"foo\" \"bar\"]\n => \"afooabar\"\n ```\n\n # Type\n\n ```\n concatMapStrings :: (a -> string) -> [a] -> string\n ```\n\n # Arguments\n\n - [f] \n - [list] \n\n ",
|
||||
"countApplied": 1
|
||||
},
|
||||
"attr": {
|
||||
"position": {
|
||||
"file": "test_data/assets/default.nix",
|
||||
"line": 98,
|
||||
"column": 27
|
||||
},
|
||||
"content": null
|
||||
}
|
||||
},
|
||||
"aliases": [],
|
||||
"path": [
|
||||
"lib",
|
||||
"concatLines"
|
||||
]
|
||||
}
|
||||
]
|
42
pesto/test_data/aliases/strings.json
Normal file
42
pesto/test_data/aliases/strings.json
Normal file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"docs": {
|
||||
"attr": {
|
||||
"position": {
|
||||
"column": 3,
|
||||
"file": "test_data/assets/strings.nix",
|
||||
"line": 243
|
||||
}
|
||||
},
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"column": 25,
|
||||
"file": "test_data/assets/strings.nix",
|
||||
"line": 84
|
||||
}
|
||||
}
|
||||
},
|
||||
"path": ["lib", "strings", "concatLines"]
|
||||
},
|
||||
{
|
||||
"docs": {
|
||||
"attr": {
|
||||
"position": {
|
||||
"column": 27,
|
||||
"file": "test_data/assets/default.nix",
|
||||
"line": 98
|
||||
}
|
||||
},
|
||||
"lambda": {
|
||||
"isPrimop": false,
|
||||
"position": {
|
||||
"column": 25,
|
||||
"file": "test_data/assets/strings.nix",
|
||||
"line": 84
|
||||
}
|
||||
}
|
||||
},
|
||||
"path": ["lib", "concatLines"]
|
||||
}
|
||||
]
|
99
pesto/test_data/assets/ascii-table.nix
Normal file
99
pesto/test_data/assets/ascii-table.nix
Normal file
@ -0,0 +1,99 @@
|
||||
{ "\t" = 9;
|
||||
"\n" = 10;
|
||||
"\r" = 13;
|
||||
" " = 32;
|
||||
"!" = 33;
|
||||
"\"" = 34;
|
||||
"#" = 35;
|
||||
"$" = 36;
|
||||
"%" = 37;
|
||||
"&" = 38;
|
||||
"'" = 39;
|
||||
"(" = 40;
|
||||
")" = 41;
|
||||
"*" = 42;
|
||||
"+" = 43;
|
||||
"," = 44;
|
||||
"-" = 45;
|
||||
"." = 46;
|
||||
"/" = 47;
|
||||
"0" = 48;
|
||||
"1" = 49;
|
||||
"2" = 50;
|
||||
"3" = 51;
|
||||
"4" = 52;
|
||||
"5" = 53;
|
||||
"6" = 54;
|
||||
"7" = 55;
|
||||
"8" = 56;
|
||||
"9" = 57;
|
||||
":" = 58;
|
||||
";" = 59;
|
||||
"<" = 60;
|
||||
"=" = 61;
|
||||
">" = 62;
|
||||
"?" = 63;
|
||||
"@" = 64;
|
||||
"A" = 65;
|
||||
"B" = 66;
|
||||
"C" = 67;
|
||||
"D" = 68;
|
||||
"E" = 69;
|
||||
"F" = 70;
|
||||
"G" = 71;
|
||||
"H" = 72;
|
||||
"I" = 73;
|
||||
"J" = 74;
|
||||
"K" = 75;
|
||||
"L" = 76;
|
||||
"M" = 77;
|
||||
"N" = 78;
|
||||
"O" = 79;
|
||||
"P" = 80;
|
||||
"Q" = 81;
|
||||
"R" = 82;
|
||||
"S" = 83;
|
||||
"T" = 84;
|
||||
"U" = 85;
|
||||
"V" = 86;
|
||||
"W" = 87;
|
||||
"X" = 88;
|
||||
"Y" = 89;
|
||||
"Z" = 90;
|
||||
"[" = 91;
|
||||
"\\" = 92;
|
||||
"]" = 93;
|
||||
"^" = 94;
|
||||
"_" = 95;
|
||||
"`" = 96;
|
||||
"a" = 97;
|
||||
"b" = 98;
|
||||
"c" = 99;
|
||||
"d" = 100;
|
||||
"e" = 101;
|
||||
"f" = 102;
|
||||
"g" = 103;
|
||||
"h" = 104;
|
||||
"i" = 105;
|
||||
"j" = 106;
|
||||
"k" = 107;
|
||||
"l" = 108;
|
||||
"m" = 109;
|
||||
"n" = 110;
|
||||
"o" = 111;
|
||||
"p" = 112;
|
||||
"q" = 113;
|
||||
"r" = 114;
|
||||
"s" = 115;
|
||||
"t" = 116;
|
||||
"u" = 117;
|
||||
"v" = 118;
|
||||
"w" = 119;
|
||||
"x" = 120;
|
||||
"y" = 121;
|
||||
"z" = 122;
|
||||
"{" = 123;
|
||||
"|" = 124;
|
||||
"}" = 125;
|
||||
"~" = 126;
|
||||
}
|
79
pesto/test_data/assets/asserts.nix
Normal file
79
pesto/test_data/assets/asserts.nix
Normal file
@ -0,0 +1,79 @@
|
||||
{ lib }:
|
||||
|
||||
rec {
|
||||
|
||||
/**
|
||||
Throw if pred is false, else return pred.
|
||||
Intended to be used to augment asserts with helpful error messages.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
assertMsg false "nope"
|
||||
stderr> error: nope
|
||||
assert assertMsg ("foo" == "bar") "foo is not bar, silly"; ""
|
||||
stderr> error: foo is not bar, silly
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
assertMsg :: Bool -> String -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [pred] Predicate that needs to succeed, otherwise `msg` is thrown
|
||||
- [msg] Message to throw in case `pred` fails
|
||||
|
||||
*/
|
||||
# TODO(Profpatsch): add tests that check stderr
|
||||
assertMsg =
|
||||
# Predicate that needs to succeed, otherwise `msg` is thrown
|
||||
pred:
|
||||
# Message to throw in case `pred` fails
|
||||
msg:
|
||||
pred || builtins.throw msg;
|
||||
|
||||
/**
|
||||
Specialized `assertMsg` for checking if `val` is one of the elements
|
||||
of the list `xs`. Useful for checking enums.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
let sslLibrary = "libressl";
|
||||
in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ]
|
||||
stderr> error: sslLibrary must be one of [
|
||||
stderr> "openssl"
|
||||
stderr> "bearssl"
|
||||
stderr> ], but is: "libressl"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [name] The name of the variable the user entered `val` into, for inclusion in the error message
|
||||
- [val] The value of what the user provided, to be compared against the values in `xs`
|
||||
- [xs] The list of valid values
|
||||
|
||||
*/
|
||||
assertOneOf =
|
||||
# The name of the variable the user entered `val` into, for inclusion in the error message
|
||||
name:
|
||||
# The value of what the user provided, to be compared against the values in `xs`
|
||||
val:
|
||||
# The list of valid values
|
||||
xs:
|
||||
assertMsg
|
||||
(lib.elem val xs)
|
||||
"${name} must be one of ${
|
||||
lib.generators.toPretty {} xs}, but is: ${
|
||||
lib.generators.toPretty {} val}";
|
||||
|
||||
}
|
1568
pesto/test_data/assets/attrsets.nix
Normal file
1568
pesto/test_data/assets/attrsets.nix
Normal file
File diff suppressed because it is too large
Load Diff
90
pesto/test_data/assets/cli.nix
Normal file
90
pesto/test_data/assets/cli.nix
Normal file
@ -0,0 +1,90 @@
|
||||
{ lib }:
|
||||
|
||||
rec {
|
||||
/**
|
||||
Automatically convert an attribute set to command-line options.
|
||||
This helps protect against malformed command lines and also to reduce
|
||||
boilerplate related to command-line construction for simple use cases.
|
||||
`toGNUCommandLine` returns a list of nix strings.
|
||||
`toGNUCommandLineShell` returns an escaped shell string.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
cli.toGNUCommandLine {} {
|
||||
data = builtins.toJSON { id = 0; };
|
||||
X = "PUT";
|
||||
retry = 3;
|
||||
retry-delay = null;
|
||||
url = [ "https://example.com/foo" "https://example.com/bar" ];
|
||||
silent = false;
|
||||
verbose = true;
|
||||
}
|
||||
=> [
|
||||
"-X" "PUT"
|
||||
"--data" "{\"id\":0}"
|
||||
"--retry" "3"
|
||||
"--url" "https://example.com/foo"
|
||||
"--url" "https://example.com/bar"
|
||||
"--verbose"
|
||||
]
|
||||
cli.toGNUCommandLineShell {} {
|
||||
data = builtins.toJSON { id = 0; };
|
||||
X = "PUT";
|
||||
retry = 3;
|
||||
retry-delay = null;
|
||||
url = [ "https://example.com/foo" "https://example.com/bar" ];
|
||||
silent = false;
|
||||
verbose = true;
|
||||
}
|
||||
=> "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [options]
|
||||
- [attrs]
|
||||
|
||||
*/
|
||||
toGNUCommandLineShell =
|
||||
options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
|
||||
|
||||
toGNUCommandLine = {
|
||||
# how to string-format the option name;
|
||||
# by default one character is a short option (`-`),
|
||||
# more than one characters a long option (`--`).
|
||||
mkOptionName ?
|
||||
k: if builtins.stringLength k == 1
|
||||
then "-${k}"
|
||||
else "--${k}",
|
||||
|
||||
# how to format a boolean value to a command list;
|
||||
# by default it’s a flag option
|
||||
# (only the option name if true, left out completely if false).
|
||||
mkBool ? k: v: lib.optional v (mkOptionName k),
|
||||
|
||||
# how to format a list value to a command list;
|
||||
# by default the option name is repeated for each value
|
||||
# and `mkOption` is applied to the values themselves.
|
||||
mkList ? k: v: lib.concatMap (mkOption k) v,
|
||||
|
||||
# how to format any remaining value to a command list;
|
||||
# on the toplevel, booleans and lists are handled by `mkBool` and `mkList`,
|
||||
# though they can still appear as values of a list.
|
||||
# By default, everything is printed verbatim and complex types
|
||||
# are forbidden (lists, attrsets, functions). `null` values are omitted.
|
||||
mkOption ?
|
||||
k: v: if v == null
|
||||
then []
|
||||
else [ (mkOptionName k) (lib.generators.mkValueStringDefault {} v) ]
|
||||
}:
|
||||
options:
|
||||
let
|
||||
render = k: v:
|
||||
if builtins.isBool v then mkBool k v
|
||||
else if builtins.isList v then mkList k v
|
||||
else mkOption k v;
|
||||
|
||||
in
|
||||
builtins.concatLists (lib.mapAttrsToList render options);
|
||||
}
|
383
pesto/test_data/assets/customisation.nix
Normal file
383
pesto/test_data/assets/customisation.nix
Normal file
@ -0,0 +1,383 @@
|
||||
{ lib }:
|
||||
|
||||
rec {
|
||||
|
||||
|
||||
/**
|
||||
`overrideDerivation drv f` takes a derivation (i.e., the result
|
||||
of a call to the builtin function `derivation`) and returns a new
|
||||
derivation in which the attributes of the original are overridden
|
||||
according to the function `f`. The function `f` is called with
|
||||
the original derivation attributes.
|
||||
`overrideDerivation` allows certain "ad-hoc" customisation
|
||||
scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance,
|
||||
if you want to "patch" the derivation returned by a package
|
||||
function in Nixpkgs to build another version than what the
|
||||
function itself provides, you can do something like this:
|
||||
mySed = overrideDerivation pkgs.gnused (oldAttrs: {
|
||||
name = "sed-4.2.2-pre";
|
||||
src = fetchurl {
|
||||
url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
|
||||
hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY=";
|
||||
};
|
||||
patches = [];
|
||||
});
|
||||
For another application, see build-support/vm, where this
|
||||
function is used to build arbitrary derivations inside a QEMU
|
||||
virtual machine.
|
||||
Note that in order to preserve evaluation errors, the new derivation's
|
||||
outPath depends on the old one's, which means that this function cannot
|
||||
be used in circular situations when the old derivation also depends on the
|
||||
new one.
|
||||
You should in general prefer `drv.overrideAttrs` over this function;
|
||||
see the nixpkgs manual for more information on overriding.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [drv]
|
||||
- [f]
|
||||
|
||||
*/
|
||||
overrideDerivation = drv: f:
|
||||
let
|
||||
newDrv = derivation (drv.drvAttrs // (f drv));
|
||||
in lib.flip (extendDerivation (builtins.seq drv.drvPath true)) newDrv (
|
||||
{ meta = drv.meta or {};
|
||||
passthru = if drv ? passthru then drv.passthru else {};
|
||||
}
|
||||
//
|
||||
(drv.passthru or {})
|
||||
//
|
||||
lib.optionalAttrs (drv ? __spliced) {
|
||||
__spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
`makeOverridable` takes a function from attribute set to attribute set and
|
||||
injects `override` attribute which can be used to override arguments of
|
||||
the function.
|
||||
nix-repl> x = {a, b}: { result = a + b; }
|
||||
nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
|
||||
nix-repl> y
|
||||
{ override = «lambda»; overrideDerivation = «lambda»; result = 3; }
|
||||
nix-repl> y.override { a = 10; }
|
||||
{ override = «lambda»; overrideDerivation = «lambda»; result = 12; }
|
||||
Please refer to "Nixpkgs Contributors Guide" section
|
||||
"<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats
|
||||
related to its use.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
|
||||
*/
|
||||
makeOverridable = f: lib.setFunctionArgs
|
||||
(origArgs: let
|
||||
result = f origArgs;
|
||||
|
||||
# Creates a functor with the same arguments as f
|
||||
copyArgs = g: lib.setFunctionArgs g (lib.functionArgs f);
|
||||
# Changes the original arguments with (potentially a function that returns) a set of new attributes
|
||||
overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
|
||||
|
||||
# Re-call the function but with different arguments
|
||||
overrideArgs = copyArgs (newArgs: makeOverridable f (overrideWith newArgs));
|
||||
# Change the result of the function call by applying g to it
|
||||
overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
|
||||
in
|
||||
if builtins.isAttrs result then
|
||||
result // {
|
||||
override = overrideArgs;
|
||||
overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
|
||||
${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
|
||||
overrideResult (x: x.overrideAttrs fdrv);
|
||||
}
|
||||
else if lib.isFunction result then
|
||||
# Transform the result into a functor while propagating its arguments
|
||||
lib.setFunctionArgs result (lib.functionArgs result) // {
|
||||
override = overrideArgs;
|
||||
}
|
||||
else result)
|
||||
(lib.functionArgs f);
|
||||
|
||||
|
||||
/**
|
||||
Call the package function in the file `fn` with the required
|
||||
arguments automatically. The function is called with the
|
||||
arguments `args`, but any missing arguments are obtained from
|
||||
`autoArgs`. This function is intended to be partially
|
||||
parameterised, e.g.,
|
||||
callPackage = callPackageWith pkgs;
|
||||
pkgs = {
|
||||
libfoo = callPackage ./foo.nix { };
|
||||
libbar = callPackage ./bar.nix { };
|
||||
};
|
||||
If the `libbar` function expects an argument named `libfoo`, it is
|
||||
automatically passed as an argument. Overrides or missing
|
||||
arguments can be supplied in `args`, e.g.
|
||||
libbar = callPackage ./bar.nix {
|
||||
libfoo = null;
|
||||
enableX11 = true;
|
||||
};
|
||||
|
||||
# Arguments
|
||||
|
||||
- [autoArgs]
|
||||
- [fn]
|
||||
- [args]
|
||||
|
||||
*/
|
||||
callPackageWith = autoArgs: fn: args:
|
||||
let
|
||||
f = if lib.isFunction fn then fn else import fn;
|
||||
fargs = lib.functionArgs f;
|
||||
|
||||
# All arguments that will be passed to the function
|
||||
# This includes automatic ones and ones passed explicitly
|
||||
allArgs = builtins.intersectAttrs fargs autoArgs // args;
|
||||
|
||||
# A list of argument names that the function requires, but
|
||||
# wouldn't be passed to it
|
||||
missingArgs = lib.attrNames
|
||||
# Filter out arguments that have a default value
|
||||
(lib.filterAttrs (name: value: ! value)
|
||||
# Filter out arguments that would be passed
|
||||
(removeAttrs fargs (lib.attrNames allArgs)));
|
||||
|
||||
# Get a list of suggested argument names for a given missing one
|
||||
getSuggestions = arg: lib.pipe (autoArgs // args) [
|
||||
lib.attrNames
|
||||
# Only use ones that are at most 2 edits away. While mork would work,
|
||||
# levenshteinAtMost is only fast for 2 or less.
|
||||
(lib.filter (lib.strings.levenshteinAtMost 2 arg))
|
||||
# Put strings with shorter distance first
|
||||
(lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg))
|
||||
# Only take the first couple results
|
||||
(lib.take 3)
|
||||
# Quote all entries
|
||||
(map (x: "\"" + x + "\""))
|
||||
];
|
||||
|
||||
prettySuggestions = suggestions:
|
||||
if suggestions == [] then ""
|
||||
else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?"
|
||||
else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
|
||||
|
||||
errorForArg = arg:
|
||||
let
|
||||
loc = builtins.unsafeGetAttrPos arg fargs;
|
||||
# loc' can be removed once lib/minver.nix is >2.3.4, since that includes
|
||||
# https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
|
||||
loc' = if loc != null then loc.file + ":" + toString loc.line
|
||||
else if ! lib.isFunction fn then
|
||||
toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix"
|
||||
else "<unknown location>";
|
||||
in "Function called without required argument \"${arg}\" at "
|
||||
+ "${loc'}${prettySuggestions (getSuggestions arg)}";
|
||||
|
||||
# Only show the error for the first missing argument
|
||||
error = errorForArg (lib.head missingArgs);
|
||||
|
||||
in if missingArgs == [] then makeOverridable f allArgs else abort error;
|
||||
|
||||
|
||||
/**
|
||||
Like callPackage, but for a function that returns an attribute
|
||||
set of derivations. The override function is added to the
|
||||
individual attributes.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [autoArgs]
|
||||
- [fn]
|
||||
- [args]
|
||||
|
||||
*/
|
||||
callPackagesWith = autoArgs: fn: args:
|
||||
let
|
||||
f = if lib.isFunction fn then fn else import fn;
|
||||
auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
|
||||
origArgs = auto // args;
|
||||
pkgs = f origArgs;
|
||||
mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
|
||||
in
|
||||
if lib.isDerivation pkgs then throw
|
||||
("function `callPackages` was called on a *single* derivation "
|
||||
+ ''"${pkgs.name or "<unknown-name>"}";''
|
||||
+ " did you mean to use `callPackage` instead?")
|
||||
else lib.mapAttrs mkAttrOverridable pkgs;
|
||||
|
||||
|
||||
/**
|
||||
Add attributes to each output of a derivation without changing
|
||||
the derivation itself and check a given condition when evaluating.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [condition]
|
||||
- [passthru]
|
||||
- [drv]
|
||||
|
||||
*/
|
||||
extendDerivation = condition: passthru: drv:
|
||||
let
|
||||
outputs = drv.outputs or [ "out" ];
|
||||
|
||||
commonAttrs = drv // (builtins.listToAttrs outputsList) //
|
||||
({ all = map (x: x.value) outputsList; }) // passthru;
|
||||
|
||||
outputToAttrListElement = outputName:
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
inherit (drv.${outputName}) type outputName;
|
||||
outputSpecified = true;
|
||||
drvPath = assert condition; drv.${outputName}.drvPath;
|
||||
outPath = assert condition; drv.${outputName}.outPath;
|
||||
} //
|
||||
# TODO: give the derivation control over the outputs.
|
||||
# `overrideAttrs` may not be the only attribute that needs
|
||||
# updating when switching outputs.
|
||||
lib.optionalAttrs (passthru?overrideAttrs) {
|
||||
# TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
|
||||
overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
|
||||
};
|
||||
};
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
in commonAttrs // {
|
||||
drvPath = assert condition; drv.drvPath;
|
||||
outPath = assert condition; drv.outPath;
|
||||
};
|
||||
|
||||
/**
|
||||
Strip a derivation of all non-essential attributes, returning
|
||||
only those needed by hydra-eval-jobs. Also strictly evaluate the
|
||||
result to ensure that there are no thunks kept alive to prevent
|
||||
garbage collection.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [drv]
|
||||
|
||||
*/
|
||||
hydraJob = drv:
|
||||
let
|
||||
outputs = drv.outputs or ["out"];
|
||||
|
||||
commonAttrs =
|
||||
{ inherit (drv) name system meta; inherit outputs; }
|
||||
// lib.optionalAttrs (drv._hydraAggregate or false) {
|
||||
_hydraAggregate = true;
|
||||
constituents = map hydraJob (lib.flatten drv.constituents);
|
||||
}
|
||||
// (lib.listToAttrs outputsList);
|
||||
|
||||
makeOutput = outputName:
|
||||
let output = drv.${outputName}; in
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
outPath = output.outPath;
|
||||
drvPath = output.drvPath;
|
||||
type = "derivation";
|
||||
inherit outputName;
|
||||
};
|
||||
};
|
||||
|
||||
outputsList = map makeOutput outputs;
|
||||
|
||||
drv' = (lib.head outputsList).value;
|
||||
in if drv == null then null else
|
||||
lib.deepSeq drv' drv';
|
||||
|
||||
/**
|
||||
Make a set of packages with a common scope. All packages called
|
||||
with the provided `callPackage` will be evaluated with the same
|
||||
arguments. Any package in the set may depend on any other. The
|
||||
`overrideScope'` function allows subsequent modification of the package
|
||||
set in a consistent way, i.e. all packages in the set will be
|
||||
called with the overridden packages. The package sets may be
|
||||
hierarchical: the packages in the set are called with the scope
|
||||
provided by `newScope` and the set provides a `newScope` attribute
|
||||
which can form the parent scope for later package sets.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [newScope]
|
||||
- [f]
|
||||
|
||||
*/
|
||||
makeScope = newScope: f:
|
||||
let self = f self // {
|
||||
newScope = scope: newScope (self // scope);
|
||||
callPackage = self.newScope {};
|
||||
overrideScope = g: makeScope newScope (lib.fixedPoints.extends g f);
|
||||
# Remove after 24.11 is released.
|
||||
overrideScope' = g: lib.warnIf (lib.isInOldestRelease 2311)
|
||||
"`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
|
||||
(makeScope newScope (lib.fixedPoints.extends g f));
|
||||
packages = f;
|
||||
};
|
||||
in self;
|
||||
|
||||
/**
|
||||
backward compatibility with old uncurried form; deprecated
|
||||
|
||||
# Arguments
|
||||
|
||||
- [splicePackages]
|
||||
- [newScope]
|
||||
- [otherSplices]
|
||||
- [keep]
|
||||
- [extra]
|
||||
- [f]
|
||||
|
||||
*/
|
||||
makeScopeWithSplicing =
|
||||
splicePackages: newScope: otherSplices: keep: extra: f:
|
||||
makeScopeWithSplicing'
|
||||
{ inherit splicePackages newScope; }
|
||||
{ inherit otherSplices keep extra f; };
|
||||
|
||||
/**
|
||||
Like makeScope, but aims to support cross compilation. It's still ugly, but
|
||||
hopefully it helps a little bit.
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
makeScopeWithSplicing' =
|
||||
{ splicePackages
|
||||
, newScope
|
||||
}:
|
||||
{ otherSplices
|
||||
, keep ? (_self: {})
|
||||
, extra ? (_spliced0: {})
|
||||
, f
|
||||
}:
|
||||
let
|
||||
spliced0 = splicePackages {
|
||||
pkgsBuildBuild = otherSplices.selfBuildBuild;
|
||||
pkgsBuildHost = otherSplices.selfBuildHost;
|
||||
pkgsBuildTarget = otherSplices.selfBuildTarget;
|
||||
pkgsHostHost = otherSplices.selfHostHost;
|
||||
pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`;
|
||||
pkgsTargetTarget = otherSplices.selfTargetTarget;
|
||||
};
|
||||
spliced = extra spliced0 // spliced0 // keep self;
|
||||
self = f self // {
|
||||
newScope = scope: newScope (spliced // scope);
|
||||
callPackage = newScope spliced; # == self.newScope {};
|
||||
# N.B. the other stages of the package set spliced in are *not*
|
||||
# overridden.
|
||||
overrideScope = g: (makeScopeWithSplicing'
|
||||
{ inherit splicePackages newScope; }
|
||||
{ inherit otherSplices keep extra;
|
||||
f = lib.fixedPoints.extends g f;
|
||||
});
|
||||
packages = f;
|
||||
};
|
||||
in self;
|
||||
|
||||
}
|
357
pesto/test_data/assets/debug.nix
Normal file
357
pesto/test_data/assets/debug.nix
Normal file
@ -0,0 +1,357 @@
|
||||
/**
|
||||
Collection of functions useful for debugging
|
||||
broken nix expressions.
|
||||
* `trace`-like functions take two values, print
|
||||
the first to stderr and return the second.
|
||||
* `traceVal`-like functions take one argument
|
||||
which both printed and returned.
|
||||
* `traceSeq`-like functions fully evaluate their
|
||||
traced value before printing (not just to “weak
|
||||
head normal form” like trace does by default).
|
||||
* Functions that end in `-Fn` take an additional
|
||||
function as their first argument, which is applied
|
||||
to the traced value before it is printed.
|
||||
*/
|
||||
{ lib }:
|
||||
let
|
||||
inherit (lib)
|
||||
isList
|
||||
isAttrs
|
||||
substring
|
||||
attrValues
|
||||
concatLists
|
||||
const
|
||||
elem
|
||||
generators
|
||||
id
|
||||
mapAttrs
|
||||
trace;
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
# -- TRACING --
|
||||
|
||||
/**
|
||||
Conditionally trace the supplied message, based on a predicate.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
traceIf true "hello" 3
|
||||
trace: hello
|
||||
=> 3
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
traceIf :: bool -> string -> a -> a
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [pred] Predicate to check
|
||||
- [msg] Message that should be traced
|
||||
- [x] Value to return
|
||||
|
||||
*/
|
||||
traceIf =
|
||||
# Predicate to check
|
||||
pred:
|
||||
# Message that should be traced
|
||||
msg:
|
||||
# Value to return
|
||||
x: if pred then trace msg x else x;
|
||||
|
||||
/**
|
||||
Trace the supplied value after applying a function to it, and
|
||||
return the original value.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
traceValFn (v: "mystring ${v}") "foo"
|
||||
trace: mystring foo
|
||||
=> "foo"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
traceValFn :: (a -> b) -> a -> a
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f] Function to apply
|
||||
- [x] Value to trace and return
|
||||
|
||||
*/
|
||||
traceValFn =
|
||||
# Function to apply
|
||||
f:
|
||||
# Value to trace and return
|
||||
x: trace (f x) x;
|
||||
|
||||
/**
|
||||
Trace the supplied value and return it.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
traceVal 42
|
||||
# trace: 42
|
||||
=> 42
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
traceVal :: a -> a
|
||||
```
|
||||
*/
|
||||
traceVal = traceValFn id;
|
||||
|
||||
/**
|
||||
`builtins.trace`, but the value is `builtins.deepSeq`ed first.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
trace { a.b.c = 3; } null
|
||||
trace: { a = <CODE>; }
|
||||
=> null
|
||||
traceSeq { a.b.c = 3; } null
|
||||
trace: { a = { b = { c = 3; }; }; }
|
||||
=> null
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
traceSeq :: a -> b -> b
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [x] The value to trace
|
||||
- [y] The value to return
|
||||
|
||||
*/
|
||||
traceSeq =
|
||||
# The value to trace
|
||||
x:
|
||||
# The value to return
|
||||
y: trace (builtins.deepSeq x x) y;
|
||||
|
||||
/**
|
||||
Like `traceSeq`, but only evaluate down to depth n.
|
||||
This is very useful because lots of `traceSeq` usages
|
||||
lead to an infinite recursion.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
traceSeqN 2 { a.b.c = 3; } null
|
||||
trace: { a = { b = {…}; }; }
|
||||
=> null
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
traceSeqN :: Int -> a -> b -> b
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [depth]
|
||||
- [x]
|
||||
- [y]
|
||||
|
||||
*/
|
||||
traceSeqN = depth: x: y:
|
||||
let snip = v: if isList v then noQuotes "[…]" v
|
||||
else if isAttrs v then noQuotes "{…}" v
|
||||
else v;
|
||||
noQuotes = str: v: { __pretty = const str; val = v; };
|
||||
modify = n: fn: v: if (n == 0) then fn v
|
||||
else if isList v then map (modify (n - 1) fn) v
|
||||
else if isAttrs v then mapAttrs
|
||||
(const (modify (n - 1) fn)) v
|
||||
else v;
|
||||
in trace (generators.toPretty { allowPrettyValues = true; }
|
||||
(modify depth snip x)) y;
|
||||
|
||||
/**
|
||||
A combination of `traceVal` and `traceSeq` that applies a
|
||||
provided function to the value to be traced after `deepSeq`ing
|
||||
it.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f] Function to apply
|
||||
- [v] Value to trace
|
||||
|
||||
*/
|
||||
traceValSeqFn =
|
||||
# Function to apply
|
||||
f:
|
||||
# Value to trace
|
||||
v: traceValFn f (builtins.deepSeq v v);
|
||||
|
||||
/**
|
||||
A combination of `traceVal` and `traceSeq`.
|
||||
*/
|
||||
traceValSeq = traceValSeqFn id;
|
||||
|
||||
/**
|
||||
A combination of `traceVal` and `traceSeqN` that applies a
|
||||
provided function to the value to be traced.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f] Function to apply
|
||||
- [depth]
|
||||
- [v] Value to trace
|
||||
|
||||
*/
|
||||
traceValSeqNFn =
|
||||
# Function to apply
|
||||
f:
|
||||
depth:
|
||||
# Value to trace
|
||||
v: traceSeqN depth (f v) v;
|
||||
|
||||
/**
|
||||
A combination of `traceVal` and `traceSeqN`.
|
||||
*/
|
||||
traceValSeqN = traceValSeqNFn id;
|
||||
|
||||
/**
|
||||
Trace the input and output of a function `f` named `name`,
|
||||
both down to `depth`.
|
||||
This is useful for adding around a function call,
|
||||
to see the before/after of values as they are transformed.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
|
||||
trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
|
||||
=> { a.b.c = 3; }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [depth]
|
||||
- [name]
|
||||
- [f]
|
||||
- [v]
|
||||
|
||||
*/
|
||||
traceFnSeqN = depth: name: f: v:
|
||||
let res = f v;
|
||||
in lib.traceSeqN
|
||||
(depth + 1)
|
||||
{
|
||||
fn = name;
|
||||
from = v;
|
||||
to = res;
|
||||
}
|
||||
res;
|
||||
|
||||
|
||||
# -- TESTING --
|
||||
|
||||
/**
|
||||
Evaluates a set of tests.
|
||||
A test is an attribute set `{expr, expected}`,
|
||||
denoting an expression and its expected result.
|
||||
The result is a `list` of __failed tests__, each represented as
|
||||
`{name, expected, result}`,
|
||||
- expected
|
||||
- What was passed as `expected`
|
||||
- result
|
||||
- The actual `result` of the test
|
||||
Used for regression testing of the functions in lib; see
|
||||
tests.nix for more examples.
|
||||
Important: Only attributes that start with `test` are executed.
|
||||
- If you want to run only a subset of the tests add the attribute `tests = ["testName"];`
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
runTests {
|
||||
testAndOk = {
|
||||
expr = lib.and true false;
|
||||
expected = false;
|
||||
};
|
||||
testAndFail = {
|
||||
expr = lib.and true false;
|
||||
expected = true;
|
||||
};
|
||||
}
|
||||
->
|
||||
[
|
||||
{
|
||||
name = "testAndFail";
|
||||
expected = true;
|
||||
result = false;
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
runTests :: {
|
||||
tests = [ String ];
|
||||
${testName} :: {
|
||||
expr :: a;
|
||||
expected :: a;
|
||||
};
|
||||
}
|
||||
->
|
||||
[
|
||||
{
|
||||
name :: String;
|
||||
expected :: a;
|
||||
result :: a;
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [tests] Tests to run
|
||||
|
||||
*/
|
||||
runTests =
|
||||
# Tests to run
|
||||
tests: concatLists (attrValues (mapAttrs (name: test:
|
||||
let testsToRun = if tests ? tests then tests.tests else [];
|
||||
in if (substring 0 4 name == "test" || elem name testsToRun)
|
||||
&& ((testsToRun == []) || elem name tests.tests)
|
||||
&& (test.expr != test.expected)
|
||||
|
||||
then [ { inherit name; expected = test.expected; result = test.expr; } ]
|
||||
else [] ) tests));
|
||||
|
||||
/**
|
||||
Create a test assuming that list elements are `true`.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
{ testX = allTrue [ true ]; }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [expr]
|
||||
|
||||
*/
|
||||
testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
|
||||
}
|
170
pesto/test_data/assets/default.nix
Normal file
170
pesto/test_data/assets/default.nix
Normal file
@ -0,0 +1,170 @@
|
||||
/**
|
||||
Library of low-level helper functions for nix expressions.
|
||||
*
|
||||
* Please implement (mostly) exhaustive unit tests
|
||||
* for new functions in `./tests.nix`.
|
||||
*/
|
||||
let
|
||||
|
||||
inherit (import ./fixed-points.nix { inherit lib; }) makeExtensible;
|
||||
|
||||
lib = makeExtensible (self: let
|
||||
callLibs = file: import file { lib = self; };
|
||||
in {
|
||||
|
||||
# often used, or depending on very little
|
||||
trivial = callLibs ./trivial.nix;
|
||||
fixedPoints = callLibs ./fixed-points.nix;
|
||||
|
||||
# datatypes
|
||||
attrsets = callLibs ./attrsets.nix;
|
||||
lists = callLibs ./lists.nix;
|
||||
strings = callLibs ./strings.nix;
|
||||
stringsWithDeps = callLibs ./strings-with-deps.nix;
|
||||
|
||||
# packaging
|
||||
customisation = callLibs ./customisation.nix;
|
||||
derivations = callLibs ./derivations.nix;
|
||||
maintainers = import ../maintainers/maintainer-list.nix;
|
||||
teams = callLibs ../maintainers/team-list.nix;
|
||||
meta = callLibs ./meta.nix;
|
||||
versions = callLibs ./versions.nix;
|
||||
|
||||
# module system
|
||||
modules = callLibs ./modules.nix;
|
||||
options = callLibs ./options.nix;
|
||||
types = callLibs ./types.nix;
|
||||
|
||||
# constants
|
||||
licenses = callLibs ./licenses.nix;
|
||||
sourceTypes = callLibs ./source-types.nix;
|
||||
systems = callLibs ./systems;
|
||||
|
||||
# serialization
|
||||
cli = callLibs ./cli.nix;
|
||||
gvariant = callLibs ./gvariant.nix;
|
||||
generators = callLibs ./generators.nix;
|
||||
|
||||
# misc
|
||||
asserts = callLibs ./asserts.nix;
|
||||
debug = callLibs ./debug.nix;
|
||||
misc = callLibs ./deprecated.nix;
|
||||
|
||||
# domain-specific
|
||||
fetchers = callLibs ./fetchers.nix;
|
||||
|
||||
# Eval-time filesystem handling
|
||||
path = callLibs ./path;
|
||||
filesystem = callLibs ./filesystem.nix;
|
||||
fileset = callLibs ./fileset;
|
||||
sources = callLibs ./sources.nix;
|
||||
|
||||
# back-compat aliases
|
||||
platforms = self.systems.doubles;
|
||||
|
||||
# linux kernel configuration
|
||||
kernel = callLibs ./kernel.nix;
|
||||
|
||||
inherit (builtins) add addErrorContext attrNames concatLists
|
||||
deepSeq elem elemAt filter genericClosure genList getAttr
|
||||
hasAttr head isAttrs isBool isInt isList isPath isString length
|
||||
lessThan listToAttrs pathExists readFile replaceStrings seq
|
||||
stringLength sub substring tail trace;
|
||||
inherit (self.trivial) id const pipe concat or and bitAnd bitOr bitXor
|
||||
bitNot boolToString mergeAttrs flip mapNullable inNixShell isFloat min max
|
||||
importJSON importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum
|
||||
info showWarnings nixpkgsVersion version isInOldestRelease
|
||||
mod compare splitByAndCompare
|
||||
functionArgs setFunctionArgs isFunction toFunction
|
||||
toHexString toBaseDigits inPureEvalMode;
|
||||
inherit (self.fixedPoints) fix fix' converge extends composeExtensions
|
||||
composeManyExtensions makeExtensible makeExtensibleWithCustomName;
|
||||
inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
|
||||
getAttrFromPath attrVals attrValues getAttrs catAttrs filterAttrs
|
||||
filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
|
||||
mapAttrs' mapAttrsToList attrsToList concatMapAttrs mapAttrsRecursive
|
||||
mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs
|
||||
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
|
||||
recursiveUpdate matchAttrs overrideExisting showAttrPath getOutput getBin
|
||||
getLib getDev getMan chooseDevOutputs zipWithNames zip
|
||||
recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets
|
||||
updateManyAttrsByPath;
|
||||
inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
|
||||
concatMap flatten remove findSingle findFirst any all count
|
||||
optional optionals toList range replicate partition zipListsWith zipLists
|
||||
reverseList listDfs toposort sort naturalSort compareLists take
|
||||
drop sublist last init crossLists unique intersectLists
|
||||
subtractLists mutuallyExclusive groupBy groupBy';
|
||||
inherit (self.strings) concatStrings concatMapStrings concatImapStrings
|
||||
intersperse concatStringsSep concatMapStringsSep
|
||||
concatImapStringsSep concatLines makeSearchPath makeSearchPathOutput
|
||||
makeLibraryPath makeBinPath optionalString
|
||||
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
|
||||
escapeShellArg escapeShellArgs
|
||||
isStorePath isStringLike
|
||||
isValidPosixName toShellVar toShellVars
|
||||
escapeRegex escapeURL escapeXML replaceChars lowerChars
|
||||
upperChars toLower toUpper addContextFrom splitString
|
||||
removePrefix removeSuffix versionOlder versionAtLeast
|
||||
getName getVersion
|
||||
cmakeOptionType cmakeBool cmakeFeature
|
||||
mesonOption mesonBool mesonEnable
|
||||
nameFromURL enableFeature enableFeatureAs withFeature
|
||||
withFeatureAs fixedWidthString fixedWidthNumber
|
||||
toInt toIntBase10 readPathsFromFile fileContents;
|
||||
inherit (self.stringsWithDeps) textClosureList textClosureMap
|
||||
noDepEntry fullDepEntry packEntry stringAfter;
|
||||
inherit (self.customisation) overrideDerivation makeOverridable
|
||||
callPackageWith callPackagesWith extendDerivation hydraJob
|
||||
makeScope makeScopeWithSplicing makeScopeWithSplicing';
|
||||
inherit (self.derivations) lazyDerivation;
|
||||
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
|
||||
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
|
||||
hiPrioSet getLicenseFromSpdxId getExe getExe';
|
||||
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile;
|
||||
inherit (self.sources) cleanSourceFilter
|
||||
cleanSource sourceByRegex sourceFilesBySuffices
|
||||
commitIdFromGitRepo cleanSourceWith pathHasContext
|
||||
canCleanSource pathIsGitRepo;
|
||||
inherit (self.modules) evalModules setDefaultModuleLocation
|
||||
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
|
||||
mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions
|
||||
pushDownProperties dischargeProperties filterOverrides
|
||||
sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
|
||||
mkOptionDefault mkDefault mkImageMediaOverride mkForce mkVMOverride
|
||||
mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
|
||||
mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
|
||||
mkRenamedOptionModule mkRenamedOptionModuleWith
|
||||
mkMergedOptionModule mkChangedOptionModule
|
||||
mkAliasOptionModule mkDerivedConfig doRename
|
||||
mkAliasOptionModuleMD;
|
||||
inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions
|
||||
mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
|
||||
getValues getFiles
|
||||
optionAttrSetToDocList optionAttrSetToDocList'
|
||||
scrubOptionValue literalExpression literalExample
|
||||
showOption showOptionWithDefLocs showFiles
|
||||
unknownModule mkOption mkPackageOption mkPackageOptionMD
|
||||
mdDoc literalMD;
|
||||
inherit (self.types) isType setType defaultTypeMerge defaultFunctor
|
||||
isOptionType mkOptionType;
|
||||
inherit (self.asserts)
|
||||
assertMsg assertOneOf;
|
||||
inherit (self.debug) traceIf traceVal traceValFn
|
||||
traceSeq traceSeqN traceValSeq
|
||||
traceValSeqFn traceValSeqN traceValSeqNFn traceFnSeqN
|
||||
runTests testAllTrue;
|
||||
inherit (self.misc) maybeEnv defaultMergeArg defaultMerge foldArgs
|
||||
maybeAttrNullable maybeAttr ifEnable checkFlag getValue
|
||||
checkReqs uniqList uniqListExt condConcat lazyGenericClosure
|
||||
innerModifySumArgs modifySumArgs innerClosePropagation
|
||||
closePropagation mapAttrsFlatten nvs setAttr setAttrMerge
|
||||
mergeAttrsWithFunc mergeAttrsConcatenateValues
|
||||
mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults
|
||||
mergeAttrsByFuncDefaultsClean mergeAttrBy
|
||||
fakeHash fakeSha256 fakeSha512
|
||||
nixType imap;
|
||||
inherit (self.versions)
|
||||
splitVersion;
|
||||
});
|
||||
in lib
|
306
pesto/test_data/assets/deprecated.nix
Normal file
306
pesto/test_data/assets/deprecated.nix
Normal file
@ -0,0 +1,306 @@
|
||||
{ lib }:
|
||||
let
|
||||
inherit (builtins) head tail isList isAttrs isInt attrNames;
|
||||
|
||||
in
|
||||
|
||||
with lib.lists;
|
||||
with lib.attrsets;
|
||||
with lib.strings;
|
||||
|
||||
rec {
|
||||
|
||||
# returns default if env var is not set
|
||||
maybeEnv = name: default:
|
||||
let value = builtins.getEnv name; in
|
||||
if value == "" then default else value;
|
||||
|
||||
defaultMergeArg = x : y: if builtins.isAttrs y then
|
||||
y
|
||||
else
|
||||
(y x);
|
||||
defaultMerge = x: y: x // (defaultMergeArg x y);
|
||||
foldArgs = merger: f: init: x:
|
||||
let arg = (merger init (defaultMergeArg init x));
|
||||
# now add the function with composed args already applied to the final attrs
|
||||
base = (setAttrMerge "passthru" {} (f arg)
|
||||
( z: z // {
|
||||
function = foldArgs merger f arg;
|
||||
args = (lib.attrByPath ["passthru" "args"] {} z) // x;
|
||||
} ));
|
||||
withStdOverrides = base // {
|
||||
override = base.passthru.function;
|
||||
};
|
||||
in
|
||||
withStdOverrides;
|
||||
|
||||
|
||||
# shortcut for attrByPath ["name"] default attrs
|
||||
maybeAttrNullable = maybeAttr;
|
||||
|
||||
# shortcut for attrByPath ["name"] default attrs
|
||||
maybeAttr = name: default: attrs: attrs.${name} or default;
|
||||
|
||||
|
||||
# Return the second argument if the first one is true or the empty version
|
||||
# of the second argument.
|
||||
ifEnable = cond: val:
|
||||
if cond then val
|
||||
else if builtins.isList val then []
|
||||
else if builtins.isAttrs val then {}
|
||||
# else if builtins.isString val then ""
|
||||
else if val == true || val == false then false
|
||||
else null;
|
||||
|
||||
|
||||
# Return true only if there is an attribute and it is true.
|
||||
checkFlag = attrSet: name:
|
||||
if name == "true" then true else
|
||||
if name == "false" then false else
|
||||
if (elem name (attrByPath ["flags"] [] attrSet)) then true else
|
||||
attrByPath [name] false attrSet ;
|
||||
|
||||
|
||||
# Input : attrSet, [ [name default] ... ], name
|
||||
# Output : its value or default.
|
||||
getValue = attrSet: argList: name:
|
||||
( attrByPath [name] (if checkFlag attrSet name then true else
|
||||
if argList == [] then null else
|
||||
let x = builtins.head argList; in
|
||||
if (head x) == name then
|
||||
(head (tail x))
|
||||
else (getValue attrSet
|
||||
(tail argList) name)) attrSet );
|
||||
|
||||
|
||||
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
|
||||
# Output : are reqs satisfied? It's asserted.
|
||||
checkReqs = attrSet: argList: condList:
|
||||
(
|
||||
foldr lib.and true
|
||||
(map (x: let name = (head x); in
|
||||
|
||||
((checkFlag attrSet name) ->
|
||||
(foldr lib.and true
|
||||
(map (y: let val=(getValue attrSet argList y); in
|
||||
(val!=null) && (val!=false))
|
||||
(tail x))))) condList));
|
||||
|
||||
|
||||
# This function has O(n^2) performance.
|
||||
uniqList = { inputList, acc ? [] }:
|
||||
let go = xs: acc:
|
||||
if xs == []
|
||||
then []
|
||||
else let x = head xs;
|
||||
y = if elem x acc then [] else [x];
|
||||
in y ++ go (tail xs) (y ++ acc);
|
||||
in go inputList acc;
|
||||
|
||||
uniqListExt = { inputList,
|
||||
outputList ? [],
|
||||
getter ? (x: x),
|
||||
compare ? (x: y: x==y) }:
|
||||
if inputList == [] then outputList else
|
||||
let x = head inputList;
|
||||
isX = y: (compare (getter y) (getter x));
|
||||
newOutputList = outputList ++
|
||||
(if any isX outputList then [] else [x]);
|
||||
in uniqListExt { outputList = newOutputList;
|
||||
inputList = (tail inputList);
|
||||
inherit getter compare;
|
||||
};
|
||||
|
||||
condConcat = name: list: checker:
|
||||
if list == [] then name else
|
||||
if checker (head list) then
|
||||
condConcat
|
||||
(name + (head (tail list)))
|
||||
(tail (tail list))
|
||||
checker
|
||||
else condConcat
|
||||
name (tail (tail list)) checker;
|
||||
|
||||
lazyGenericClosure = {startSet, operator}:
|
||||
let
|
||||
work = list: doneKeys: result:
|
||||
if list == [] then
|
||||
result
|
||||
else
|
||||
let x = head list; key = x.key; in
|
||||
if elem key doneKeys then
|
||||
work (tail list) doneKeys result
|
||||
else
|
||||
work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
|
||||
in
|
||||
work startSet [] [];
|
||||
|
||||
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
|
||||
innerModifySumArgs f x (a // b);
|
||||
modifySumArgs = f: x: innerModifySumArgs f x {};
|
||||
|
||||
|
||||
innerClosePropagation = acc: xs:
|
||||
if xs == []
|
||||
then acc
|
||||
else let y = head xs;
|
||||
ys = tail xs;
|
||||
in if ! isAttrs y
|
||||
then innerClosePropagation acc ys
|
||||
else let acc' = [y] ++ acc;
|
||||
in innerClosePropagation
|
||||
acc'
|
||||
(uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
|
||||
++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
|
||||
++ ys;
|
||||
acc = acc';
|
||||
}
|
||||
);
|
||||
|
||||
closePropagationSlow = list: (uniqList {inputList = (innerClosePropagation [] list);});
|
||||
|
||||
# This is an optimisation of lib.closePropagation which avoids the O(n^2) behavior
|
||||
# Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs
|
||||
# The ordering / sorting / comparison is done based on the `outPath`
|
||||
# attribute of each derivation.
|
||||
# On some benchmarks, it performs up to 15 times faster than lib.closePropagation.
|
||||
# See https://github.com/NixOS/nixpkgs/pull/194391 for details.
|
||||
closePropagationFast = list:
|
||||
builtins.map (x: x.val) (builtins.genericClosure {
|
||||
startSet = builtins.map (x: {
|
||||
key = x.outPath;
|
||||
val = x;
|
||||
}) (builtins.filter (x: x != null) list);
|
||||
operator = item:
|
||||
if !builtins.isAttrs item.val then
|
||||
[ ]
|
||||
else
|
||||
builtins.concatMap (x:
|
||||
if x != null then [{
|
||||
key = x.outPath;
|
||||
val = x;
|
||||
}] else
|
||||
[ ]) ((item.val.propagatedBuildInputs or [ ])
|
||||
++ (item.val.propagatedNativeBuildInputs or [ ]));
|
||||
});
|
||||
|
||||
closePropagation = if builtins ? genericClosure
|
||||
then closePropagationFast
|
||||
else closePropagationSlow;
|
||||
|
||||
# calls a function (f attr value ) for each record item. returns a list
|
||||
mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r);
|
||||
|
||||
# attribute set containing one attribute
|
||||
nvs = name: value: listToAttrs [ (nameValuePair name value) ];
|
||||
# adds / replaces an attribute of an attribute set
|
||||
setAttr = set: name: v: set // (nvs name v);
|
||||
|
||||
# setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
|
||||
# setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
|
||||
# setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
|
||||
setAttrMerge = name: default: attrs: f:
|
||||
setAttr attrs name (f (maybeAttr name default attrs));
|
||||
|
||||
# Using f = a: b = b the result is similar to //
|
||||
# merge attributes with custom function handling the case that the attribute
|
||||
# exists in both sets
|
||||
mergeAttrsWithFunc = f: set1: set2:
|
||||
foldr (n: set: if set ? ${n}
|
||||
then setAttr set n (f set.${n} set2.${n})
|
||||
else set )
|
||||
(set2 // set1) (attrNames set2);
|
||||
|
||||
# merging two attribute set concatenating the values of same attribute names
|
||||
# eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
|
||||
mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) );
|
||||
|
||||
# merges attributes using //, if a name exists in both attributes
|
||||
# an error will be triggered unless its listed in mergeLists
|
||||
# so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get
|
||||
# { buildInputs = [a b]; }
|
||||
# merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
|
||||
# in these cases the first buildPhase will override the second one
|
||||
# ! deprecated, use mergeAttrByFunc instead
|
||||
mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
|
||||
overrideSnd ? [ "buildPhase" ]
|
||||
}: attrs1: attrs2:
|
||||
foldr (n: set:
|
||||
setAttr set n ( if set ? ${n}
|
||||
then # merge
|
||||
if elem n mergeLists # attribute contains list, merge them by concatenating
|
||||
then attrs2.${n} ++ attrs1.${n}
|
||||
else if elem n overrideSnd
|
||||
then attrs1.${n}
|
||||
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
|
||||
else attrs2.${n} # add attribute not existing in attr1
|
||||
)) attrs1 (attrNames attrs2);
|
||||
|
||||
|
||||
# example usage:
|
||||
# mergeAttrByFunc {
|
||||
# inherit mergeAttrBy; # defined below
|
||||
# buildInputs = [ a b ];
|
||||
# } {
|
||||
# buildInputs = [ c d ];
|
||||
# };
|
||||
# will result in
|
||||
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
|
||||
# is used by defaultOverridableDelayableArgs and can be used when composing using
|
||||
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
|
||||
mergeAttrByFunc = x: y:
|
||||
let
|
||||
mergeAttrBy2 = { mergeAttrBy = lib.mergeAttrs; }
|
||||
// (maybeAttr "mergeAttrBy" {} x)
|
||||
// (maybeAttr "mergeAttrBy" {} y); in
|
||||
foldr lib.mergeAttrs {} [
|
||||
x y
|
||||
(mapAttrs ( a: v: # merge special names using given functions
|
||||
if x ? ${a}
|
||||
then if y ? ${a}
|
||||
then v x.${a} y.${a} # both have attr, use merge func
|
||||
else x.${a} # only x has attr
|
||||
else y.${a} # only y has attr)
|
||||
) (removeAttrs mergeAttrBy2
|
||||
# don't merge attrs which are neither in x nor y
|
||||
(filter (a: ! x ? ${a} && ! y ? ${a})
|
||||
(attrNames mergeAttrBy2))
|
||||
)
|
||||
)
|
||||
];
|
||||
mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
|
||||
mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
|
||||
|
||||
# sane defaults (same name as attr name so that inherit can be used)
|
||||
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
|
||||
listToAttrs (map (n: nameValuePair n lib.concat)
|
||||
[ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
|
||||
// listToAttrs (map (n: nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
|
||||
// listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
|
||||
;
|
||||
|
||||
nixType = x:
|
||||
if isAttrs x then
|
||||
if x ? outPath then "derivation"
|
||||
else "attrs"
|
||||
else if lib.isFunction x then "function"
|
||||
else if isList x then "list"
|
||||
else if x == true then "bool"
|
||||
else if x == false then "bool"
|
||||
else if x == null then "null"
|
||||
else if isInt x then "int"
|
||||
else "string";
|
||||
|
||||
/**
|
||||
deprecated:
|
||||
For historical reasons, imap has an index starting at 1.
|
||||
But for consistency with the rest of the library we want an index
|
||||
starting at zero.
|
||||
*/
|
||||
imap = imap1;
|
||||
|
||||
# Fake hashes. Can be used as hash placeholders, when computing hash ahead isn't trivial
|
||||
fakeHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||
fakeSha256 = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
fakeSha512 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
}
|
87
pesto/test_data/assets/derivations.nix
Normal file
87
pesto/test_data/assets/derivations.nix
Normal file
@ -0,0 +1,87 @@
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
inherit (lib) throwIfNot;
|
||||
in
|
||||
{
|
||||
/**
|
||||
Restrict a derivation to a predictable set of attribute names, so
|
||||
that the returned attrset is not strict in the actual derivation,
|
||||
saving a lot of computation when the derivation is non-trivial.
|
||||
This is useful in situations where a derivation might only be used for its
|
||||
passthru attributes, improving evaluation performance.
|
||||
The returned attribute set is lazy in `derivation`. Specifically, this
|
||||
means that the derivation will not be evaluated in at least the
|
||||
situations below.
|
||||
For illustration and/or testing, we define derivation such that its
|
||||
evaluation is very noticeable.
|
||||
let derivation = throw "This won't be evaluated.";
|
||||
In the following expressions, `derivation` will _not_ be evaluated:
|
||||
(lazyDerivation { inherit derivation; }).type
|
||||
attrNames (lazyDerivation { inherit derivation; })
|
||||
(lazyDerivation { inherit derivation; } // { foo = true; }).foo
|
||||
(lazyDerivation { inherit derivation; meta.foo = true; }).meta
|
||||
In these expressions, `derivation` _will_ be evaluated:
|
||||
"${lazyDerivation { inherit derivation }}"
|
||||
(lazyDerivation { inherit derivation }).outPath
|
||||
(lazyDerivation { inherit derivation }).meta
|
||||
And the following expressions are not valid, because the refer to
|
||||
implementation details and/or attributes that may not be present on
|
||||
some derivations:
|
||||
(lazyDerivation { inherit derivation }).buildInputs
|
||||
(lazyDerivation { inherit derivation }).passthru
|
||||
(lazyDerivation { inherit derivation }).pythonPath
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
lazyDerivation =
|
||||
args@{
|
||||
# The derivation to be wrapped.
|
||||
derivation
|
||||
, # Optional meta attribute.
|
||||
#
|
||||
# While this function is primarily about derivations, it can improve
|
||||
# the `meta` package attribute, which is usually specified through
|
||||
# `mkDerivation`.
|
||||
meta ? null
|
||||
, # Optional extra values to add to the returned attrset.
|
||||
#
|
||||
# This can be used for adding package attributes, such as `tests`.
|
||||
passthru ? { }
|
||||
}:
|
||||
let
|
||||
# These checks are strict in `drv` and some `drv` attributes, but the
|
||||
# attrset spine returned by lazyDerivation does not depend on it.
|
||||
# Instead, the individual derivation attributes do depend on it.
|
||||
checked =
|
||||
throwIfNot (derivation.type or null == "derivation")
|
||||
"lazySimpleDerivation: input must be a derivation."
|
||||
throwIfNot
|
||||
(derivation.outputs == [ "out" ])
|
||||
# Supporting multiple outputs should be a matter of inheriting more attrs.
|
||||
"The derivation ${derivation.name or "<unknown>"} has multiple outputs. This is not supported by lazySimpleDerivation yet. Support could be added, and be useful as long as the set of outputs is known in advance, without evaluating the actual derivation."
|
||||
derivation;
|
||||
in
|
||||
{
|
||||
# Hardcoded `type`
|
||||
#
|
||||
# `lazyDerivation` requires its `derivation` argument to be a derivation,
|
||||
# so if it is not, that is a programming error by the caller and not
|
||||
# something that `lazyDerivation` consumers should be able to correct
|
||||
# for after the fact.
|
||||
# So, to improve laziness, we assume correctness here and check it only
|
||||
# when actual derivation values are accessed later.
|
||||
type = "derivation";
|
||||
|
||||
# A fixed set of derivation values, so that `lazyDerivation` can return
|
||||
# its attrset before evaluating `derivation`.
|
||||
# This must only list attributes that are available on _all_ derivations.
|
||||
inherit (checked) outputs out outPath outputName drvPath name system;
|
||||
|
||||
# The meta attribute can either be taken from the derivation, or if the
|
||||
# `lazyDerivation` caller knew a shortcut, be taken from there.
|
||||
meta = args.meta or checked.meta;
|
||||
} // passthru;
|
||||
}
|
13
pesto/test_data/assets/fetchers.nix
Normal file
13
pesto/test_data/assets/fetchers.nix
Normal file
@ -0,0 +1,13 @@
|
||||
# snippets that can be shared by multiple fetchers (pkgs/build-support)
|
||||
{ lib }:
|
||||
{
|
||||
|
||||
proxyImpureEnvVars = [
|
||||
# We borrow these environment variables from the caller to allow
|
||||
# easy proxy configuration. This is impure, but a fixed-output
|
||||
# derivation like fetchurl is allowed to do so since its result is
|
||||
# by definition pure.
|
||||
"http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
|
||||
];
|
||||
|
||||
}
|
460
pesto/test_data/assets/fileset/default.nix
Normal file
460
pesto/test_data/assets/fileset/default.nix
Normal file
@ -0,0 +1,460 @@
|
||||
{ lib }:
|
||||
let
|
||||
|
||||
inherit (import ./internal.nix { inherit lib; })
|
||||
_coerce
|
||||
_coerceMany
|
||||
_toSourceFilter
|
||||
_unionMany
|
||||
_printFileset
|
||||
_intersection
|
||||
;
|
||||
|
||||
inherit (builtins)
|
||||
isList
|
||||
isPath
|
||||
pathExists
|
||||
seq
|
||||
typeOf
|
||||
;
|
||||
|
||||
inherit (lib.lists)
|
||||
elemAt
|
||||
imap0
|
||||
;
|
||||
|
||||
inherit (lib.path)
|
||||
hasPrefix
|
||||
splitRoot
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
isStringLike
|
||||
;
|
||||
|
||||
inherit (lib.filesystem)
|
||||
pathType
|
||||
;
|
||||
|
||||
inherit (lib.sources)
|
||||
cleanSourceWith
|
||||
;
|
||||
|
||||
inherit (lib.trivial)
|
||||
pipe
|
||||
;
|
||||
|
||||
in {
|
||||
|
||||
/**
|
||||
Add the local files contained in `fileset` to the store as a single [store path](https://nixos.org/manual/nix/stable/glossary#gloss-store-path) rooted at `root`.
|
||||
The result is the store path as a string-like value, making it usable e.g. as the `src` of a derivation, or in string interpolation:
|
||||
```nix
|
||||
stdenv.mkDerivation {
|
||||
src = lib.fileset.toSource { ... };
|
||||
# ...
|
||||
}
|
||||
```
|
||||
The name of the store path is always `source`.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Import the current directory into the store
|
||||
# but only include files under ./src
|
||||
toSource {
|
||||
root = ./.;
|
||||
fileset = ./src;
|
||||
}
|
||||
=> "/nix/store/...-source"
|
||||
# Import the current directory into the store
|
||||
# but only include ./Makefile and all files under ./src
|
||||
toSource {
|
||||
root = ./.;
|
||||
fileset = union
|
||||
./Makefile
|
||||
./src;
|
||||
}
|
||||
=> "/nix/store/...-source"
|
||||
# Trying to include a file outside the root will fail
|
||||
toSource {
|
||||
root = ./.;
|
||||
fileset = unions [
|
||||
./Makefile
|
||||
./src
|
||||
../LICENSE
|
||||
];
|
||||
}
|
||||
=> <error>
|
||||
# The root needs to point to a directory that contains all the files
|
||||
toSource {
|
||||
root = ../.;
|
||||
fileset = unions [
|
||||
./Makefile
|
||||
./src
|
||||
../LICENSE
|
||||
];
|
||||
}
|
||||
=> "/nix/store/...-source"
|
||||
# The root has to be a local filesystem path
|
||||
toSource {
|
||||
root = "/nix/store/...-source";
|
||||
fileset = ./.;
|
||||
}
|
||||
=> <error>
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toSource :: {
|
||||
root :: Path,
|
||||
fileset :: FileSet,
|
||||
} -> SourceLike
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
toSource = {
|
||||
/**
|
||||
(required) The local directory [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) that will correspond to the root of the resulting store path.
|
||||
Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`.
|
||||
`root` has to be a directory.
|
||||
<!-- Ignore the indentation here, this is a nixdoc rendering bug that needs to be fixed: https://github.com/nix-community/nixdoc/issues/75 -->
|
||||
:::{.note}
|
||||
Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store.
|
||||
The only way to change which files get added to the store is by changing the `fileset` attribute.
|
||||
:::
|
||||
*/
|
||||
root,
|
||||
/**
|
||||
(required) The file set whose files to import into the store.
|
||||
File sets can be created using other functions in this library.
|
||||
This argument can also be a path,
|
||||
which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
<!-- Ignore the indentation here, this is a nixdoc rendering bug that needs to be fixed: https://github.com/nix-community/nixdoc/issues/75 -->
|
||||
:::{.note}
|
||||
If a directory does not recursively contain any file, it is omitted from the store path contents.
|
||||
:::
|
||||
*/
|
||||
fileset,
|
||||
}:
|
||||
let
|
||||
# We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement
|
||||
filesetArg = fileset;
|
||||
in
|
||||
let
|
||||
fileset = _coerce "lib.fileset.toSource: `fileset`" filesetArg;
|
||||
rootFilesystemRoot = (splitRoot root).root;
|
||||
filesetFilesystemRoot = (splitRoot fileset._internalBase).root;
|
||||
sourceFilter = _toSourceFilter fileset;
|
||||
in
|
||||
if ! isPath root then
|
||||
if isStringLike root then
|
||||
throw ''
|
||||
lib.fileset.toSource: `root` ("${toString root}") is a string-like value, but it should be a path instead.
|
||||
Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
|
||||
else
|
||||
throw ''
|
||||
lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.''
|
||||
# Currently all Nix paths have the same filesystem root, but this could change in the future.
|
||||
# See also ../path/README.md
|
||||
else if ! fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then
|
||||
throw ''
|
||||
lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` ("${toString root}"):
|
||||
`root`: root "${toString rootFilesystemRoot}"
|
||||
`fileset`: root "${toString filesetFilesystemRoot}"
|
||||
Different roots are not supported.''
|
||||
else if ! pathExists root then
|
||||
throw ''
|
||||
lib.fileset.toSource: `root` (${toString root}) does not exist.''
|
||||
else if pathType root != "directory" then
|
||||
throw ''
|
||||
lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions:
|
||||
- If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function.
|
||||
- If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as ${toString (dirOf root)}, and set `fileset` to the file path.''
|
||||
else if ! fileset._internalIsEmptyWithoutBase && ! hasPrefix root fileset._internalBase then
|
||||
throw ''
|
||||
lib.fileset.toSource: `fileset` could contain files in ${toString fileset._internalBase}, which is not under the `root` (${toString root}). Potential solutions:
|
||||
- Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path.
|
||||
- Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.''
|
||||
else
|
||||
builtins.seq sourceFilter
|
||||
cleanSourceWith {
|
||||
name = "source";
|
||||
src = root;
|
||||
filter = sourceFilter;
|
||||
};
|
||||
|
||||
/**
|
||||
The file set containing all files that are in either of two given file sets.
|
||||
This is the same as [`unions`](#function-library-lib.fileset.unions),
|
||||
but takes just two file sets instead of a list.
|
||||
See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)).
|
||||
The given file sets are evaluated as lazily as possible,
|
||||
with the first argument being evaluated first if needed.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Create a file set containing the file `Makefile`
|
||||
# and all files recursively in the `src` directory
|
||||
union ./Makefile ./src
|
||||
# Create a file set containing the file `Makefile`
|
||||
# and the LICENSE file from the parent directory
|
||||
union ./Makefile ../LICENSE
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
union :: FileSet -> FileSet -> FileSet
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [fileset1] The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
- [fileset2] The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
|
||||
*/
|
||||
union =
|
||||
# The first file set.
|
||||
# This argument can also be a path,
|
||||
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
fileset1:
|
||||
# The second file set.
|
||||
# This argument can also be a path,
|
||||
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
fileset2:
|
||||
_unionMany
|
||||
(_coerceMany "lib.fileset.union" [
|
||||
{
|
||||
context = "first argument";
|
||||
value = fileset1;
|
||||
}
|
||||
{
|
||||
context = "second argument";
|
||||
value = fileset2;
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
The file set containing all files that are in any of the given file sets.
|
||||
This is the same as [`union`](#function-library-lib.fileset.unions),
|
||||
but takes a list of file sets instead of just two.
|
||||
See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)).
|
||||
The given file sets are evaluated as lazily as possible,
|
||||
with earlier elements being evaluated first if needed.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Create a file set containing selected files
|
||||
unions [
|
||||
# Include the single file `Makefile` in the current directory
|
||||
# This errors if the file doesn't exist
|
||||
./Makefile
|
||||
# Recursively include all files in the `src/code` directory
|
||||
# If this directory is empty this has no effect
|
||||
./src/code
|
||||
# Include the files `run.sh` and `unit.c` from the `tests` directory
|
||||
./tests/run.sh
|
||||
./tests/unit.c
|
||||
# Include the `LICENSE` file from the parent directory
|
||||
../LICENSE
|
||||
]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
unions :: [ FileSet ] -> FileSet
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [filesets] A list of file sets. The elements can also be paths, which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
|
||||
|
||||
*/
|
||||
unions =
|
||||
# A list of file sets.
|
||||
# The elements can also be paths,
|
||||
# which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
|
||||
filesets:
|
||||
if ! isList filesets then
|
||||
throw "lib.fileset.unions: Expected argument to be a list, but got a ${typeOf filesets}."
|
||||
else
|
||||
pipe filesets [
|
||||
# Annotate the elements with context, used by _coerceMany for better errors
|
||||
(imap0 (i: el: {
|
||||
context = "element ${toString i}";
|
||||
value = el;
|
||||
}))
|
||||
(_coerceMany "lib.fileset.unions")
|
||||
_unionMany
|
||||
];
|
||||
|
||||
/**
|
||||
The file set containing all files that are in both of two given file sets.
|
||||
See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)).
|
||||
The given file sets are evaluated as lazily as possible,
|
||||
with the first argument being evaluated first if needed.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Limit the selected files to the ones in ./., so only ./src and ./Makefile
|
||||
intersection ./. (unions [ ../LICENSE ./src ./Makefile ])
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
intersection :: FileSet -> FileSet -> FileSet
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [fileset1] The first file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
- [fileset2] The second file set. This argument can also be a path, which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
|
||||
*/
|
||||
intersection =
|
||||
# The first file set.
|
||||
# This argument can also be a path,
|
||||
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
fileset1:
|
||||
# The second file set.
|
||||
# This argument can also be a path,
|
||||
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
fileset2:
|
||||
let
|
||||
filesets = _coerceMany "lib.fileset.intersection" [
|
||||
{
|
||||
context = "first argument";
|
||||
value = fileset1;
|
||||
}
|
||||
{
|
||||
context = "second argument";
|
||||
value = fileset2;
|
||||
}
|
||||
];
|
||||
in
|
||||
_intersection
|
||||
(elemAt filesets 0)
|
||||
(elemAt filesets 1);
|
||||
|
||||
/**
|
||||
Incrementally evaluate and trace a file set in a pretty way.
|
||||
This function is only intended for debugging purposes.
|
||||
The exact tracing format is unspecified and may change.
|
||||
This function takes a final argument to return.
|
||||
In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns
|
||||
the given file set argument.
|
||||
This variant is useful for tracing file sets in the Nix repl.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
trace (unions [ ./Makefile ./src ./tests/run.sh ]) null
|
||||
=>
|
||||
trace: /home/user/src/myProject
|
||||
trace: - Makefile (regular)
|
||||
trace: - src (all files in directory)
|
||||
trace: - tests
|
||||
trace: - run.sh (regular)
|
||||
null
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
trace :: FileSet -> Any -> Any
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [fileset] The file set to trace.
|
||||
|
||||
This argument can also be a path,
|
||||
which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
|
||||
*/
|
||||
trace =
|
||||
/**
|
||||
The file set to trace.
|
||||
This argument can also be a path,
|
||||
which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
*/
|
||||
fileset:
|
||||
let
|
||||
# "fileset" would be a better name, but that would clash with the argument name,
|
||||
# and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
|
||||
actualFileset = _coerce "lib.fileset.trace: argument" fileset;
|
||||
in
|
||||
seq
|
||||
(_printFileset actualFileset)
|
||||
(x: x);
|
||||
|
||||
/**
|
||||
Incrementally evaluate and trace a file set in a pretty way.
|
||||
This function is only intended for debugging purposes.
|
||||
The exact tracing format is unspecified and may change.
|
||||
This function returns the given file set.
|
||||
In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return.
|
||||
This variant is useful for tracing file sets passed as arguments to other functions.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
toSource {
|
||||
root = ./.;
|
||||
fileset = traceVal (unions [
|
||||
./Makefile
|
||||
./src
|
||||
./tests/run.sh
|
||||
]);
|
||||
}
|
||||
=>
|
||||
trace: /home/user/src/myProject
|
||||
trace: - Makefile (regular)
|
||||
trace: - src (all files in directory)
|
||||
trace: - tests
|
||||
trace: - run.sh (regular)
|
||||
"/nix/store/...-source"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
traceVal :: FileSet -> FileSet
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [fileset] The file set to trace and return.
|
||||
|
||||
This argument can also be a path,
|
||||
which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
|
||||
*/
|
||||
traceVal =
|
||||
/**
|
||||
The file set to trace and return.
|
||||
This argument can also be a path,
|
||||
which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
|
||||
*/
|
||||
fileset:
|
||||
let
|
||||
# "fileset" would be a better name, but that would clash with the argument name,
|
||||
# and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
|
||||
actualFileset = _coerce "lib.fileset.traceVal: argument" fileset;
|
||||
in
|
||||
seq
|
||||
(_printFileset actualFileset)
|
||||
# We could also return the original fileset argument here,
|
||||
# but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
|
||||
actualFileset;
|
||||
}
|
659
pesto/test_data/assets/fileset/internal.nix
Normal file
659
pesto/test_data/assets/fileset/internal.nix
Normal file
@ -0,0 +1,659 @@
|
||||
{ lib ? import ../. }:
|
||||
let
|
||||
|
||||
inherit (builtins)
|
||||
isAttrs
|
||||
isPath
|
||||
isString
|
||||
pathExists
|
||||
readDir
|
||||
seq
|
||||
split
|
||||
trace
|
||||
typeOf
|
||||
;
|
||||
|
||||
inherit (lib.attrsets)
|
||||
attrNames
|
||||
attrValues
|
||||
mapAttrs
|
||||
setAttrByPath
|
||||
zipAttrsWith
|
||||
;
|
||||
|
||||
inherit (lib.filesystem)
|
||||
pathType
|
||||
;
|
||||
|
||||
inherit (lib.lists)
|
||||
all
|
||||
commonPrefix
|
||||
drop
|
||||
elemAt
|
||||
filter
|
||||
findFirst
|
||||
findFirstIndex
|
||||
foldl'
|
||||
head
|
||||
length
|
||||
sublist
|
||||
tail
|
||||
;
|
||||
|
||||
inherit (lib.path)
|
||||
append
|
||||
splitRoot
|
||||
;
|
||||
|
||||
inherit (lib.path.subpath)
|
||||
components
|
||||
join
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
isStringLike
|
||||
concatStringsSep
|
||||
substring
|
||||
stringLength
|
||||
;
|
||||
|
||||
in
|
||||
# Rare case of justified usage of rec:
|
||||
# - This file is internal, so the return value doesn't matter, no need to make things overridable
|
||||
# - The functions depend on each other
|
||||
# - We want to expose all of these functions for easy testing
|
||||
rec {
|
||||
|
||||
# If you change the internal representation, make sure to:
|
||||
# - Increment this version
|
||||
# - Add an additional migration function below
|
||||
# - Update the description of the internal representation in ./README.md
|
||||
_currentVersion = 3;
|
||||
|
||||
# Migrations between versions. The 0th element converts from v0 to v1, and so on
|
||||
migrations = [
|
||||
# Convert v0 into v1: Add the _internalBase{Root,Components} attributes
|
||||
(
|
||||
filesetV0:
|
||||
let
|
||||
parts = splitRoot filesetV0._internalBase;
|
||||
in
|
||||
filesetV0 // {
|
||||
_internalVersion = 1;
|
||||
_internalBaseRoot = parts.root;
|
||||
_internalBaseComponents = components parts.subpath;
|
||||
}
|
||||
)
|
||||
|
||||
# Convert v1 into v2: filesetTree's can now also omit attributes to signal paths not being included
|
||||
(
|
||||
filesetV1:
|
||||
# This change is backwards compatible (but not forwards compatible, so we still need a new version)
|
||||
filesetV1 // {
|
||||
_internalVersion = 2;
|
||||
}
|
||||
)
|
||||
|
||||
# Convert v2 into v3: filesetTree's now have a representation for an empty file set without a base path
|
||||
(
|
||||
filesetV2:
|
||||
filesetV2 // {
|
||||
# All v1 file sets are not the new empty file set
|
||||
_internalIsEmptyWithoutBase = false;
|
||||
_internalVersion = 3;
|
||||
}
|
||||
)
|
||||
];
|
||||
|
||||
_noEvalMessage = ''
|
||||
lib.fileset: Directly evaluating a file set is not supported.
|
||||
To turn it into a usable source, use `lib.fileset.toSource`.
|
||||
To pretty-print the contents, use `lib.fileset.trace` or `lib.fileset.traceVal`.'';
|
||||
|
||||
# The empty file set without a base path
|
||||
_emptyWithoutBase = {
|
||||
_type = "fileset";
|
||||
|
||||
_internalVersion = _currentVersion;
|
||||
|
||||
# The one and only!
|
||||
_internalIsEmptyWithoutBase = true;
|
||||
|
||||
# Due to alphabetical ordering, this is evaluated last,
|
||||
# which makes the nix repl output nicer than if it would be ordered first.
|
||||
# It also allows evaluating it strictly up to this error, which could be useful
|
||||
_noEval = throw _noEvalMessage;
|
||||
};
|
||||
|
||||
# Create a fileset, see ./README.md#fileset
|
||||
# Type: path -> filesetTree -> fileset
|
||||
_create = base: tree:
|
||||
let
|
||||
# Decompose the base into its components
|
||||
# See ../path/README.md for why we're not just using `toString`
|
||||
parts = splitRoot base;
|
||||
in
|
||||
{
|
||||
_type = "fileset";
|
||||
|
||||
_internalVersion = _currentVersion;
|
||||
|
||||
_internalIsEmptyWithoutBase = false;
|
||||
_internalBase = base;
|
||||
_internalBaseRoot = parts.root;
|
||||
_internalBaseComponents = components parts.subpath;
|
||||
_internalTree = tree;
|
||||
|
||||
# Due to alphabetical ordering, this is evaluated last,
|
||||
# which makes the nix repl output nicer than if it would be ordered first.
|
||||
# It also allows evaluating it strictly up to this error, which could be useful
|
||||
_noEval = throw _noEvalMessage;
|
||||
};
|
||||
|
||||
# Coerce a value to a fileset, erroring when the value cannot be coerced.
|
||||
# The string gives the context for error messages.
|
||||
# Type: String -> (fileset | Path) -> fileset
|
||||
_coerce = context: value:
|
||||
if value._type or "" == "fileset" then
|
||||
if value._internalVersion > _currentVersion then
|
||||
throw ''
|
||||
${context} is a file set created from a future version of the file set library with a different internal representation:
|
||||
- Internal version of the file set: ${toString value._internalVersion}
|
||||
- Internal version of the library: ${toString _currentVersion}
|
||||
Make sure to update your Nixpkgs to have a newer version of `lib.fileset`.''
|
||||
else if value._internalVersion < _currentVersion then
|
||||
let
|
||||
# Get all the migration functions necessary to convert from the old to the current version
|
||||
migrationsToApply = sublist value._internalVersion (_currentVersion - value._internalVersion) migrations;
|
||||
in
|
||||
foldl' (value: migration: migration value) value migrationsToApply
|
||||
else
|
||||
value
|
||||
else if ! isPath value then
|
||||
if isStringLike value then
|
||||
throw ''
|
||||
${context} ("${toString value}") is a string-like value, but it should be a file set or a path instead.
|
||||
Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
|
||||
else
|
||||
throw ''
|
||||
${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
|
||||
else if ! pathExists value then
|
||||
throw ''
|
||||
${context} (${toString value}) does not exist.''
|
||||
else
|
||||
_singleton value;
|
||||
|
||||
# Coerce many values to filesets, erroring when any value cannot be coerced,
|
||||
# or if the filesystem root of the values doesn't match.
|
||||
# Type: String -> [ { context :: String, value :: fileset | Path } ] -> [ fileset ]
|
||||
_coerceMany = functionContext: list:
|
||||
let
|
||||
filesets = map ({ context, value }:
|
||||
_coerce "${functionContext}: ${context}" value
|
||||
) list;
|
||||
|
||||
# Find the first value with a base, there may be none!
|
||||
firstWithBase = findFirst (fileset: ! fileset._internalIsEmptyWithoutBase) null filesets;
|
||||
# This value is only accessed if first != null
|
||||
firstBaseRoot = firstWithBase._internalBaseRoot;
|
||||
|
||||
# Finds the first element with a filesystem root different than the first element, if any
|
||||
differentIndex = findFirstIndex (fileset:
|
||||
# The empty value without a base doesn't have a base path
|
||||
! fileset._internalIsEmptyWithoutBase
|
||||
&& firstBaseRoot != fileset._internalBaseRoot
|
||||
) null filesets;
|
||||
in
|
||||
# Only evaluates `differentIndex` if there are any elements with a base
|
||||
if firstWithBase != null && differentIndex != null then
|
||||
throw ''
|
||||
${functionContext}: Filesystem roots are not the same:
|
||||
${(head list).context}: root "${toString firstBaseRoot}"
|
||||
${(elemAt list differentIndex).context}: root "${toString (elemAt filesets differentIndex)._internalBaseRoot}"
|
||||
Different roots are not supported.''
|
||||
else
|
||||
filesets;
|
||||
|
||||
# Create a file set from a path.
|
||||
# Type: Path -> fileset
|
||||
_singleton = path:
|
||||
let
|
||||
type = pathType path;
|
||||
in
|
||||
if type == "directory" then
|
||||
_create path type
|
||||
else
|
||||
# This turns a file path ./default.nix into a fileset with
|
||||
# - _internalBase: ./.
|
||||
# - _internalTree: {
|
||||
# "default.nix" = <type>;
|
||||
# }
|
||||
# See ./README.md#single-files
|
||||
_create (dirOf path)
|
||||
{
|
||||
${baseNameOf path} = type;
|
||||
};
|
||||
|
||||
# Expand a directory representation to an equivalent one in attribute set form.
|
||||
# All directory entries are included in the result.
|
||||
# Type: Path -> filesetTree -> { <name> = filesetTree; }
|
||||
_directoryEntries = path: value:
|
||||
if value == "directory" then
|
||||
readDir path
|
||||
else
|
||||
# Set all entries not present to null
|
||||
mapAttrs (name: value: null) (readDir path)
|
||||
// value;
|
||||
|
||||
/**
|
||||
A normalisation of a filesetTree suitable filtering with `builtins.path`:
|
||||
- Replace all directories that have no files with `null`.
|
||||
This removes directories that would be empty
|
||||
- Replace all directories with all files with `"directory"`.
|
||||
This speeds up the source filter function
|
||||
Note that this function is strict, it evaluates the entire tree
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
Path -> filesetTree -> filesetTree
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path]
|
||||
- [tree]
|
||||
|
||||
*/
|
||||
_normaliseTreeFilter = path: tree:
|
||||
if tree == "directory" || isAttrs tree then
|
||||
let
|
||||
entries = _directoryEntries path tree;
|
||||
normalisedSubtrees = mapAttrs (name: _normaliseTreeFilter (path + "/${name}")) entries;
|
||||
subtreeValues = attrValues normalisedSubtrees;
|
||||
in
|
||||
# This triggers either when all files in a directory are filtered out
|
||||
# Or when the directory doesn't contain any files at all
|
||||
if all isNull subtreeValues then
|
||||
null
|
||||
# Triggers when we have the same as a `readDir path`, so we can turn it back into an equivalent "directory".
|
||||
else if all isString subtreeValues then
|
||||
"directory"
|
||||
else
|
||||
normalisedSubtrees
|
||||
else
|
||||
tree;
|
||||
|
||||
/**
|
||||
A minimal normalisation of a filesetTree, intended for pretty-printing:
|
||||
- If all children of a path are recursively included or empty directories, the path itself is also recursively included
|
||||
- If all children of a path are fully excluded or empty directories, the path itself is an empty directory
|
||||
- Other empty directories are represented with the special "emptyDir" string
|
||||
While these could be replaced with `null`, that would take another mapAttrs
|
||||
Note that this function is partially lazy.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
Path -> filesetTree -> filesetTree (with "emptyDir"'s)
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path]
|
||||
- [tree]
|
||||
|
||||
*/
|
||||
_normaliseTreeMinimal = path: tree:
|
||||
if tree == "directory" || isAttrs tree then
|
||||
let
|
||||
entries = _directoryEntries path tree;
|
||||
normalisedSubtrees = mapAttrs (name: _normaliseTreeMinimal (path + "/${name}")) entries;
|
||||
subtreeValues = attrValues normalisedSubtrees;
|
||||
in
|
||||
# If there are no entries, or all entries are empty directories, return "emptyDir".
|
||||
# After this branch we know that there's at least one file
|
||||
if all (value: value == "emptyDir") subtreeValues then
|
||||
"emptyDir"
|
||||
|
||||
# If all subtrees are fully included or empty directories
|
||||
# (both of which are coincidentally represented as strings), return "directory".
|
||||
# This takes advantage of the fact that empty directories can be represented as included directories.
|
||||
# Note that the tree == "directory" check allows avoiding recursion
|
||||
else if tree == "directory" || all (value: isString value) subtreeValues then
|
||||
"directory"
|
||||
|
||||
# If all subtrees are fully excluded or empty directories, return null.
|
||||
# This takes advantage of the fact that empty directories can be represented as excluded directories
|
||||
else if all (value: isNull value || value == "emptyDir") subtreeValues then
|
||||
null
|
||||
|
||||
# Mix of included and excluded entries
|
||||
else
|
||||
normalisedSubtrees
|
||||
else
|
||||
tree;
|
||||
|
||||
# Trace a filesetTree in a pretty way when the resulting value is evaluated.
|
||||
# This can handle both normal filesetTree's, and ones returned from _normaliseTreeMinimal
|
||||
# Type: Path -> filesetTree (with "emptyDir"'s) -> Null
|
||||
_printMinimalTree = base: tree:
|
||||
let
|
||||
treeSuffix = tree:
|
||||
if isAttrs tree then
|
||||
""
|
||||
else if tree == "directory" then
|
||||
" (all files in directory)"
|
||||
else
|
||||
# This does "leak" the file type strings of the internal representation,
|
||||
# but this is the main reason these file type strings even are in the representation!
|
||||
# TODO: Consider removing that information from the internal representation for performance.
|
||||
# The file types can still be printed by querying them only during tracing
|
||||
" (${tree})";
|
||||
|
||||
# Only for attribute set trees
|
||||
traceTreeAttrs = prevLine: indent: tree:
|
||||
foldl' (prevLine: name:
|
||||
let
|
||||
subtree = tree.${name};
|
||||
|
||||
# Evaluating this prints the line for this subtree
|
||||
thisLine =
|
||||
trace "${indent}- ${name}${treeSuffix subtree}" prevLine;
|
||||
in
|
||||
if subtree == null || subtree == "emptyDir" then
|
||||
# Don't print anything at all if this subtree is empty
|
||||
prevLine
|
||||
else if isAttrs subtree then
|
||||
# A directory with explicit entries
|
||||
# Do print this node, but also recurse
|
||||
traceTreeAttrs thisLine "${indent} " subtree
|
||||
else
|
||||
# Either a file, or a recursively included directory
|
||||
# Do print this node but no further recursion needed
|
||||
thisLine
|
||||
) prevLine (attrNames tree);
|
||||
|
||||
# Evaluating this will print the first line
|
||||
firstLine =
|
||||
if tree == null || tree == "emptyDir" then
|
||||
trace "(empty)" null
|
||||
else
|
||||
trace "${toString base}${treeSuffix tree}" null;
|
||||
in
|
||||
if isAttrs tree then
|
||||
traceTreeAttrs firstLine "" tree
|
||||
else
|
||||
firstLine;
|
||||
|
||||
# Pretty-print a file set in a pretty way when the resulting value is evaluated
|
||||
# Type: fileset -> Null
|
||||
_printFileset = fileset:
|
||||
if fileset._internalIsEmptyWithoutBase then
|
||||
trace "(empty)" null
|
||||
else
|
||||
_printMinimalTree fileset._internalBase
|
||||
(_normaliseTreeMinimal fileset._internalBase fileset._internalTree);
|
||||
|
||||
# Turn a fileset into a source filter function suitable for `builtins.path`
|
||||
# Only directories recursively containing at least one files are recursed into
|
||||
# Type: Path -> fileset -> (String -> String -> Bool)
|
||||
_toSourceFilter = fileset:
|
||||
let
|
||||
# Simplify the tree, necessary to make sure all empty directories are null
|
||||
# which has the effect that they aren't included in the result
|
||||
tree = _normaliseTreeFilter fileset._internalBase fileset._internalTree;
|
||||
|
||||
# The base path as a string with a single trailing slash
|
||||
baseString =
|
||||
if fileset._internalBaseComponents == [] then
|
||||
# Need to handle the filesystem root specially
|
||||
"/"
|
||||
else
|
||||
"/" + concatStringsSep "/" fileset._internalBaseComponents + "/";
|
||||
|
||||
baseLength = stringLength baseString;
|
||||
|
||||
# Check whether a list of path components under the base path exists in the tree.
|
||||
# This function is called often, so it should be fast.
|
||||
# Type: [ String ] -> Bool
|
||||
inTree = components:
|
||||
let
|
||||
recurse = index: localTree:
|
||||
if isAttrs localTree then
|
||||
# We have an attribute set, meaning this is a directory with at least one file
|
||||
if index >= length components then
|
||||
# The path may have no more components though, meaning the filter is running on the directory itself,
|
||||
# so we always include it, again because there's at least one file in it.
|
||||
true
|
||||
else
|
||||
# If we do have more components, the filter runs on some entry inside this directory, so we need to recurse
|
||||
# We do +2 because builtins.split is an interleaved list of the inbetweens and the matches
|
||||
recurse (index + 2) localTree.${elemAt components index}
|
||||
else
|
||||
# If it's not an attribute set it can only be either null (in which case it's not included)
|
||||
# or a string ("directory" or "regular", etc.) in which case it's included
|
||||
localTree != null;
|
||||
in recurse 0 tree;
|
||||
|
||||
# Filter suited when there's no files
|
||||
empty = _: _: false;
|
||||
|
||||
# Filter suited when there's some files
|
||||
# This can't be used for when there's no files, because the base directory is always included
|
||||
nonEmpty =
|
||||
path: _:
|
||||
let
|
||||
# Add a slash to the path string, turning "/foo" to "/foo/",
|
||||
# making sure to not have any false prefix matches below.
|
||||
# Note that this would produce "//" for "/",
|
||||
# but builtins.path doesn't call the filter function on the `path` argument itself,
|
||||
# meaning this function can never receive "/" as an argument
|
||||
pathSlash = path + "/";
|
||||
in
|
||||
# Same as `hasPrefix pathSlash baseString`, but more efficient.
|
||||
# With base /foo/bar we need to include /foo:
|
||||
# hasPrefix "/foo/" "/foo/bar/"
|
||||
if substring 0 (stringLength pathSlash) baseString == pathSlash then
|
||||
true
|
||||
# Same as `! hasPrefix baseString pathSlash`, but more efficient.
|
||||
# With base /foo/bar we need to exclude /baz
|
||||
# ! hasPrefix "/baz/" "/foo/bar/"
|
||||
else if substring 0 baseLength pathSlash != baseString then
|
||||
false
|
||||
else
|
||||
# Same as `removePrefix baseString path`, but more efficient.
|
||||
# From the above code we know that hasPrefix baseString pathSlash holds, so this is safe.
|
||||
# We don't use pathSlash here because we only needed the trailing slash for the prefix matching.
|
||||
# With base /foo and path /foo/bar/baz this gives
|
||||
# inTree (split "/" (removePrefix "/foo/" "/foo/bar/baz"))
|
||||
# == inTree (split "/" "bar/baz")
|
||||
# == inTree [ "bar" "baz" ]
|
||||
inTree (split "/" (substring baseLength (-1) path));
|
||||
in
|
||||
# Special case because the code below assumes that the _internalBase is always included in the result
|
||||
# which shouldn't be done when we have no files at all in the base
|
||||
# This also forces the tree before returning the filter, leads to earlier error messages
|
||||
if fileset._internalIsEmptyWithoutBase || tree == null then
|
||||
empty
|
||||
else
|
||||
nonEmpty;
|
||||
|
||||
# Transforms the filesetTree of a file set to a shorter base path, e.g.
|
||||
# _shortenTreeBase [ "foo" ] (_create /foo/bar null)
|
||||
# => { bar = null; }
|
||||
_shortenTreeBase = targetBaseComponents: fileset:
|
||||
let
|
||||
recurse = index:
|
||||
# If we haven't reached the required depth yet
|
||||
if index < length fileset._internalBaseComponents then
|
||||
# Create an attribute set and recurse as the value, this can be lazily evaluated this way
|
||||
{ ${elemAt fileset._internalBaseComponents index} = recurse (index + 1); }
|
||||
else
|
||||
# Otherwise we reached the appropriate depth, here's the original tree
|
||||
fileset._internalTree;
|
||||
in
|
||||
recurse (length targetBaseComponents);
|
||||
|
||||
# Transforms the filesetTree of a file set to a longer base path, e.g.
|
||||
# _lengthenTreeBase [ "foo" "bar" ] (_create /foo { bar.baz = "regular"; })
|
||||
# => { baz = "regular"; }
|
||||
_lengthenTreeBase = targetBaseComponents: fileset:
|
||||
let
|
||||
recurse = index: tree:
|
||||
# If the filesetTree is an attribute set and we haven't reached the required depth yet
|
||||
if isAttrs tree && index < length targetBaseComponents then
|
||||
# Recurse with the tree under the right component (which might not exist)
|
||||
recurse (index + 1) (tree.${elemAt targetBaseComponents index} or null)
|
||||
else
|
||||
# For all values here we can just return the tree itself:
|
||||
# tree == null -> the result is also null, everything is excluded
|
||||
# tree == "directory" -> the result is also "directory",
|
||||
# because the base path is always a directory and everything is included
|
||||
# isAttrs tree -> the result is `tree`
|
||||
# because we don't need to recurse any more since `index == length longestBaseComponents`
|
||||
tree;
|
||||
in
|
||||
recurse (length fileset._internalBaseComponents) fileset._internalTree;
|
||||
|
||||
# Computes the union of a list of filesets.
|
||||
# The filesets must already be coerced and validated to be in the same filesystem root
|
||||
# Type: [ Fileset ] -> Fileset
|
||||
_unionMany = filesets:
|
||||
let
|
||||
# All filesets that have a base, aka not the ones that are the empty value without a base
|
||||
filesetsWithBase = filter (fileset: ! fileset._internalIsEmptyWithoutBase) filesets;
|
||||
|
||||
# The first fileset that has a base.
|
||||
# This value is only accessed if there are at all.
|
||||
firstWithBase = head filesetsWithBase;
|
||||
|
||||
# To be able to union filesetTree's together, they need to have the same base path.
|
||||
# Base paths can be unioned by taking their common prefix,
|
||||
# e.g. such that `union /foo/bar /foo/baz` has the base path `/foo`
|
||||
|
||||
# A list of path components common to all base paths.
|
||||
# Note that commonPrefix can only be fully evaluated,
|
||||
# so this cannot cause a stack overflow due to a build-up of unevaluated thunks.
|
||||
commonBaseComponents = foldl'
|
||||
(components: el: commonPrefix components el._internalBaseComponents)
|
||||
firstWithBase._internalBaseComponents
|
||||
# We could also not do the `tail` here to avoid a list allocation,
|
||||
# but then we'd have to pay for a potentially expensive
|
||||
# but unnecessary `commonPrefix` call
|
||||
(tail filesetsWithBase);
|
||||
|
||||
# The common base path assembled from a filesystem root and the common components
|
||||
commonBase = append firstWithBase._internalBaseRoot (join commonBaseComponents);
|
||||
|
||||
# A list of filesetTree's that all have the same base path
|
||||
# This is achieved by nesting the trees into the components they have over the common base path
|
||||
# E.g. `union /foo/bar /foo/baz` has the base path /foo
|
||||
# So the tree under `/foo/bar` gets nested under `{ bar = ...; ... }`,
|
||||
# while the tree under `/foo/baz` gets nested under `{ baz = ...; ... }`
|
||||
# Therefore allowing combined operations over them.
|
||||
trees = map (_shortenTreeBase commonBaseComponents) filesetsWithBase;
|
||||
|
||||
# Folds all trees together into a single one using _unionTree
|
||||
# We do not use a fold here because it would cause a thunk build-up
|
||||
# which could cause a stack overflow for a large number of trees
|
||||
resultTree = _unionTrees trees;
|
||||
in
|
||||
# If there's no values with a base, we have no files
|
||||
if filesetsWithBase == [ ] then
|
||||
_emptyWithoutBase
|
||||
else
|
||||
_create commonBase resultTree;
|
||||
|
||||
# The union of multiple filesetTree's with the same base path.
|
||||
# Later elements are only evaluated if necessary.
|
||||
# Type: [ filesetTree ] -> filesetTree
|
||||
_unionTrees = trees:
|
||||
let
|
||||
stringIndex = findFirstIndex isString null trees;
|
||||
withoutNull = filter (tree: tree != null) trees;
|
||||
in
|
||||
if stringIndex != null then
|
||||
# If there's a string, it's always a fully included tree (dir or file),
|
||||
# no need to look at other elements
|
||||
elemAt trees stringIndex
|
||||
else if withoutNull == [ ] then
|
||||
# If all trees are null, then the resulting tree is also null
|
||||
null
|
||||
else
|
||||
# The non-null elements have to be attribute sets representing partial trees
|
||||
# We need to recurse into those
|
||||
zipAttrsWith (name: _unionTrees) withoutNull;
|
||||
|
||||
# Computes the intersection of a list of filesets.
|
||||
# The filesets must already be coerced and validated to be in the same filesystem root
|
||||
# Type: Fileset -> Fileset -> Fileset
|
||||
_intersection = fileset1: fileset2:
|
||||
let
|
||||
# The common base components prefix, e.g.
|
||||
# (/foo/bar, /foo/bar/baz) -> /foo/bar
|
||||
# (/foo/bar, /foo/baz) -> /foo
|
||||
commonBaseComponentsLength =
|
||||
# TODO: Have a `lib.lists.commonPrefixLength` function such that we don't need the list allocation from commonPrefix here
|
||||
length (
|
||||
commonPrefix
|
||||
fileset1._internalBaseComponents
|
||||
fileset2._internalBaseComponents
|
||||
);
|
||||
|
||||
# To be able to intersect filesetTree's together, they need to have the same base path.
|
||||
# Base paths can be intersected by taking the longest one (if any)
|
||||
|
||||
# The fileset with the longest base, if any, e.g.
|
||||
# (/foo/bar, /foo/bar/baz) -> /foo/bar/baz
|
||||
# (/foo/bar, /foo/baz) -> null
|
||||
longestBaseFileset =
|
||||
if commonBaseComponentsLength == length fileset1._internalBaseComponents then
|
||||
# The common prefix is the same as the first path, so the second path is equal or longer
|
||||
fileset2
|
||||
else if commonBaseComponentsLength == length fileset2._internalBaseComponents then
|
||||
# The common prefix is the same as the second path, so the first path is longer
|
||||
fileset1
|
||||
else
|
||||
# The common prefix is neither the first nor the second path
|
||||
# This means there's no overlap between the two sets
|
||||
null;
|
||||
|
||||
# Whether the result should be the empty value without a base
|
||||
resultIsEmptyWithoutBase =
|
||||
# If either fileset is the empty fileset without a base, the intersection is too
|
||||
fileset1._internalIsEmptyWithoutBase
|
||||
|| fileset2._internalIsEmptyWithoutBase
|
||||
# If there is no overlap between the base paths
|
||||
|| longestBaseFileset == null;
|
||||
|
||||
# Lengthen each fileset's tree to the longest base prefix
|
||||
tree1 = _lengthenTreeBase longestBaseFileset._internalBaseComponents fileset1;
|
||||
tree2 = _lengthenTreeBase longestBaseFileset._internalBaseComponents fileset2;
|
||||
|
||||
# With two filesetTree's with the same base, we can compute their intersection
|
||||
resultTree = _intersectTree tree1 tree2;
|
||||
in
|
||||
if resultIsEmptyWithoutBase then
|
||||
_emptyWithoutBase
|
||||
else
|
||||
_create longestBaseFileset._internalBase resultTree;
|
||||
|
||||
# The intersection of two filesetTree's with the same base path
|
||||
# The second element is only evaluated as much as necessary.
|
||||
# Type: filesetTree -> filesetTree -> filesetTree
|
||||
_intersectTree = lhs: rhs:
|
||||
if isAttrs lhs && isAttrs rhs then
|
||||
# Both sides are attribute sets, we can recurse for the attributes existing on both sides
|
||||
mapAttrs
|
||||
(name: _intersectTree lhs.${name})
|
||||
(builtins.intersectAttrs lhs rhs)
|
||||
else if lhs == null || isString rhs then
|
||||
# If the lhs is null, the result should also be null
|
||||
# And if the rhs is the identity element
|
||||
# (a string, aka it includes everything), then it's also the lhs
|
||||
lhs
|
||||
else
|
||||
# In all other cases it's the rhs
|
||||
rhs;
|
||||
}
|
26
pesto/test_data/assets/fileset/mock-splitRoot.nix
Normal file
26
pesto/test_data/assets/fileset/mock-splitRoot.nix
Normal file
@ -0,0 +1,26 @@
|
||||
# This overlay implements mocking of the lib.path.splitRoot function
|
||||
# It pretends that the last component named "mock-root" is the root:
|
||||
#
|
||||
# splitRoot /foo/mock-root/bar/mock-root/baz
|
||||
# => {
|
||||
# root = /foo/mock-root/bar/mock-root;
|
||||
# subpath = "./baz";
|
||||
# }
|
||||
self: super: {
|
||||
path = super.path // {
|
||||
splitRoot = path:
|
||||
let
|
||||
parts = super.path.splitRoot path;
|
||||
components = self.path.subpath.components parts.subpath;
|
||||
count = self.length components;
|
||||
rootIndex = count - self.lists.findFirstIndex
|
||||
(component: component == "mock-root")
|
||||
(self.length components)
|
||||
(self.reverseList components);
|
||||
root = self.path.append parts.root (self.path.subpath.join (self.take rootIndex components));
|
||||
subpath = self.path.subpath.join (self.drop rootIndex components);
|
||||
in {
|
||||
inherit root subpath;
|
||||
};
|
||||
};
|
||||
}
|
206
pesto/test_data/assets/filesystem.nix
Normal file
206
pesto/test_data/assets/filesystem.nix
Normal file
@ -0,0 +1,206 @@
|
||||
# Functions for querying information about the filesystem
|
||||
# without copying any files to the Nix store.
|
||||
{ lib }:
|
||||
|
||||
# Tested in lib/tests/filesystem.sh
|
||||
let
|
||||
inherit (builtins)
|
||||
readDir
|
||||
pathExists
|
||||
;
|
||||
|
||||
inherit (lib.filesystem)
|
||||
pathType
|
||||
;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
/**
|
||||
The type of a path. The path needs to exist and be accessible.
|
||||
The result is either "directory" for a directory, "regular" for a regular file, "symlink" for a symlink, or "unknown" for anything else.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
pathType /.
|
||||
=> "directory"
|
||||
pathType /some/file.nix
|
||||
=> "regular"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
pathType :: Path -> String
|
||||
```
|
||||
*/
|
||||
pathType =
|
||||
builtins.readFileType or
|
||||
# Nix <2.14 compatibility shim
|
||||
(path:
|
||||
if ! pathExists path
|
||||
# Fail irrecoverably to mimic the historic behavior of this function and
|
||||
# the new builtins.readFileType
|
||||
then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
|
||||
# The filesystem root is the only path where `dirOf / == /` and
|
||||
# `baseNameOf /` is not valid. We can detect this and directly return
|
||||
# "directory", since we know the filesystem root can't be anything else.
|
||||
else if dirOf path == path
|
||||
then "directory"
|
||||
else (readDir (dirOf path)).${baseNameOf path}
|
||||
);
|
||||
|
||||
/**
|
||||
Whether a path exists and is a directory.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
pathIsDirectory /.
|
||||
=> true
|
||||
pathIsDirectory /this/does/not/exist
|
||||
=> false
|
||||
pathIsDirectory /some/file.nix
|
||||
=> false
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
pathIsDirectory :: Path -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path]
|
||||
|
||||
*/
|
||||
pathIsDirectory = path:
|
||||
pathExists path && pathType path == "directory";
|
||||
|
||||
/**
|
||||
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
pathIsRegularFile /.
|
||||
=> false
|
||||
pathIsRegularFile /this/does/not/exist
|
||||
=> false
|
||||
pathIsRegularFile /some/file.nix
|
||||
=> true
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
pathIsRegularFile :: Path -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path]
|
||||
|
||||
*/
|
||||
pathIsRegularFile = path:
|
||||
pathExists path && pathType path == "regular";
|
||||
|
||||
/**
|
||||
A map of all haskell packages defined in the given path,
|
||||
identified by having a cabal file with the same name as the
|
||||
directory itself.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
Path -> Map String Path
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [root] The directory within to search
|
||||
|
||||
*/
|
||||
haskellPathsInDir =
|
||||
# The directory within to search
|
||||
root:
|
||||
let # Files in the root
|
||||
root-files = builtins.attrNames (builtins.readDir root);
|
||||
# Files with their full paths
|
||||
root-files-with-paths =
|
||||
map (file:
|
||||
{ name = file; value = root + "/${file}"; }
|
||||
) root-files;
|
||||
# Subdirectories of the root with a cabal file.
|
||||
cabal-subdirs =
|
||||
builtins.filter ({ name, value }:
|
||||
builtins.pathExists (value + "/${name}.cabal")
|
||||
) root-files-with-paths;
|
||||
in builtins.listToAttrs cabal-subdirs;
|
||||
/**
|
||||
Find the first directory containing a file matching 'pattern'
|
||||
upward from a given 'file'.
|
||||
Returns 'null' if no directories contain a file matching 'pattern'.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
RegExp -> Path -> Nullable { path : Path; matches : [ MatchResults ]; }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [pattern] The pattern to search for
|
||||
- [file] The file to start searching upward from
|
||||
|
||||
*/
|
||||
locateDominatingFile =
|
||||
# The pattern to search for
|
||||
pattern:
|
||||
# The file to start searching upward from
|
||||
file:
|
||||
let go = path:
|
||||
let files = builtins.attrNames (builtins.readDir path);
|
||||
matches = builtins.filter (match: match != null)
|
||||
(map (builtins.match pattern) files);
|
||||
in
|
||||
if builtins.length matches != 0
|
||||
then { inherit path matches; }
|
||||
else if path == /.
|
||||
then null
|
||||
else go (dirOf path);
|
||||
parent = dirOf file;
|
||||
isDir =
|
||||
let base = baseNameOf file;
|
||||
type = (builtins.readDir parent).${base} or null;
|
||||
in file == /. || type == "directory";
|
||||
in go (if isDir then file else parent);
|
||||
|
||||
|
||||
/**
|
||||
Given a directory, return a flattened list of all files within it recursively.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
Path -> [ Path ]
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [dir] The path to recursively list
|
||||
|
||||
*/
|
||||
listFilesRecursive =
|
||||
# The path to recursively list
|
||||
dir:
|
||||
lib.flatten (lib.mapAttrsToList (name: type:
|
||||
if type == "directory" then
|
||||
lib.filesystem.listFilesRecursive (dir + "/${name}")
|
||||
else
|
||||
dir + "/${name}"
|
||||
) (builtins.readDir dir));
|
||||
|
||||
}
|
215
pesto/test_data/assets/fixed-points.nix
Normal file
215
pesto/test_data/assets/fixed-points.nix
Normal file
@ -0,0 +1,215 @@
|
||||
{ lib, ... }:
|
||||
rec {
|
||||
/**
|
||||
`fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`.
|
||||
`f` must be a lazy function.
|
||||
This means that `x` must be a value that can be partially evaluated,
|
||||
such as an attribute set, a list, or a function.
|
||||
This way, `f` can use one part of `x` to compute another part.
|
||||
**Relation to syntactic recursion**
|
||||
This section explains `fix` by refactoring from syntactic recursion to a call of `fix` instead.
|
||||
For context, Nix lets you define attributes in terms of other attributes syntactically using the [`rec { }` syntax](https://nixos.org/manual/nix/stable/language/constructs.html#recursive-sets).
|
||||
```nix
|
||||
nix-repl> rec {
|
||||
foo = "foo";
|
||||
bar = "bar";
|
||||
foobar = foo + bar;
|
||||
}
|
||||
{ bar = "bar"; foo = "foo"; foobar = "foobar"; }
|
||||
```
|
||||
This is convenient when constructing a value to pass to a function for example,
|
||||
but an equivalent effect can be achieved with the `let` binding syntax:
|
||||
```nix
|
||||
nix-repl> let self = {
|
||||
foo = "foo";
|
||||
bar = "bar";
|
||||
foobar = self.foo + self.bar;
|
||||
}; in self
|
||||
{ bar = "bar"; foo = "foo"; foobar = "foobar"; }
|
||||
```
|
||||
But in general you can get more reuse out of `let` bindings by refactoring them to a function.
|
||||
```nix
|
||||
nix-repl> f = self: {
|
||||
foo = "foo";
|
||||
bar = "bar";
|
||||
foobar = self.foo + self.bar;
|
||||
}
|
||||
```
|
||||
This is where `fix` comes in, it contains the syntactic that's not in `f` anymore.
|
||||
```nix
|
||||
nix-repl> fix = f:
|
||||
let self = f self; in self;
|
||||
```
|
||||
By applying `fix` we get the final result.
|
||||
```nix
|
||||
nix-repl> fix f
|
||||
{ bar = "bar"; foo = "foo"; foobar = "foobar"; }
|
||||
```
|
||||
Such a refactored `f` using `fix` is not useful by itself.
|
||||
See [`extends`](#function-library-lib.fixedPoints.extends) for an example use case.
|
||||
There `self` is also often called `final`.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
fix (self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; })
|
||||
=> { bar = "bar"; foo = "foo"; foobar = "foobar"; }
|
||||
fix (self: [ 1 2 (elemAt self 0 + elemAt self 1) ])
|
||||
=> [ 1 2 3 ]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
fix :: (a -> a) -> a
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
|
||||
*/
|
||||
fix = f: let x = f x; in x;
|
||||
|
||||
/**
|
||||
A variant of `fix` that records the original recursive attribute set in the
|
||||
result, in an attribute named `__unfix__`.
|
||||
This is useful in combination with the `extends` function to
|
||||
implement deep overriding.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
|
||||
*/
|
||||
fix' = f: let x = f x // { __unfix__ = f; }; in x;
|
||||
|
||||
/**
|
||||
Return the fixpoint that `f` converges to when called iteratively, starting
|
||||
with the input `x`.
|
||||
```
|
||||
nix-repl> converge (x: x / 2) 16
|
||||
0
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
(a -> a) -> a -> a
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
- [x]
|
||||
|
||||
*/
|
||||
converge = f: x:
|
||||
let
|
||||
x' = f x;
|
||||
in
|
||||
if x' == x
|
||||
then x
|
||||
else converge f x';
|
||||
|
||||
/**
|
||||
Modify the contents of an explicitly recursive attribute set in a way that
|
||||
honors `self`-references. This is accomplished with a function
|
||||
```nix
|
||||
g = self: super: { foo = super.foo + " + "; }
|
||||
```
|
||||
that has access to the unmodified input (`super`) as well as the final
|
||||
non-recursive representation of the attribute set (`self`). `extends`
|
||||
differs from the native `//` operator insofar as that it's applied *before*
|
||||
references to `self` are resolved:
|
||||
```
|
||||
nix-repl> fix (extends g f)
|
||||
{ bar = "bar"; foo = "foo + "; foobar = "foo + bar"; }
|
||||
```
|
||||
The name of the function is inspired by object-oriented inheritance, i.e.
|
||||
think of it as an infix operator `g extends f` that mimics the syntax from
|
||||
Java. It may seem counter-intuitive to have the "base class" as the second
|
||||
argument, but it's nice this way if several uses of `extends` are cascaded.
|
||||
To get a better understanding how `extends` turns a function with a fix
|
||||
point (the package set we start with) into a new function with a different fix
|
||||
point (the desired packages set) lets just see, how `extends g f`
|
||||
unfolds with `g` and `f` defined above:
|
||||
```
|
||||
extends g f = self: let super = f self; in super // g self super;
|
||||
= self: let super = { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }; in super // g self super
|
||||
= self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; } // g self { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }
|
||||
= self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; } // { foo = "foo" + " + "; }
|
||||
= self: { foo = "foo + "; bar = "bar"; foobar = self.foo + self.bar; }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
- [rattrs]
|
||||
- [self]
|
||||
|
||||
*/
|
||||
extends = f: rattrs: self: let super = rattrs self; in super // f self super;
|
||||
|
||||
/**
|
||||
Compose two extending functions of the type expected by 'extends'
|
||||
into one where changes made in the first are available in the
|
||||
'super' of the second
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
- [g]
|
||||
- [final]
|
||||
- [prev]
|
||||
|
||||
*/
|
||||
composeExtensions =
|
||||
f: g: final: prev:
|
||||
let fApplied = f final prev;
|
||||
prev' = prev // fApplied;
|
||||
in fApplied // g final prev';
|
||||
|
||||
/**
|
||||
Compose several extending functions of the type expected by 'extends' into
|
||||
one where changes made in preceding functions are made available to
|
||||
subsequent ones.
|
||||
```
|
||||
composeManyExtensions : [packageSet -> packageSet -> packageSet] -> packageSet -> packageSet -> packageSet
|
||||
^final ^prev ^overrides ^final ^prev ^overrides
|
||||
```
|
||||
*/
|
||||
composeManyExtensions =
|
||||
lib.foldr (x: y: composeExtensions x y) (final: prev: {});
|
||||
|
||||
/**
|
||||
Create an overridable, recursive attribute set. For example:
|
||||
```
|
||||
nix-repl> obj = makeExtensible (self: { })
|
||||
nix-repl> obj
|
||||
{ __unfix__ = «lambda»; extend = «lambda»; }
|
||||
nix-repl> obj = obj.extend (self: super: { foo = "foo"; })
|
||||
nix-repl> obj
|
||||
{ __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; }
|
||||
nix-repl> obj = obj.extend (self: super: { foo = super.foo + " + "; bar = "bar"; foobar = self.foo + self.bar; })
|
||||
nix-repl> obj
|
||||
{ __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; }
|
||||
```
|
||||
*/
|
||||
makeExtensible = makeExtensibleWithCustomName "extend";
|
||||
|
||||
/**
|
||||
Same as `makeExtensible` but the name of the extending attribute is
|
||||
customized.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [extenderName]
|
||||
- [rattrs]
|
||||
|
||||
*/
|
||||
makeExtensibleWithCustomName = extenderName: rattrs:
|
||||
fix' (self: (rattrs self) // {
|
||||
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
|
||||
});
|
||||
}
|
5
pesto/test_data/assets/flake.nix
Normal file
5
pesto/test_data/assets/flake.nix
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
description = "Library of low-level helper functions for nix expressions.";
|
||||
|
||||
outputs = { self }: { lib = import ./.; };
|
||||
}
|
630
pesto/test_data/assets/generators.nix
Normal file
630
pesto/test_data/assets/generators.nix
Normal file
@ -0,0 +1,630 @@
|
||||
/**
|
||||
Functions that generate widespread file
|
||||
* formats from nix data structures.
|
||||
*
|
||||
* They all follow a similar interface:
|
||||
* generator { config-attrs } data
|
||||
*
|
||||
* `config-attrs` are “holes” in the generators
|
||||
* with sensible default implementations that
|
||||
* can be overwritten. The default implementations
|
||||
* are mostly generators themselves, called with
|
||||
* their respective default values; they can be reused.
|
||||
*
|
||||
* Tests can be found in ./tests/misc.nix
|
||||
* Documentation in the manual, #sec-generators
|
||||
*/
|
||||
{ lib }:
|
||||
with (lib).trivial;
|
||||
let
|
||||
libStr = lib.strings;
|
||||
libAttr = lib.attrsets;
|
||||
|
||||
inherit (lib) isFunction;
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
## -- HELPER FUNCTIONS & DEFAULTS --
|
||||
|
||||
/**
|
||||
Convert a value to a sensible default string representation.
|
||||
* The builtin `toString` function has some strange defaults,
|
||||
* suitable for bash scripts but not much else.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkValueStringDefault = {}: v: with builtins;
|
||||
let err = t: v: abort
|
||||
("generators.mkValueStringDefault: " +
|
||||
"${t} not supported: ${toPretty {} v}");
|
||||
in if isInt v then toString v
|
||||
# convert derivations to store paths
|
||||
else if lib.isDerivation v then toString v
|
||||
# we default to not quoting strings
|
||||
else if isString v then v
|
||||
# isString returns "1", which is not a good default
|
||||
else if true == v then "true"
|
||||
# here it returns to "", which is even less of a good default
|
||||
else if false == v then "false"
|
||||
else if null == v then "null"
|
||||
# if you have lists you probably want to replace this
|
||||
else if isList v then err "lists" v
|
||||
# same as for lists, might want to replace
|
||||
else if isAttrs v then err "attrsets" v
|
||||
# functions can’t be printed of course
|
||||
else if isFunction v then err "functions" v
|
||||
# Floats currently can't be converted to precise strings,
|
||||
# condition warning on nix version once this isn't a problem anymore
|
||||
# See https://github.com/NixOS/nix/pull/3480
|
||||
else if isFloat v then libStr.floatToString v
|
||||
else err "this value is" (toString v);
|
||||
|
||||
|
||||
/**
|
||||
Generate a line of key k and value v, separated by
|
||||
* character sep. If sep appears in k, it is escaped.
|
||||
* Helper for synaxes with different separators.
|
||||
*
|
||||
* mkValueString specifies how values should be formatted.
|
||||
*
|
||||
* mkKeyValueDefault {} ":" "f:oo" "bar"
|
||||
* > "f\:oo:bar"
|
||||
|
||||
# Arguments
|
||||
|
||||
- [sep]
|
||||
- [k]
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkKeyValueDefault = {
|
||||
mkValueString ? mkValueStringDefault {}
|
||||
}: sep: k: v:
|
||||
"${libStr.escape [sep] k}${sep}${mkValueString v}";
|
||||
|
||||
|
||||
## -- FILE FORMAT GENERATORS --
|
||||
|
||||
|
||||
/**
|
||||
Generate a key-value-style config file from an attrset.
|
||||
*
|
||||
* mkKeyValue is the same as in toINI.
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
toKeyValue = {
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
indent ? ""
|
||||
}:
|
||||
let mkLine = k: v: indent + mkKeyValue k v + "\n";
|
||||
mkLines = if listsAsDuplicateKeys
|
||||
then k: v: map (mkLine k) (if lib.isList v then v else [v])
|
||||
else k: v: [ (mkLine k v) ];
|
||||
in attrs: libStr.concatStrings (lib.concatLists (libAttr.mapAttrsToList mkLines attrs));
|
||||
|
||||
|
||||
/**
|
||||
Generate an INI-style config file from an
|
||||
* attrset of sections to an attrset of key-value pairs.
|
||||
*
|
||||
* generators.toINI {} {
|
||||
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
|
||||
* baz = { "also, integers" = 42; };
|
||||
* }
|
||||
*
|
||||
*> [baz]
|
||||
*> also, integers=42
|
||||
*>
|
||||
*> [foo]
|
||||
*> ciao=bar
|
||||
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
|
||||
*
|
||||
* The mk* configuration attributes can generically change
|
||||
* the way sections and key-value strings are generated.
|
||||
*
|
||||
* For more examples see the test cases in ./tests/misc.nix.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [attrsOfAttrs]
|
||||
|
||||
*/
|
||||
toINI = {
|
||||
# apply transformations (e.g. escapes) to section names
|
||||
mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
|
||||
# format a setting line from key and value
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
# allow lists as values for duplicate keys
|
||||
listsAsDuplicateKeys ? false
|
||||
}: attrsOfAttrs:
|
||||
let
|
||||
# map function to string for each key val
|
||||
mapAttrsToStringsSep = sep: mapFn: attrs:
|
||||
libStr.concatStringsSep sep
|
||||
(libAttr.mapAttrsToList mapFn attrs);
|
||||
mkSection = sectName: sectValues: ''
|
||||
[${mkSectionName sectName}]
|
||||
'' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
|
||||
in
|
||||
# map input to ini sections
|
||||
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
||||
|
||||
/**
|
||||
Generate an INI-style config file from an attrset
|
||||
* specifying the global section (no header), and an
|
||||
* attrset of sections to an attrset of key-value pairs.
|
||||
*
|
||||
* generators.toINIWithGlobalSection {} {
|
||||
* globalSection = {
|
||||
* someGlobalKey = "hi";
|
||||
* };
|
||||
* sections = {
|
||||
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
|
||||
* baz = { "also, integers" = 42; };
|
||||
* }
|
||||
*
|
||||
*> someGlobalKey=hi
|
||||
*>
|
||||
*> [baz]
|
||||
*> also, integers=42
|
||||
*>
|
||||
*> [foo]
|
||||
*> ciao=bar
|
||||
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
|
||||
*
|
||||
* The mk* configuration attributes can generically change
|
||||
* the way sections and key-value strings are generated.
|
||||
*
|
||||
* For more examples see the test cases in ./tests/misc.nix.
|
||||
*
|
||||
* If you don’t need a global section, you can also use
|
||||
* `generators.toINI` directly, which only takes
|
||||
* the part in `sections`.
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
toINIWithGlobalSection = {
|
||||
# apply transformations (e.g. escapes) to section names
|
||||
mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
|
||||
# format a setting line from key and value
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
# allow lists as values for duplicate keys
|
||||
listsAsDuplicateKeys ? false
|
||||
}: { globalSection, sections ? {} }:
|
||||
( if globalSection == {}
|
||||
then ""
|
||||
else (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection)
|
||||
+ "\n")
|
||||
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
|
||||
|
||||
/**
|
||||
Generate a git-config file from an attrset.
|
||||
*
|
||||
* It has two major differences from the regular INI format:
|
||||
*
|
||||
* 1. values are indented with tabs
|
||||
* 2. sections can have sub-sections
|
||||
*
|
||||
* generators.toGitINI {
|
||||
* url."ssh://git@github.com/".insteadOf = "https://github.com";
|
||||
* user.name = "edolstra";
|
||||
* }
|
||||
*
|
||||
*> [url "ssh://git@github.com/"]
|
||||
*> insteadOf = "https://github.com"
|
||||
*>
|
||||
*> [user]
|
||||
*> name = "edolstra"
|
||||
|
||||
# Arguments
|
||||
|
||||
- [attrs]
|
||||
|
||||
*/
|
||||
toGitINI = attrs:
|
||||
with builtins;
|
||||
let
|
||||
mkSectionName = name:
|
||||
let
|
||||
containsQuote = libStr.hasInfix ''"'' name;
|
||||
sections = libStr.splitString "." name;
|
||||
section = head sections;
|
||||
subsections = tail sections;
|
||||
subsection = concatStringsSep "." subsections;
|
||||
in if containsQuote || subsections == [ ] then
|
||||
name
|
||||
else
|
||||
''${section} "${subsection}"'';
|
||||
|
||||
mkValueString = v:
|
||||
let
|
||||
escapedV = ''
|
||||
"${
|
||||
replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v
|
||||
}"'';
|
||||
in mkValueStringDefault { } (if isString v then escapedV else v);
|
||||
|
||||
# generation for multiple ini values
|
||||
mkKeyValue = k: v:
|
||||
let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
|
||||
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (lib.toList v));
|
||||
|
||||
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
|
||||
gitFlattenAttrs = let
|
||||
recurse = path: value:
|
||||
if isAttrs value && !lib.isDerivation value then
|
||||
lib.mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
||||
else if length path > 1 then {
|
||||
${concatStringsSep "." (lib.reverseList (tail path))}.${head path} = value;
|
||||
} else {
|
||||
${head path} = value;
|
||||
};
|
||||
in attrs: lib.foldl lib.recursiveUpdate { } (lib.flatten (recurse [ ] attrs));
|
||||
|
||||
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
|
||||
in
|
||||
toINI_ (gitFlattenAttrs attrs);
|
||||
|
||||
# mkKeyValueDefault wrapper that handles dconf INI quirks.
|
||||
# The main differences of the format is that it requires strings to be quoted.
|
||||
mkDconfKeyValue = mkKeyValueDefault { mkValueString = v: toString (lib.gvariant.mkValue v); } "=";
|
||||
|
||||
# Generates INI in dconf keyfile style. See https://help.gnome.org/admin/system-admin-guide/stable/dconf-keyfiles.html.en
|
||||
# for details.
|
||||
toDconfINI = toINI { mkKeyValue = mkDconfKeyValue; };
|
||||
|
||||
/**
|
||||
Generates JSON from an arbitrary (non-function) value.
|
||||
* For more information see the documentation of the builtin.
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
toJSON = {}: builtins.toJSON;
|
||||
|
||||
|
||||
/**
|
||||
YAML has been a strict superset of JSON since 1.2, so we
|
||||
* use toJSON. Before it only had a few differences referring
|
||||
* to implicit typing rules, so it should work with older
|
||||
* parsers as well.
|
||||
*/
|
||||
toYAML = toJSON;
|
||||
|
||||
withRecursion =
|
||||
{
|
||||
/**
|
||||
If this option is not null, the given value will stop evaluating at a certain depth
|
||||
*/
|
||||
depthLimit
|
||||
/**
|
||||
If this option is true, an error will be thrown, if a certain given depth is exceeded
|
||||
*/
|
||||
, throwOnDepthLimit ? true
|
||||
}:
|
||||
assert builtins.isInt depthLimit;
|
||||
let
|
||||
specialAttrs = [
|
||||
"__functor"
|
||||
"__functionArgs"
|
||||
"__toString"
|
||||
"__pretty"
|
||||
];
|
||||
stepIntoAttr = evalNext: name:
|
||||
if builtins.elem name specialAttrs
|
||||
then id
|
||||
else evalNext;
|
||||
transform = depth:
|
||||
if depthLimit != null && depth > depthLimit then
|
||||
if throwOnDepthLimit
|
||||
then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
|
||||
else const "<unevaluated>"
|
||||
else id;
|
||||
mapAny = with builtins; depth: v:
|
||||
let
|
||||
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
|
||||
in
|
||||
if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
|
||||
else if isList v then map evalNext v
|
||||
else transform (depth + 1) v;
|
||||
in
|
||||
mapAny 0;
|
||||
|
||||
/**
|
||||
Pretty print a value, akin to `builtins.trace`.
|
||||
* Should probably be a builtin as well.
|
||||
* The pretty-printed string should be suitable for rendering default values
|
||||
* in the NixOS manual. In particular, it should be as close to a valid Nix expression
|
||||
* as possible.
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
toPretty = {
|
||||
/**
|
||||
If this option is true, attrsets like { __pretty = fn; val = …; }
|
||||
will use fn to convert val to a pretty printed representation.
|
||||
(This means fn is type Val -> String.)
|
||||
*/
|
||||
allowPrettyValues ? false,
|
||||
/**
|
||||
If this option is true, the output is indented with newlines for attribute sets and lists
|
||||
*/
|
||||
multiline ? true,
|
||||
/**
|
||||
Initial indentation level
|
||||
*/
|
||||
indent ? ""
|
||||
}:
|
||||
let
|
||||
go = indent: v: with builtins;
|
||||
let isPath = v: typeOf v == "path";
|
||||
introSpace = if multiline then "\n${indent} " else " ";
|
||||
outroSpace = if multiline then "\n${indent}" else " ";
|
||||
in if isInt v then toString v
|
||||
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
|
||||
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
|
||||
# pretty-printing purposes this is acceptable.
|
||||
else if isFloat v then builtins.toJSON v
|
||||
else if isString v then
|
||||
let
|
||||
lines = filter (v: ! isList v) (builtins.split "\n" v);
|
||||
escapeSingleline = libStr.escape [ "\\" "\"" "\${" ];
|
||||
escapeMultiline = libStr.replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
||||
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
|
||||
multilineResult = let
|
||||
escapedLines = map escapeMultiline lines;
|
||||
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
|
||||
# indentation level. Otherwise, '' is appended to the last line.
|
||||
lastLine = lib.last escapedLines;
|
||||
in "''" + introSpace + concatStringsSep introSpace (lib.init escapedLines)
|
||||
+ (if lastLine == "" then outroSpace else introSpace + lastLine) + "''";
|
||||
in
|
||||
if multiline && length lines > 1 then multilineResult else singlelineResult
|
||||
else if true == v then "true"
|
||||
else if false == v then "false"
|
||||
else if null == v then "null"
|
||||
else if isPath v then toString v
|
||||
else if isList v then
|
||||
if v == [] then "[ ]"
|
||||
else "[" + introSpace
|
||||
+ libStr.concatMapStringsSep introSpace (go (indent + " ")) v
|
||||
+ outroSpace + "]"
|
||||
else if isFunction v then
|
||||
let fna = lib.functionArgs v;
|
||||
showFnas = concatStringsSep ", " (libAttr.mapAttrsToList
|
||||
(name: hasDefVal: if hasDefVal then name + "?" else name)
|
||||
fna);
|
||||
in if fna == {} then "<function>"
|
||||
else "<function, args: {${showFnas}}>"
|
||||
else if isAttrs v then
|
||||
# apply pretty values if allowed
|
||||
if allowPrettyValues && v ? __pretty && v ? val
|
||||
then v.__pretty v.val
|
||||
else if v == {} then "{ }"
|
||||
else if v ? type && v.type == "derivation" then
|
||||
"<derivation ${v.name or "???"}>"
|
||||
else "{" + introSpace
|
||||
+ libStr.concatStringsSep introSpace (libAttr.mapAttrsToList
|
||||
(name: value:
|
||||
"${libStr.escapeNixIdentifier name} = ${
|
||||
builtins.addErrorContext "while evaluating an attribute `${name}`"
|
||||
(go (indent + " ") value)
|
||||
};") v)
|
||||
+ outroSpace + "}"
|
||||
else abort "generators.toPretty: should never happen (v = ${v})";
|
||||
in go indent;
|
||||
|
||||
# PLIST handling
|
||||
toPlist = {}: v: let
|
||||
isFloat = builtins.isFloat or (x: false);
|
||||
isPath = x: builtins.typeOf x == "path";
|
||||
expr = ind: x: with builtins;
|
||||
if x == null then "" else
|
||||
if isBool x then bool ind x else
|
||||
if isInt x then int ind x else
|
||||
if isString x then str ind x else
|
||||
if isList x then list ind x else
|
||||
if isAttrs x then attrs ind x else
|
||||
if isPath x then str ind (toString x) else
|
||||
if isFloat x then float ind x else
|
||||
abort "generators.toPlist: should never happen (v = ${v})";
|
||||
|
||||
literal = ind: x: ind + x;
|
||||
|
||||
bool = ind: x: literal ind (if x then "<true/>" else "<false/>");
|
||||
int = ind: x: literal ind "<integer>${toString x}</integer>";
|
||||
str = ind: x: literal ind "<string>${x}</string>";
|
||||
key = ind: x: literal ind "<key>${x}</key>";
|
||||
float = ind: x: literal ind "<real>${toString x}</real>";
|
||||
|
||||
indent = ind: expr "\t${ind}";
|
||||
|
||||
item = ind: libStr.concatMapStringsSep "\n" (indent ind);
|
||||
|
||||
list = ind: x: libStr.concatStringsSep "\n" [
|
||||
(literal ind "<array>")
|
||||
(item ind x)
|
||||
(literal ind "</array>")
|
||||
];
|
||||
|
||||
attrs = ind: x: libStr.concatStringsSep "\n" [
|
||||
(literal ind "<dict>")
|
||||
(attr ind x)
|
||||
(literal ind "</dict>")
|
||||
];
|
||||
|
||||
attr = let attrFilter = name: value: name != "_module" && value != null;
|
||||
in ind: x: libStr.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList
|
||||
(name: value: lib.optionals (attrFilter name value) [
|
||||
(key "\t${ind}" name)
|
||||
(expr "\t${ind}" value)
|
||||
]) x));
|
||||
|
||||
in ''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
${expr "" v}
|
||||
</plist>'';
|
||||
|
||||
/**
|
||||
Translate a simple Nix expression to Dhall notation.
|
||||
* Note that integers are translated to Integer and never
|
||||
* the Natural type.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
toDhall = { }@args: v:
|
||||
with builtins;
|
||||
let concatItems = lib.strings.concatStringsSep ", ";
|
||||
in if isAttrs v then
|
||||
"{ ${
|
||||
concatItems (lib.attrsets.mapAttrsToList
|
||||
(key: value: "${key} = ${toDhall args value}") v)
|
||||
} }"
|
||||
else if isList v then
|
||||
"[ ${concatItems (map (toDhall args) v)} ]"
|
||||
else if isInt v then
|
||||
"${if v < 0 then "" else "+"}${toString v}"
|
||||
else if isBool v then
|
||||
(if v then "True" else "False")
|
||||
else if isFunction v then
|
||||
abort "generators.toDhall: cannot convert a function to Dhall"
|
||||
else if v == null then
|
||||
abort "generators.toDhall: cannot convert a null to Dhall"
|
||||
else
|
||||
builtins.toJSON v;
|
||||
|
||||
/**
|
||||
Translate a simple Nix expression to Lua representation with occasional
|
||||
Lua-inlines that can be constructed by mkLuaInline function.
|
||||
Configuration:
|
||||
* multiline - by default is true which results in indented block-like view.
|
||||
* indent - initial indent.
|
||||
* asBindings - by default generate single value, but with this use attrset to set global vars.
|
||||
Attention:
|
||||
Regardless of multiline parameter there is no trailing newline.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
generators.toLua {}
|
||||
{
|
||||
cmd = [ "typescript-language-server" "--stdio" ];
|
||||
settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
|
||||
}
|
||||
->
|
||||
{
|
||||
["cmd"] = {
|
||||
"typescript-language-server",
|
||||
"--stdio"
|
||||
},
|
||||
["settings"] = {
|
||||
["workspace"] = {
|
||||
["library"] = (vim.api.nvim_get_runtime_file("", true))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toLua :: AttrSet -> Any -> String
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
toLua = {
|
||||
/**
|
||||
If this option is true, the output is indented with newlines for attribute sets and lists
|
||||
*/
|
||||
multiline ? true,
|
||||
/**
|
||||
Initial indentation level
|
||||
*/
|
||||
indent ? "",
|
||||
/**
|
||||
Interpret as variable bindings
|
||||
*/
|
||||
asBindings ? false,
|
||||
}@args: v:
|
||||
with builtins;
|
||||
let
|
||||
innerIndent = "${indent} ";
|
||||
introSpace = if multiline then "\n${innerIndent}" else " ";
|
||||
outroSpace = if multiline then "\n${indent}" else " ";
|
||||
innerArgs = args // {
|
||||
indent = if asBindings then indent else innerIndent;
|
||||
asBindings = false;
|
||||
};
|
||||
concatItems = concatStringsSep ",${introSpace}";
|
||||
isLuaInline = { _type ? null, ... }: _type == "lua-inline";
|
||||
|
||||
generatedBindings =
|
||||
assert lib.assertMsg (badVarNames == []) "Bad Lua var names: ${toPretty {} badVarNames}";
|
||||
libStr.concatStrings (
|
||||
lib.attrsets.mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
|
||||
);
|
||||
|
||||
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
|
||||
matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
|
||||
badVarNames = filter (name: matchVarName name == null) (attrNames v);
|
||||
in
|
||||
if asBindings then
|
||||
generatedBindings
|
||||
else if v == null then
|
||||
"nil"
|
||||
else if isInt v || isFloat v || isString v || isBool v then
|
||||
builtins.toJSON v
|
||||
else if isList v then
|
||||
(if v == [ ] then "{}" else
|
||||
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}")
|
||||
else if isAttrs v then
|
||||
(
|
||||
if isLuaInline v then
|
||||
"(${v.expr})"
|
||||
else if v == { } then
|
||||
"{}"
|
||||
else
|
||||
"{${introSpace}${concatItems (
|
||||
lib.attrsets.mapAttrsToList (key: value: "[${builtins.toJSON key}] = ${toLua innerArgs value}") v
|
||||
)}${outroSpace}}"
|
||||
)
|
||||
else
|
||||
abort "generators.toLua: type ${typeOf v} is unsupported";
|
||||
|
||||
/**
|
||||
Mark string as Lua expression to be inlined when processed by toLua.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkLuaInline :: String -> AttrSet
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [expr]
|
||||
|
||||
*/
|
||||
mkLuaInline = expr: { _type = "lua-inline"; inherit expr; };
|
||||
}
|
463
pesto/test_data/assets/gvariant.nix
Normal file
463
pesto/test_data/assets/gvariant.nix
Normal file
@ -0,0 +1,463 @@
|
||||
# This file is based on https://github.com/nix-community/home-manager
|
||||
# Copyright (c) 2017-2022 Home Manager contributors
|
||||
#
|
||||
|
||||
|
||||
{ lib }:
|
||||
|
||||
/**
|
||||
A partial and basic implementation of GVariant formatted strings.
|
||||
See https://docs.gtk.org/glib/gvariant-format-strings.html for detauls.
|
||||
Note, this API is not considered fully stable and it might therefore
|
||||
change in backwards incompatible ways without prior notice.
|
||||
*/
|
||||
let
|
||||
inherit (lib)
|
||||
concatMapStringsSep concatStrings escape head replaceStrings;
|
||||
|
||||
mkPrimitive = t: v: {
|
||||
_type = "gvariant";
|
||||
type = t;
|
||||
value = v;
|
||||
__toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html
|
||||
};
|
||||
|
||||
type = {
|
||||
arrayOf = t: "a${t}";
|
||||
maybeOf = t: "m${t}";
|
||||
tupleOf = ts: "(${concatStrings ts})";
|
||||
dictionaryEntryOf = nameType: valueType: "{${nameType}${valueType}}";
|
||||
string = "s";
|
||||
boolean = "b";
|
||||
uchar = "y";
|
||||
int16 = "n";
|
||||
uint16 = "q";
|
||||
int32 = "i";
|
||||
uint32 = "u";
|
||||
int64 = "x";
|
||||
uint64 = "t";
|
||||
double = "d";
|
||||
variant = "v";
|
||||
};
|
||||
|
||||
/**
|
||||
Check if a value is a GVariant value
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
isGVariant :: Any -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
isGVariant = v: v._type or "" == "gvariant";
|
||||
|
||||
in
|
||||
rec {
|
||||
|
||||
inherit type isGVariant;
|
||||
|
||||
/**
|
||||
Returns the GVariant value that most closely matches the given Nix value.
|
||||
If no GVariant value can be found unambiguously then error is thrown.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkValue :: Any -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkValue = v:
|
||||
if builtins.isBool v then
|
||||
mkBoolean v
|
||||
else if builtins.isFloat v then
|
||||
mkDouble v
|
||||
else if builtins.isString v then
|
||||
mkString v
|
||||
else if builtins.isList v then
|
||||
mkArray v
|
||||
else if isGVariant v then
|
||||
v
|
||||
else
|
||||
throw "The GVariant type of ${v} can't be inferred.";
|
||||
|
||||
/**
|
||||
Returns the GVariant array from the given type of the elements and a Nix list.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Creating a string array
|
||||
lib.gvariant.mkArray [ "a" "b" "c" ]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkArray :: [Any] -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elems]
|
||||
|
||||
*/
|
||||
mkArray = elems:
|
||||
let
|
||||
vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems);
|
||||
elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs))
|
||||
"Elements in a list should have same type."
|
||||
(head vs).type;
|
||||
in
|
||||
mkPrimitive (type.arrayOf elemType) vs // {
|
||||
__toString = self:
|
||||
"@${self.type} [${concatMapStringsSep "," toString self.value}]";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant array from the given empty Nix list.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Creating an empty string array
|
||||
lib.gvariant.mkEmptyArray (lib.gvariant.type.string)
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkEmptyArray :: gvariant.type -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elemType]
|
||||
|
||||
*/
|
||||
mkEmptyArray = elemType: mkPrimitive (type.arrayOf elemType) [ ] // {
|
||||
__toString = self: "@${self.type} []";
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Returns the GVariant variant from the given Nix value. Variants are containers
|
||||
of different GVariant type.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
lib.gvariant.mkArray [
|
||||
(lib.gvariant.mkVariant "a string")
|
||||
(lib.gvariant.mkVariant (lib.gvariant.mkInt32 1))
|
||||
]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkVariant :: Any -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elem]
|
||||
|
||||
*/
|
||||
mkVariant = elem:
|
||||
let gvarElem = mkValue elem;
|
||||
in mkPrimitive type.variant gvarElem // {
|
||||
__toString = self: "<${toString self.value}>";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant dictionary entry from the given key and value.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# A dictionary describing an Epiphany’s search provider
|
||||
[
|
||||
(lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany"))
|
||||
(lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d"))
|
||||
(lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo"))
|
||||
]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDictionaryEntry :: String -> Any -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [name] The key of the entry
|
||||
- [value] The value of the entry
|
||||
|
||||
*/
|
||||
mkDictionaryEntry =
|
||||
# The key of the entry
|
||||
name:
|
||||
# The value of the entry
|
||||
value:
|
||||
let
|
||||
name' = mkValue name;
|
||||
value' = mkValue value;
|
||||
dictionaryType = type.dictionaryEntryOf name'.type value'.type;
|
||||
in
|
||||
mkPrimitive dictionaryType { inherit name value; } // {
|
||||
__toString = self: "@${self.type} {${name'},${value'}}";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant maybe from the given element type.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkMaybe :: gvariant.type -> Any -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elemType]
|
||||
- [elem]
|
||||
|
||||
*/
|
||||
mkMaybe = elemType: elem:
|
||||
mkPrimitive (type.maybeOf elemType) elem // {
|
||||
__toString = self:
|
||||
if self.value == null then
|
||||
"@${self.type} nothing"
|
||||
else
|
||||
"just ${toString self.value}";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant nothing from the given element type.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkNothing :: gvariant.type -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elemType]
|
||||
|
||||
*/
|
||||
mkNothing = elemType: mkMaybe elemType null;
|
||||
|
||||
/**
|
||||
Returns the GVariant just from the given Nix value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkJust :: Any -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elem]
|
||||
|
||||
*/
|
||||
mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
|
||||
|
||||
/**
|
||||
Returns the GVariant tuple from the given Nix list.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkTuple :: [Any] -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [elems]
|
||||
|
||||
*/
|
||||
mkTuple = elems:
|
||||
let
|
||||
gvarElems = map mkValue elems;
|
||||
tupleType = type.tupleOf (map (e: e.type) gvarElems);
|
||||
in
|
||||
mkPrimitive tupleType gvarElems // {
|
||||
__toString = self:
|
||||
"@${self.type} (${concatMapStringsSep "," toString self.value})";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant boolean from the given Nix bool value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkBoolean :: Bool -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkBoolean = v:
|
||||
mkPrimitive type.boolean v // {
|
||||
__toString = self: if self.value then "true" else "false";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant string from the given Nix string value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkString :: String -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkString = v:
|
||||
let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
|
||||
in mkPrimitive type.string v // {
|
||||
__toString = self: "'${sanitize self.value}'";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant object path from the given Nix string value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkObjectpath :: String -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkObjectpath = v:
|
||||
mkPrimitive type.string v // {
|
||||
__toString = self: "objectpath '${escape [ "'" ] self.value}'";
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant uchar from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkUchar :: Int -> gvariant
|
||||
```
|
||||
*/
|
||||
mkUchar = mkPrimitive type.uchar;
|
||||
|
||||
/**
|
||||
Returns the GVariant int16 from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkInt16 :: Int -> gvariant
|
||||
```
|
||||
*/
|
||||
mkInt16 = mkPrimitive type.int16;
|
||||
|
||||
/**
|
||||
Returns the GVariant uint16 from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkUint16 :: Int -> gvariant
|
||||
```
|
||||
*/
|
||||
mkUint16 = mkPrimitive type.uint16;
|
||||
|
||||
/**
|
||||
Returns the GVariant int32 from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkInt32 :: Int -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkInt32 = v:
|
||||
mkPrimitive type.int32 v // {
|
||||
__toString = self: toString self.value;
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the GVariant uint32 from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkUint32 :: Int -> gvariant
|
||||
```
|
||||
*/
|
||||
mkUint32 = mkPrimitive type.uint32;
|
||||
|
||||
/**
|
||||
Returns the GVariant int64 from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkInt64 :: Int -> gvariant
|
||||
```
|
||||
*/
|
||||
mkInt64 = mkPrimitive type.int64;
|
||||
|
||||
/**
|
||||
Returns the GVariant uint64 from the given Nix int value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkUint64 :: Int -> gvariant
|
||||
```
|
||||
*/
|
||||
mkUint64 = mkPrimitive type.uint64;
|
||||
|
||||
/**
|
||||
Returns the GVariant double from the given Nix float value.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDouble :: Float -> gvariant
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
mkDouble = v:
|
||||
mkPrimitive type.double v // {
|
||||
__toString = self: toString self.value;
|
||||
};
|
||||
}
|
32
pesto/test_data/assets/kernel.nix
Normal file
32
pesto/test_data/assets/kernel.nix
Normal file
@ -0,0 +1,32 @@
|
||||
{ lib }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
|
||||
|
||||
# Keeping these around in case we decide to change this horrible implementation :)
|
||||
option = x:
|
||||
x // { optional = true; };
|
||||
|
||||
yes = { tristate = "y"; optional = false; };
|
||||
no = { tristate = "n"; optional = false; };
|
||||
module = { tristate = "m"; optional = false; };
|
||||
unset = { tristate = null; optional = false; };
|
||||
freeform = x: { freeform = x; optional = false; };
|
||||
|
||||
/**
|
||||
Common patterns/legacy used in common-config/hardened/config.nix
|
||||
|
||||
# Arguments
|
||||
|
||||
- [version]
|
||||
|
||||
*/
|
||||
whenHelpers = version: {
|
||||
whenAtLeast = ver: mkIf (versionAtLeast version ver);
|
||||
whenOlder = ver: mkIf (versionOlder version ver);
|
||||
# range is (inclusive, exclusive)
|
||||
whenBetween = verLow: verHigh: mkIf (versionAtLeast version verLow && versionOlder version verHigh);
|
||||
};
|
||||
|
||||
}
|
1222
pesto/test_data/assets/licenses.nix
Normal file
1222
pesto/test_data/assets/licenses.nix
Normal file
File diff suppressed because it is too large
Load Diff
1346
pesto/test_data/assets/lists.nix
Normal file
1346
pesto/test_data/assets/lists.nix
Normal file
File diff suppressed because it is too large
Load Diff
276
pesto/test_data/assets/meta.nix
Normal file
276
pesto/test_data/assets/meta.nix
Normal file
@ -0,0 +1,276 @@
|
||||
/**
|
||||
Some functions for manipulating meta attributes, as well as the
|
||||
name attribute.
|
||||
*/
|
||||
|
||||
{ lib }:
|
||||
|
||||
rec {
|
||||
|
||||
|
||||
/**
|
||||
Add to or override the meta attributes of the given
|
||||
derivation.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
addMetaAttrs {description = "Bla blah";} somePkg
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [newAttrs]
|
||||
- [drv]
|
||||
|
||||
*/
|
||||
addMetaAttrs = newAttrs: drv:
|
||||
drv // { meta = (drv.meta or {}) // newAttrs; };
|
||||
|
||||
|
||||
/**
|
||||
Disable Hydra builds of given derivation.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [drv]
|
||||
|
||||
*/
|
||||
dontDistribute = drv: addMetaAttrs { hydraPlatforms = []; } drv;
|
||||
|
||||
|
||||
/**
|
||||
Change the symbolic name of a package for presentation purposes
|
||||
(i.e., so that nix-env users can tell them apart).
|
||||
|
||||
# Arguments
|
||||
|
||||
- [name]
|
||||
- [drv]
|
||||
|
||||
*/
|
||||
setName = name: drv: drv // {inherit name;};
|
||||
|
||||
|
||||
/**
|
||||
Like `setName`, but takes the previous name as an argument.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
updateName (oldName: oldName + "-experimental") somePkg
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [updater]
|
||||
- [drv]
|
||||
|
||||
*/
|
||||
updateName = updater: drv: drv // {name = updater (drv.name);};
|
||||
|
||||
|
||||
/**
|
||||
Append a suffix to the name of a package (before the version
|
||||
part).
|
||||
|
||||
# Arguments
|
||||
|
||||
- [suffix]
|
||||
|
||||
*/
|
||||
appendToName = suffix: updateName (name:
|
||||
let x = builtins.parseDrvName name; in "${x.name}-${suffix}-${x.version}");
|
||||
|
||||
|
||||
/**
|
||||
Apply a function to each derivation and only to derivations in an attrset.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [f]
|
||||
- [set]
|
||||
|
||||
*/
|
||||
mapDerivationAttrset = f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
|
||||
|
||||
/**
|
||||
Set the nix-env priority of the package.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [priority]
|
||||
|
||||
*/
|
||||
setPrio = priority: addMetaAttrs { inherit priority; };
|
||||
|
||||
/**
|
||||
Decrease the nix-env priority of the package, i.e., other
|
||||
versions/variants of the package will be preferred.
|
||||
*/
|
||||
lowPrio = setPrio 10;
|
||||
|
||||
/**
|
||||
Apply lowPrio to an attrset with derivations
|
||||
|
||||
# Arguments
|
||||
|
||||
- [set]
|
||||
|
||||
*/
|
||||
lowPrioSet = set: mapDerivationAttrset lowPrio set;
|
||||
|
||||
|
||||
/**
|
||||
Increase the nix-env priority of the package, i.e., this
|
||||
version/variant of the package will be preferred.
|
||||
*/
|
||||
hiPrio = setPrio (-10);
|
||||
|
||||
/**
|
||||
Apply hiPrio to an attrset with derivations
|
||||
|
||||
# Arguments
|
||||
|
||||
- [set]
|
||||
|
||||
*/
|
||||
hiPrioSet = set: mapDerivationAttrset hiPrio set;
|
||||
|
||||
|
||||
/**
|
||||
Check to see if a platform is matched by the given `meta.platforms`
|
||||
element.
|
||||
A `meta.platform` pattern is either
|
||||
1. (legacy) a system string.
|
||||
2. (modern) a pattern for the entire platform structure (see `lib.systems.inspect.platformPatterns`).
|
||||
3. (modern) a pattern for the platform `parsed` field (see `lib.systems.inspect.patterns`).
|
||||
We can inject these into a pattern for the whole of a structured platform,
|
||||
and then match that.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [platform]
|
||||
- [elem]
|
||||
|
||||
*/
|
||||
platformMatch = platform: elem: let
|
||||
pattern =
|
||||
if builtins.isString elem
|
||||
then { system = elem; }
|
||||
else if elem?parsed
|
||||
then elem
|
||||
else { parsed = elem; };
|
||||
in lib.matchAttrs pattern platform;
|
||||
|
||||
/**
|
||||
Check if a package is available on a given platform.
|
||||
A package is available on a platform if both
|
||||
1. One of `meta.platforms` pattern matches the given
|
||||
platform, or `meta.platforms` is not present.
|
||||
2. None of `meta.badPlatforms` pattern matches the given platform.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [platform]
|
||||
- [pkg]
|
||||
|
||||
*/
|
||||
availableOn = platform: pkg:
|
||||
((!pkg?meta.platforms) || lib.any (platformMatch platform) pkg.meta.platforms) &&
|
||||
lib.all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or []);
|
||||
|
||||
/**
|
||||
Get the corresponding attribute in lib.licenses
|
||||
from the SPDX ID.
|
||||
For SPDX IDs, see
|
||||
https://spdx.org/licenses
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
lib.getLicenseFromSpdxId "MIT" == lib.licenses.mit
|
||||
=> true
|
||||
lib.getLicenseFromSpdxId "mIt" == lib.licenses.mit
|
||||
=> true
|
||||
lib.getLicenseFromSpdxId "MY LICENSE"
|
||||
=> trace: warning: getLicenseFromSpdxId: No license matches the given SPDX ID: MY LICENSE
|
||||
=> { shortName = "MY LICENSE"; }
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
getLicenseFromSpdxId :: str -> AttrSet
|
||||
```
|
||||
*/
|
||||
getLicenseFromSpdxId =
|
||||
let
|
||||
spdxLicenses = lib.mapAttrs (id: ls: assert lib.length ls == 1; builtins.head ls)
|
||||
(lib.groupBy (l: lib.toLower l.spdxId) (lib.filter (l: l ? spdxId) (lib.attrValues lib.licenses)));
|
||||
in licstr:
|
||||
spdxLicenses.${ lib.toLower licstr } or (
|
||||
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
|
||||
{ shortName = licstr; }
|
||||
);
|
||||
|
||||
/**
|
||||
Get the path to the main program of a package based on meta.mainProgram
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
getExe pkgs.hello
|
||||
=> "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello"
|
||||
getExe pkgs.mustache-go
|
||||
=> "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
getExe :: package -> string
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [x]
|
||||
|
||||
*/
|
||||
getExe = x:
|
||||
let
|
||||
y = x.meta.mainProgram or (
|
||||
# This could be turned into an error when 23.05 is at end of life
|
||||
lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
|
||||
lib.getName
|
||||
x
|
||||
);
|
||||
in
|
||||
getExe' x y;
|
||||
|
||||
/**
|
||||
Get the path of a program of a derivation.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
getExe' pkgs.hello "hello"
|
||||
=> "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello"
|
||||
getExe' pkgs.imagemagick "convert"
|
||||
=> "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
getExe' :: derivation -> string -> string
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [x]
|
||||
- [y]
|
||||
|
||||
*/
|
||||
getExe' = x: y: "${lib.getBin x}/bin/${y}";
|
||||
}
|
2
pesto/test_data/assets/minver.nix
Normal file
2
pesto/test_data/assets/minver.nix
Normal file
@ -0,0 +1,2 @@
|
||||
# Expose the minimum required version for evaluating Nixpkgs
|
||||
"2.3"
|
1488
pesto/test_data/assets/modules.nix
Normal file
1488
pesto/test_data/assets/modules.nix
Normal file
File diff suppressed because it is too large
Load Diff
533
pesto/test_data/assets/options.nix
Normal file
533
pesto/test_data/assets/options.nix
Normal file
@ -0,0 +1,533 @@
|
||||
# Nixpkgs/NixOS option handling.
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
all
|
||||
collect
|
||||
concatLists
|
||||
concatMap
|
||||
concatMapStringsSep
|
||||
filter
|
||||
foldl'
|
||||
head
|
||||
tail
|
||||
isAttrs
|
||||
isBool
|
||||
isDerivation
|
||||
isFunction
|
||||
isInt
|
||||
isList
|
||||
isString
|
||||
length
|
||||
mapAttrs
|
||||
optional
|
||||
optionals
|
||||
take
|
||||
;
|
||||
inherit (lib.attrsets)
|
||||
attrByPath
|
||||
optionalAttrs
|
||||
;
|
||||
inherit (lib.strings)
|
||||
concatMapStrings
|
||||
concatStringsSep
|
||||
;
|
||||
inherit (lib.types)
|
||||
mkOptionType
|
||||
;
|
||||
inherit (lib.lists)
|
||||
last
|
||||
;
|
||||
prioritySuggestion = ''
|
||||
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
|
||||
'';
|
||||
in
|
||||
rec {
|
||||
|
||||
/**
|
||||
Returns true when the given argument is an option
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
isOption 1 // => false
|
||||
isOption (mkOption {}) // => true
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
isOption :: a -> bool
|
||||
```
|
||||
*/
|
||||
isOption = lib.isType "option";
|
||||
|
||||
/**
|
||||
Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
|
||||
All keys default to `null` when not given.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
mkOption { } // => { _type = "option"; }
|
||||
mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
mkOption =
|
||||
{
|
||||
# Default value used when no definition is given in the configuration.
|
||||
default ? null,
|
||||
# Textual representation of the default, for the manual.
|
||||
defaultText ? null,
|
||||
# Example value used in the manual.
|
||||
example ? null,
|
||||
# String describing the option.
|
||||
description ? null,
|
||||
# Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
|
||||
relatedPackages ? null,
|
||||
# Option type, providing type-checking and value merging.
|
||||
type ? null,
|
||||
# Function that converts the option value to something else.
|
||||
apply ? null,
|
||||
# Whether the option is for NixOS developers only.
|
||||
internal ? null,
|
||||
# Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options.
|
||||
visible ? null,
|
||||
# Whether the option can be set only once
|
||||
readOnly ? null,
|
||||
} @ attrs:
|
||||
attrs // { _type = "option"; };
|
||||
|
||||
/**
|
||||
Creates an Option attribute set for a boolean value option i.e an
|
||||
option to be toggled on or off:
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
mkEnableOption "foo"
|
||||
=> { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [name] Name for the created option
|
||||
|
||||
*/
|
||||
mkEnableOption =
|
||||
# Name for the created option
|
||||
name: mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
description = "Whether to enable ${name}.";
|
||||
type = lib.types.bool;
|
||||
};
|
||||
|
||||
/**
|
||||
Creates an Option attribute set for an option that specifies the
|
||||
package a module should use for some purpose.
|
||||
The package is specified in the third argument under `default` as a list of strings
|
||||
representing its attribute path in nixpkgs (or another package set).
|
||||
Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module;
|
||||
alternatively to nixpkgs itself, another package set) as the first argument.
|
||||
If you pass another package set you should set the `pkgsText` option.
|
||||
This option is used to display the expression for the package set. It is `"pkgs"` by default.
|
||||
If your expression is complex you should parenthesize it, as the `pkgsText` argument
|
||||
is usually immediately followed by an attribute lookup (`.`).
|
||||
The second argument may be either a string or a list of strings.
|
||||
It provides the display name of the package in the description of the generated option
|
||||
(using only the last element if the passed value is a list)
|
||||
and serves as the fallback value for the `default` argument.
|
||||
To include extra information in the description, pass `extraDescription` to
|
||||
append arbitrary text to the generated description.
|
||||
You can also pass an `example` value, either a literal string or an attribute path.
|
||||
The `default` argument can be omitted if the provided name is
|
||||
an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list).
|
||||
You can also set `default` to just a string in which case it is interpreted as an attribute name
|
||||
(a singleton attribute path, if you will).
|
||||
If you wish to explicitly provide no default, pass `null` as `default`.
|
||||
If you want users to be able to set no package, pass `nullable = true`.
|
||||
In this mode a `default = null` will not be interpreted as no default and is interpreted literally.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
mkPackageOption pkgs "hello" { }
|
||||
=> { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
|
||||
mkPackageOption pkgs "GHC" {
|
||||
default = [ "ghc" ];
|
||||
example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
|
||||
}
|
||||
=> { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; }
|
||||
mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
|
||||
extraDescription = "This is an example and doesn't actually do anything.";
|
||||
}
|
||||
=> { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; }
|
||||
mkPackageOption pkgs "nushell" {
|
||||
nullable = true;
|
||||
}
|
||||
=> { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
|
||||
mkPackageOption pkgs "coreutils" {
|
||||
default = null;
|
||||
}
|
||||
=> { ...; description = "The coreutils package to use."; type = package; }
|
||||
mkPackageOption pkgs "dbus" {
|
||||
nullable = true;
|
||||
default = null;
|
||||
}
|
||||
=> { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
|
||||
mkPackageOption pkgs.javaPackages "OpenJFX" {
|
||||
default = "openjfx20";
|
||||
pkgsText = "pkgs.javaPackages";
|
||||
}
|
||||
=> { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; }
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkPackageOption :: pkgs -> (string|[string]) -> { nullable? :: bool, default? :: string|[string], example? :: null|string|[string], extraDescription? :: string, pkgsText? :: string } -> option
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [pkgs] Package set (an instantiation of nixpkgs such as pkgs in modules or another package set)
|
||||
- [name] Name for the package, shown in option description
|
||||
|
||||
*/
|
||||
mkPackageOption =
|
||||
# Package set (an instantiation of nixpkgs such as pkgs in modules or another package set)
|
||||
pkgs:
|
||||
# Name for the package, shown in option description
|
||||
name:
|
||||
{
|
||||
# Whether the package can be null, for example to disable installing a package altogether (defaults to false)
|
||||
nullable ? false,
|
||||
# The attribute path where the default package is located (may be omitted, in which case it is copied from `name`)
|
||||
default ? name,
|
||||
# A string or an attribute path to use as an example (may be omitted)
|
||||
example ? null,
|
||||
# Additional text to include in the option description (may be omitted)
|
||||
extraDescription ? "",
|
||||
# Representation of the package set passed as pkgs (defaults to `"pkgs"`)
|
||||
pkgsText ? "pkgs"
|
||||
}:
|
||||
let
|
||||
name' = if isList name then last name else name;
|
||||
default' = if isList default then default else [ default ];
|
||||
defaultText = concatStringsSep "." default';
|
||||
defaultValue = attrByPath default'
|
||||
(throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
|
||||
defaults = if default != null then {
|
||||
default = defaultValue;
|
||||
defaultText = literalExpression ("${pkgsText}." + defaultText);
|
||||
} else optionalAttrs nullable {
|
||||
default = null;
|
||||
};
|
||||
in mkOption (defaults // {
|
||||
description = "The ${name'} package to use."
|
||||
+ (if extraDescription == "" then "" else " ") + extraDescription;
|
||||
type = with lib.types; (if nullable then nullOr else lib.id) package;
|
||||
} // optionalAttrs (example != null) {
|
||||
example = literalExpression
|
||||
(if isList example then "${pkgsText}." + concatStringsSep "." example else example);
|
||||
});
|
||||
|
||||
/**
|
||||
Alias of mkPackageOption. Previously used to create options with markdown
|
||||
documentation, which is no longer required.
|
||||
*/
|
||||
mkPackageOptionMD = mkPackageOption;
|
||||
|
||||
/**
|
||||
This option accepts anything, but it does not produce any result.
|
||||
This is useful for sharing a module across different module sets
|
||||
without having to implement similar features as long as the
|
||||
values of the options are not accessed.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [attrs]
|
||||
|
||||
*/
|
||||
mkSinkUndeclaredOptions = attrs: mkOption ({
|
||||
internal = true;
|
||||
visible = false;
|
||||
default = false;
|
||||
description = "Sink for option definitions.";
|
||||
type = mkOptionType {
|
||||
name = "sink";
|
||||
check = x: true;
|
||||
merge = loc: defs: false;
|
||||
};
|
||||
apply = x: throw "Option value is not readable because the option is not declared.";
|
||||
} // attrs);
|
||||
|
||||
mergeDefaultOption = loc: defs:
|
||||
let list = getValues defs; in
|
||||
if length list == 1 then head list
|
||||
else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
|
||||
else if all isList list then concatLists list
|
||||
else if all isAttrs list then foldl' lib.mergeAttrs {} list
|
||||
else if all isBool list then foldl' lib.or false list
|
||||
else if all isString list then lib.concatStrings list
|
||||
else if all isInt list && all (x: x == head list) list then head list
|
||||
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
|
||||
|
||||
mergeOneOption = mergeUniqueOption { message = ""; };
|
||||
|
||||
mergeUniqueOption = { message }: loc: defs:
|
||||
if length defs == 1
|
||||
then (head defs).value
|
||||
else assert length defs > 1;
|
||||
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
|
||||
|
||||
/**
|
||||
"Merge" option definitions by checking that they all have the same value.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [loc]
|
||||
- [defs]
|
||||
|
||||
*/
|
||||
mergeEqualOption = loc: defs:
|
||||
if defs == [] then abort "This case should never happen."
|
||||
# Return early if we only have one element
|
||||
# This also makes it work for functions, because the foldl' below would try
|
||||
# to compare the first element with itself, which is false for functions
|
||||
else if length defs == 1 then (head defs).value
|
||||
else (foldl' (first: def:
|
||||
if def.value != first.value then
|
||||
throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}\n${prioritySuggestion}"
|
||||
else
|
||||
first) (head defs) (tail defs)).value;
|
||||
|
||||
/**
|
||||
Extracts values of all "value" keys of the given list.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
|
||||
getValues [ ] // => [ ]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
getValues :: [ { value :: a; } ] -> [a]
|
||||
```
|
||||
*/
|
||||
getValues = map (x: x.value);
|
||||
|
||||
/**
|
||||
Extracts values of all "file" keys of the given list
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
|
||||
getFiles [ ] // => [ ]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
getFiles :: [ { file :: a; } ] -> [a]
|
||||
```
|
||||
*/
|
||||
getFiles = map (x: x.file);
|
||||
|
||||
# Generate documentation template from the list of option declaration like
|
||||
# the set generated with filterOptionSets.
|
||||
optionAttrSetToDocList = optionAttrSetToDocList' [];
|
||||
|
||||
optionAttrSetToDocList' = _: options:
|
||||
concatMap (opt:
|
||||
let
|
||||
name = showOption opt.loc;
|
||||
docOption = {
|
||||
loc = opt.loc;
|
||||
inherit name;
|
||||
description = opt.description or null;
|
||||
declarations = filter (x: x != unknownModule) opt.declarations;
|
||||
internal = opt.internal or false;
|
||||
visible =
|
||||
if (opt?visible && opt.visible == "shallow")
|
||||
then true
|
||||
else opt.visible or true;
|
||||
readOnly = opt.readOnly or false;
|
||||
type = opt.type.description or "unspecified";
|
||||
}
|
||||
// optionalAttrs (opt ? example) {
|
||||
example =
|
||||
builtins.addErrorContext "while evaluating the example of option `${name}`" (
|
||||
renderOptionValue opt.example
|
||||
);
|
||||
}
|
||||
// optionalAttrs (opt ? defaultText || opt ? default) {
|
||||
default =
|
||||
builtins.addErrorContext "while evaluating the ${if opt?defaultText then "defaultText" else "default value"} of option `${name}`" (
|
||||
renderOptionValue (opt.defaultText or opt.default)
|
||||
);
|
||||
}
|
||||
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
|
||||
|
||||
subOptions =
|
||||
let ss = opt.type.getSubOptions opt.loc;
|
||||
in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
|
||||
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
|
||||
in
|
||||
# To find infinite recursion in NixOS option docs:
|
||||
# builtins.trace opt.loc
|
||||
[ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
|
||||
|
||||
|
||||
/**
|
||||
This function recursively removes all derivation attributes from
|
||||
`x` except for the `name` attribute.
|
||||
This is to make the generation of `options.xml` much more
|
||||
efficient: the XML representation of derivations is very large
|
||||
(on the order of megabytes) and is not actually used by the
|
||||
manual generator.
|
||||
This function was made obsolete by renderOptionValue and is kept for
|
||||
compatibility with out-of-tree code.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [x]
|
||||
|
||||
*/
|
||||
scrubOptionValue = x:
|
||||
if isDerivation x then
|
||||
{ type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
|
||||
else if isList x then map scrubOptionValue x
|
||||
else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
|
||||
else x;
|
||||
|
||||
|
||||
/**
|
||||
Ensures that the given option value (default or example) is a `_type`d string
|
||||
by rendering Nix values to `literalExpression`s.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [v]
|
||||
|
||||
*/
|
||||
renderOptionValue = v:
|
||||
if v ? _type && v ? text then v
|
||||
else literalExpression (lib.generators.toPretty {
|
||||
multiline = true;
|
||||
allowPrettyValues = true;
|
||||
} v);
|
||||
|
||||
|
||||
/**
|
||||
For use in the `defaultText` and `example` option attributes. Causes the
|
||||
given string to be rendered verbatim in the documentation as Nix code. This
|
||||
is necessary for complex values, e.g. functions, or values that depend on
|
||||
other values or packages.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [text]
|
||||
|
||||
*/
|
||||
literalExpression = text:
|
||||
if ! isString text then throw "literalExpression expects a string."
|
||||
else { _type = "literalExpression"; inherit text; };
|
||||
|
||||
literalExample = lib.warn "literalExample is deprecated, use literalExpression instead, or use literalMD for a non-Nix description." literalExpression;
|
||||
|
||||
/**
|
||||
Transition marker for documentation that's already migrated to markdown
|
||||
syntax. This is a no-op and no longer needed.
|
||||
*/
|
||||
mdDoc = lib.id;
|
||||
|
||||
/**
|
||||
For use in the `defaultText` and `example` option attributes. Causes the
|
||||
given MD text to be inserted verbatim in the documentation, for when
|
||||
a `literalExpression` would be too hard to read.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [text]
|
||||
|
||||
*/
|
||||
literalMD = text:
|
||||
if ! isString text then throw "literalMD expects a string."
|
||||
else { _type = "literalMD"; inherit text; };
|
||||
|
||||
# Helper functions.
|
||||
|
||||
/**
|
||||
Convert an option, described as a list of the option parts to a
|
||||
human-readable version.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
(showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
|
||||
(showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux"
|
||||
(showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable"
|
||||
Placeholders will not be quoted as they are not actual values:
|
||||
(showOption ["foo" "*" "bar"]) == "foo.*.bar"
|
||||
(showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [parts]
|
||||
|
||||
*/
|
||||
showOption = parts: let
|
||||
escapeOptionPart = part:
|
||||
let
|
||||
# We assume that these are "special values" and not real configuration data.
|
||||
# If it is real configuration data, it is rendered incorrectly.
|
||||
specialIdentifiers = [
|
||||
"<name>" # attrsOf (submodule {})
|
||||
"*" # listOf (submodule {})
|
||||
"<function body>" # functionTo
|
||||
];
|
||||
in if builtins.elem part specialIdentifiers
|
||||
then part
|
||||
else lib.strings.escapeNixIdentifier part;
|
||||
in (concatStringsSep ".") (map escapeOptionPart parts);
|
||||
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
|
||||
|
||||
showDefs = defs: concatMapStrings (def:
|
||||
let
|
||||
# Pretty print the value for display, if successful
|
||||
prettyEval = builtins.tryEval
|
||||
(lib.generators.toPretty { }
|
||||
(lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
|
||||
# Split it into its lines
|
||||
lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
|
||||
# Only display the first 5 lines, and indent them for better visibility
|
||||
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
|
||||
result =
|
||||
# Don't print any value if evaluating the value strictly fails
|
||||
if ! prettyEval.success then ""
|
||||
# Put it on a new line if it consists of multiple
|
||||
else if length lines > 1 then ":\n " + value
|
||||
else ": " + value;
|
||||
in "\n- In `${def.file}'${result}"
|
||||
) defs;
|
||||
|
||||
showOptionWithDefLocs = opt: ''
|
||||
${showOption opt.loc}, with values defined in:
|
||||
${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files}
|
||||
'';
|
||||
|
||||
unknownModule = "<unknown-file>";
|
||||
|
||||
}
|
575
pesto/test_data/assets/path/default.nix
Normal file
575
pesto/test_data/assets/path/default.nix
Normal file
@ -0,0 +1,575 @@
|
||||
# Functions for working with paths, see ./path.md
|
||||
{ lib }:
|
||||
let
|
||||
|
||||
inherit (builtins)
|
||||
isString
|
||||
isPath
|
||||
split
|
||||
match
|
||||
typeOf
|
||||
;
|
||||
|
||||
inherit (lib.lists)
|
||||
length
|
||||
head
|
||||
last
|
||||
genList
|
||||
elemAt
|
||||
all
|
||||
concatMap
|
||||
foldl'
|
||||
take
|
||||
drop
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
concatStringsSep
|
||||
substring
|
||||
;
|
||||
|
||||
inherit (lib.asserts)
|
||||
assertMsg
|
||||
;
|
||||
|
||||
inherit (lib.path.subpath)
|
||||
isValid
|
||||
;
|
||||
|
||||
# Return the reason why a subpath is invalid, or `null` if it's valid
|
||||
subpathInvalidReason = value:
|
||||
if ! isString value then
|
||||
"The given value is of type ${builtins.typeOf value}, but a string was expected"
|
||||
else if value == "" then
|
||||
"The given string is empty"
|
||||
else if substring 0 1 value == "/" then
|
||||
"The given string \"${value}\" starts with a `/`, representing an absolute path"
|
||||
# We don't support ".." components, see ./path.md#parent-directory
|
||||
else if match "(.*/)?\\.\\.(/.*)?" value != null then
|
||||
"The given string \"${value}\" contains a `..` component, which is not allowed in subpaths"
|
||||
else null;
|
||||
|
||||
# Split and normalise a relative path string into its components.
|
||||
# Error for ".." components and doesn't include "." components
|
||||
splitRelPath = path:
|
||||
let
|
||||
# Split the string into its parts using regex for efficiency. This regex
|
||||
# matches patterns like "/", "/./", "/././", with arbitrarily many "/"s
|
||||
# together. These are the main special cases:
|
||||
# - Leading "./" gets split into a leading "." part
|
||||
# - Trailing "/." or "/" get split into a trailing "." or ""
|
||||
# part respectively
|
||||
#
|
||||
# These are the only cases where "." and "" parts can occur
|
||||
parts = split "/+(\\./+)*" path;
|
||||
|
||||
# `split` creates a list of 2 * k + 1 elements, containing the k +
|
||||
# 1 parts, interleaved with k matches where k is the number of
|
||||
# (non-overlapping) matches. This calculation here gets the number of parts
|
||||
# back from the list length
|
||||
# floor( (2 * k + 1) / 2 ) + 1 == floor( k + 1/2 ) + 1 == k + 1
|
||||
partCount = length parts / 2 + 1;
|
||||
|
||||
# To assemble the final list of components we want to:
|
||||
# - Skip a potential leading ".", normalising "./foo" to "foo"
|
||||
# - Skip a potential trailing "." or "", normalising "foo/" and "foo/." to
|
||||
# "foo". See ./path.md#trailing-slashes
|
||||
skipStart = if head parts == "." then 1 else 0;
|
||||
skipEnd = if last parts == "." || last parts == "" then 1 else 0;
|
||||
|
||||
# We can now know the length of the result by removing the number of
|
||||
# skipped parts from the total number
|
||||
componentCount = partCount - skipEnd - skipStart;
|
||||
|
||||
in
|
||||
# Special case of a single "." path component. Such a case leaves a
|
||||
# componentCount of -1 due to the skipStart/skipEnd not verifying that
|
||||
# they don't refer to the same character
|
||||
if path == "." then []
|
||||
|
||||
# Generate the result list directly. This is more efficient than a
|
||||
# combination of `filter`, `init` and `tail`, because here we don't
|
||||
# allocate any intermediate lists
|
||||
else genList (index:
|
||||
# To get to the element we need to add the number of parts we skip and
|
||||
# multiply by two due to the interleaved layout of `parts`
|
||||
elemAt parts ((skipStart + index) * 2)
|
||||
) componentCount;
|
||||
|
||||
# Join relative path components together
|
||||
joinRelPath = components:
|
||||
# Always return relative paths with `./` as a prefix (./path.md#leading-dots-for-relative-paths)
|
||||
"./" +
|
||||
# An empty string is not a valid relative path, so we need to return a `.` when we have no components
|
||||
(if components == [] then "." else concatStringsSep "/" components);
|
||||
|
||||
# Type: Path -> { root :: Path, components :: [ String ] }
|
||||
#
|
||||
# Deconstruct a path value type into:
|
||||
# - root: The filesystem root of the path, generally `/`
|
||||
# - components: All the path's components
|
||||
#
|
||||
# This is similar to `splitString "/" (toString path)` but safer
|
||||
# because it can distinguish different filesystem roots
|
||||
deconstructPath =
|
||||
let
|
||||
recurse = components: base:
|
||||
# If the parent of a path is the path itself, then it's a filesystem root
|
||||
if base == dirOf base then { root = base; inherit components; }
|
||||
else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
|
||||
in recurse [];
|
||||
|
||||
in /**
|
||||
No rec! Add dependencies on this file at the top.
|
||||
*/ {
|
||||
|
||||
/**
|
||||
Append a subpath string to a path.
|
||||
Like `path + ("/" + string)` but safer, because it errors instead of returning potentially surprising results.
|
||||
More specifically, it checks that the first argument is a [path value type](https://nixos.org/manual/nix/stable/language/values.html#type-path"),
|
||||
and that the second argument is a [valid subpath string](#function-library-lib.path.subpath.isValid).
|
||||
Laws:
|
||||
- Not influenced by subpath [normalisation](#function-library-lib.path.subpath.normalise):
|
||||
append p s == append p (subpath.normalise s)
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
append /foo "bar/baz"
|
||||
=> /foo/bar/baz
|
||||
# subpaths don't need to be normalised
|
||||
append /foo "./bar//baz/./"
|
||||
=> /foo/bar/baz
|
||||
# can append to root directory
|
||||
append /. "foo/bar"
|
||||
=> /foo/bar
|
||||
# first argument needs to be a path value type
|
||||
append "/foo" "bar"
|
||||
=> <error>
|
||||
# second argument needs to be a valid subpath string
|
||||
append /foo /bar
|
||||
=> <error>
|
||||
append /foo ""
|
||||
=> <error>
|
||||
append /foo "/bar"
|
||||
=> <error>
|
||||
append /foo "../bar"
|
||||
=> <error>
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
append :: Path -> String -> Path
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path] The absolute path to append to
|
||||
- [subpath] The subpath string to append
|
||||
|
||||
*/
|
||||
append =
|
||||
# The absolute path to append to
|
||||
path:
|
||||
# The subpath string to append
|
||||
subpath:
|
||||
assert assertMsg (isPath path) ''
|
||||
lib.path.append: The first argument is of type ${builtins.typeOf path}, but a path was expected'';
|
||||
assert assertMsg (isValid subpath) ''
|
||||
lib.path.append: Second argument is not a valid subpath string:
|
||||
${subpathInvalidReason subpath}'';
|
||||
path + ("/" + subpath);
|
||||
|
||||
/**
|
||||
Whether the first path is a component-wise prefix of the second path.
|
||||
Laws:
|
||||
- `hasPrefix p q` is only true if [`q == append p s`](#function-library-lib.path.append) for some [subpath](#function-library-lib.path.subpath.isValid) `s`.
|
||||
- `hasPrefix` is a [non-strict partial order](https://en.wikipedia.org/wiki/Partially_ordered_set#Non-strict_partial_order) over the set of all path values.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
hasPrefix /foo /foo/bar
|
||||
=> true
|
||||
hasPrefix /foo /foo
|
||||
=> true
|
||||
hasPrefix /foo/bar /foo
|
||||
=> false
|
||||
hasPrefix /. /foo
|
||||
=> true
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
hasPrefix :: Path -> Path -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path1]
|
||||
|
||||
*/
|
||||
hasPrefix =
|
||||
path1:
|
||||
assert assertMsg
|
||||
(isPath path1)
|
||||
"lib.path.hasPrefix: First argument is of type ${typeOf path1}, but a path was expected";
|
||||
let
|
||||
path1Deconstructed = deconstructPath path1;
|
||||
in
|
||||
path2:
|
||||
assert assertMsg
|
||||
(isPath path2)
|
||||
"lib.path.hasPrefix: Second argument is of type ${typeOf path2}, but a path was expected";
|
||||
let
|
||||
path2Deconstructed = deconstructPath path2;
|
||||
in
|
||||
assert assertMsg
|
||||
(path1Deconstructed.root == path2Deconstructed.root) ''
|
||||
lib.path.hasPrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
|
||||
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
|
||||
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
|
||||
take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components;
|
||||
|
||||
/**
|
||||
Remove the first path as a component-wise prefix from the second path.
|
||||
The result is a [normalised subpath string](#function-library-lib.path.subpath.normalise).
|
||||
Laws:
|
||||
- Inverts [`append`](#function-library-lib.path.append) for [normalised subpath string](#function-library-lib.path.subpath.normalise):
|
||||
removePrefix p (append p s) == subpath.normalise s
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
removePrefix /foo /foo/bar/baz
|
||||
=> "./bar/baz"
|
||||
removePrefix /foo /foo
|
||||
=> "./."
|
||||
removePrefix /foo/bar /foo
|
||||
=> <error>
|
||||
removePrefix /. /foo
|
||||
=> "./foo"
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
removePrefix :: Path -> Path -> String
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path1]
|
||||
|
||||
*/
|
||||
removePrefix =
|
||||
path1:
|
||||
assert assertMsg
|
||||
(isPath path1)
|
||||
"lib.path.removePrefix: First argument is of type ${typeOf path1}, but a path was expected.";
|
||||
let
|
||||
path1Deconstructed = deconstructPath path1;
|
||||
path1Length = length path1Deconstructed.components;
|
||||
in
|
||||
path2:
|
||||
assert assertMsg
|
||||
(isPath path2)
|
||||
"lib.path.removePrefix: Second argument is of type ${typeOf path2}, but a path was expected.";
|
||||
let
|
||||
path2Deconstructed = deconstructPath path2;
|
||||
success = take path1Length path2Deconstructed.components == path1Deconstructed.components;
|
||||
components =
|
||||
if success then
|
||||
drop path1Length path2Deconstructed.components
|
||||
else
|
||||
throw ''
|
||||
lib.path.removePrefix: The first path argument "${toString path1}" is not a component-wise prefix of the second path argument "${toString path2}".'';
|
||||
in
|
||||
assert assertMsg
|
||||
(path1Deconstructed.root == path2Deconstructed.root) ''
|
||||
lib.path.removePrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
|
||||
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
|
||||
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
|
||||
joinRelPath components;
|
||||
|
||||
/**
|
||||
Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path).
|
||||
The result is an attribute set with these attributes:
|
||||
- `root`: The filesystem root of the path, meaning that this directory has no parent directory.
|
||||
- `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path.
|
||||
Laws:
|
||||
- [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path:
|
||||
p ==
|
||||
append
|
||||
(splitRoot p).root
|
||||
(splitRoot p).subpath
|
||||
- Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself:
|
||||
dirOf (splitRoot p).root == (splitRoot p).root
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
splitRoot /foo/bar
|
||||
=> { root = /.; subpath = "./foo/bar"; }
|
||||
splitRoot /.
|
||||
=> { root = /.; subpath = "./."; }
|
||||
# Nix neutralises `..` path components for all path values automatically
|
||||
splitRoot /foo/../bar
|
||||
=> { root = /.; subpath = "./bar"; }
|
||||
splitRoot "/foo/bar"
|
||||
=> <error>
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
splitRoot :: Path -> { root :: Path, subpath :: String }
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path] The path to split the root off of
|
||||
|
||||
*/
|
||||
splitRoot =
|
||||
# The path to split the root off of
|
||||
path:
|
||||
assert assertMsg
|
||||
(isPath path)
|
||||
"lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected";
|
||||
let
|
||||
deconstructed = deconstructPath path;
|
||||
in {
|
||||
root = deconstructed.root;
|
||||
subpath = joinRelPath deconstructed.components;
|
||||
};
|
||||
|
||||
/**
|
||||
Whether a value is a valid subpath string.
|
||||
A subpath string points to a specific file or directory within an absolute base directory.
|
||||
It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory.
|
||||
- The value is a string.
|
||||
- The string is not empty.
|
||||
- The string doesn't start with a `/`.
|
||||
- The string doesn't contain any `..` path components.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# Not a string
|
||||
subpath.isValid null
|
||||
=> false
|
||||
# Empty string
|
||||
subpath.isValid ""
|
||||
=> false
|
||||
# Absolute path
|
||||
subpath.isValid "/foo"
|
||||
=> false
|
||||
# Contains a `..` path component
|
||||
subpath.isValid "../foo"
|
||||
=> false
|
||||
# Valid subpath
|
||||
subpath.isValid "foo/bar"
|
||||
=> true
|
||||
# Doesn't need to be normalised
|
||||
subpath.isValid "./foo//bar/"
|
||||
=> true
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
subpath.isValid :: String -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [value] The value to check
|
||||
|
||||
*/
|
||||
subpath.isValid =
|
||||
# The value to check
|
||||
value:
|
||||
subpathInvalidReason value == null;
|
||||
|
||||
|
||||
/**
|
||||
Join subpath strings together using `/`, returning a normalised subpath string.
|
||||
Like `concatStringsSep "/"` but safer, specifically:
|
||||
- All elements must be [valid subpath strings](#function-library-lib.path.subpath.isValid).
|
||||
- The result gets [normalised](#function-library-lib.path.subpath.normalise).
|
||||
- The edge case of an empty list gets properly handled by returning the neutral subpath `"./."`.
|
||||
Laws:
|
||||
- Associativity:
|
||||
subpath.join [ x (subpath.join [ y z ]) ] == subpath.join [ (subpath.join [ x y ]) z ]
|
||||
- Identity - `"./."` is the neutral element for normalised paths:
|
||||
subpath.join [ ] == "./."
|
||||
subpath.join [ (subpath.normalise p) "./." ] == subpath.normalise p
|
||||
subpath.join [ "./." (subpath.normalise p) ] == subpath.normalise p
|
||||
- Normalisation - the result is [normalised](#function-library-lib.path.subpath.normalise):
|
||||
subpath.join ps == subpath.normalise (subpath.join ps)
|
||||
- For non-empty lists, the implementation is equivalent to [normalising](#function-library-lib.path.subpath.normalise) the result of `concatStringsSep "/"`.
|
||||
Note that the above laws can be derived from this one:
|
||||
ps != [] -> subpath.join ps == subpath.normalise (concatStringsSep "/" ps)
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
subpath.join [ "foo" "bar/baz" ]
|
||||
=> "./foo/bar/baz"
|
||||
# normalise the result
|
||||
subpath.join [ "./foo" "." "bar//./baz/" ]
|
||||
=> "./foo/bar/baz"
|
||||
# passing an empty list results in the current directory
|
||||
subpath.join [ ]
|
||||
=> "./."
|
||||
# elements must be valid subpath strings
|
||||
subpath.join [ /foo ]
|
||||
=> <error>
|
||||
subpath.join [ "" ]
|
||||
=> <error>
|
||||
subpath.join [ "/foo" ]
|
||||
=> <error>
|
||||
subpath.join [ "../foo" ]
|
||||
=> <error>
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
subpath.join :: [ String ] -> String
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [subpaths] The list of subpaths to join together
|
||||
|
||||
*/
|
||||
subpath.join =
|
||||
# The list of subpaths to join together
|
||||
subpaths:
|
||||
# Fast in case all paths are valid
|
||||
if all isValid subpaths
|
||||
then joinRelPath (concatMap splitRelPath subpaths)
|
||||
else
|
||||
# Otherwise we take our time to gather more info for a better error message
|
||||
# Strictly go through each path, throwing on the first invalid one
|
||||
# Tracks the list index in the fold accumulator
|
||||
foldl' (i: path:
|
||||
if isValid path
|
||||
then i + 1
|
||||
else throw ''
|
||||
lib.path.subpath.join: Element at index ${toString i} is not a valid subpath string:
|
||||
${subpathInvalidReason path}''
|
||||
) 0 subpaths;
|
||||
|
||||
/**
|
||||
Split [a subpath](#function-library-lib.path.subpath.isValid) into its path component strings.
|
||||
Throw an error if the subpath isn't valid.
|
||||
Note that the returned path components are also [valid subpath strings](#function-library-lib.path.subpath.isValid), though they are intentionally not [normalised](#function-library-lib.path.subpath.normalise).
|
||||
Laws:
|
||||
- Splitting a subpath into components and [joining](#function-library-lib.path.subpath.join) the components gives the same subpath but [normalised](#function-library-lib.path.subpath.normalise):
|
||||
subpath.join (subpath.components s) == subpath.normalise s
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
subpath.components "."
|
||||
=> [ ]
|
||||
subpath.components "./foo//bar/./baz/"
|
||||
=> [ "foo" "bar" "baz" ]
|
||||
subpath.components "/foo"
|
||||
=> <error>
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
subpath.components :: String -> [ String ]
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [subpath] The subpath string to split into components
|
||||
|
||||
*/
|
||||
subpath.components =
|
||||
# The subpath string to split into components
|
||||
subpath:
|
||||
assert assertMsg (isValid subpath) ''
|
||||
lib.path.subpath.components: Argument is not a valid subpath string:
|
||||
${subpathInvalidReason subpath}'';
|
||||
splitRelPath subpath;
|
||||
|
||||
/**
|
||||
Normalise a subpath. Throw an error if the subpath isn't [valid](#function-library-lib.path.subpath.isValid).
|
||||
- Limit repeating `/` to a single one.
|
||||
- Remove redundant `.` components.
|
||||
- Remove trailing `/` and `/.`.
|
||||
- Add leading `./`.
|
||||
Laws:
|
||||
- Idempotency - normalising multiple times gives the same result:
|
||||
subpath.normalise (subpath.normalise p) == subpath.normalise p
|
||||
- Uniqueness - there's only a single normalisation for the paths that lead to the same file system node:
|
||||
subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q})
|
||||
- Don't change the result when [appended](#function-library-lib.path.append) to a Nix path value:
|
||||
append base p == append base (subpath.normalise p)
|
||||
- Don't change the path according to `realpath`:
|
||||
$(realpath ${p}) == $(realpath ${subpath.normalise p})
|
||||
- Only error on [invalid subpaths](#function-library-lib.path.subpath.isValid):
|
||||
builtins.tryEval (subpath.normalise p)).success == subpath.isValid p
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
# limit repeating `/` to a single one
|
||||
subpath.normalise "foo//bar"
|
||||
=> "./foo/bar"
|
||||
# remove redundant `.` components
|
||||
subpath.normalise "foo/./bar"
|
||||
=> "./foo/bar"
|
||||
# add leading `./`
|
||||
subpath.normalise "foo/bar"
|
||||
=> "./foo/bar"
|
||||
# remove trailing `/`
|
||||
subpath.normalise "foo/bar/"
|
||||
=> "./foo/bar"
|
||||
# remove trailing `/.`
|
||||
subpath.normalise "foo/bar/."
|
||||
=> "./foo/bar"
|
||||
# Return the current directory as `./.`
|
||||
subpath.normalise "."
|
||||
=> "./."
|
||||
# error on `..` path components
|
||||
subpath.normalise "foo/../bar"
|
||||
=> <error>
|
||||
# error on empty string
|
||||
subpath.normalise ""
|
||||
=> <error>
|
||||
# error on absolute path
|
||||
subpath.normalise "/foo"
|
||||
=> <error>
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
subpath.normalise :: String -> String
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [subpath] The subpath string to normalise
|
||||
|
||||
*/
|
||||
subpath.normalise =
|
||||
# The subpath string to normalise
|
||||
subpath:
|
||||
assert assertMsg (isValid subpath) ''
|
||||
lib.path.subpath.normalise: Argument is not a valid subpath string:
|
||||
${subpathInvalidReason subpath}'';
|
||||
joinRelPath (splitRelPath subpath);
|
||||
|
||||
}
|
42
pesto/test_data/assets/path/tests/default.nix
Normal file
42
pesto/test_data/assets/path/tests/default.nix
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
nixpkgs ? ../../..,
|
||||
system ? builtins.currentSystem,
|
||||
pkgs ? import nixpkgs {
|
||||
config = {};
|
||||
overlays = [];
|
||||
inherit system;
|
||||
},
|
||||
libpath ? ../..,
|
||||
# Random seed
|
||||
seed ? null,
|
||||
}:
|
||||
pkgs.runCommand "lib-path-tests" {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
nix
|
||||
jq
|
||||
bc
|
||||
];
|
||||
} ''
|
||||
# Needed to make Nix evaluation work
|
||||
export TEST_ROOT=$(pwd)/test-tmp
|
||||
export NIX_BUILD_HOOK=
|
||||
export NIX_CONF_DIR=$TEST_ROOT/etc
|
||||
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
|
||||
export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
|
||||
export NIX_STATE_DIR=$TEST_ROOT/var/nix
|
||||
export NIX_STORE_DIR=$TEST_ROOT/store
|
||||
export PAGER=cat
|
||||
|
||||
cp -r ${libpath} lib
|
||||
export TEST_LIB=$PWD/lib
|
||||
|
||||
echo "Running unit tests lib/path/tests/unit.nix"
|
||||
nix-instantiate --eval --show-trace \
|
||||
--argstr libpath "$TEST_LIB" \
|
||||
lib/path/tests/unit.nix
|
||||
|
||||
echo "Running property tests lib/path/tests/prop.sh"
|
||||
bash lib/path/tests/prop.sh ${toString seed}
|
||||
|
||||
touch $out
|
||||
''
|
60
pesto/test_data/assets/path/tests/prop.nix
Normal file
60
pesto/test_data/assets/path/tests/prop.nix
Normal file
@ -0,0 +1,60 @@
|
||||
# Given a list of path-like strings, check some properties of the path library
|
||||
# using those paths and return a list of attribute sets of the following form:
|
||||
#
|
||||
# { <string> = <lib.path.subpath.normalise string>; }
|
||||
#
|
||||
# If `normalise` fails to evaluate, the attribute value is set to `""`.
|
||||
# If not, the resulting value is normalised again and an appropriate attribute set added to the output list.
|
||||
{
|
||||
# The path to the nixpkgs lib to use
|
||||
libpath,
|
||||
# A flat directory containing files with randomly-generated
|
||||
# path-like values
|
||||
dir,
|
||||
}:
|
||||
let
|
||||
lib = import libpath;
|
||||
|
||||
# read each file into a string
|
||||
strings = map (name:
|
||||
builtins.readFile (dir + "/${name}")
|
||||
) (builtins.attrNames (builtins.readDir dir));
|
||||
|
||||
inherit (lib.path.subpath) normalise isValid;
|
||||
inherit (lib.asserts) assertMsg;
|
||||
|
||||
normaliseAndCheck = str:
|
||||
let
|
||||
originalValid = isValid str;
|
||||
|
||||
tryOnce = builtins.tryEval (normalise str);
|
||||
tryTwice = builtins.tryEval (normalise tryOnce.value);
|
||||
|
||||
absConcatOrig = /. + ("/" + str);
|
||||
absConcatNormalised = /. + ("/" + tryOnce.value);
|
||||
in
|
||||
# Check the lib.path.subpath.normalise property to only error on invalid subpaths
|
||||
assert assertMsg
|
||||
(originalValid -> tryOnce.success)
|
||||
"Even though string \"${str}\" is valid as a subpath, the normalisation for it failed";
|
||||
assert assertMsg
|
||||
(! originalValid -> ! tryOnce.success)
|
||||
"Even though string \"${str}\" is invalid as a subpath, the normalisation for it succeeded";
|
||||
|
||||
# Check normalisation idempotency
|
||||
assert assertMsg
|
||||
(originalValid -> tryTwice.success)
|
||||
"For valid subpath \"${str}\", the normalisation \"${tryOnce.value}\" was not a valid subpath";
|
||||
assert assertMsg
|
||||
(originalValid -> tryOnce.value == tryTwice.value)
|
||||
"For valid subpath \"${str}\", normalising it once gives \"${tryOnce.value}\" but normalising it twice gives a different result: \"${tryTwice.value}\"";
|
||||
|
||||
# Check that normalisation doesn't change a string when appended to an absolute Nix path value
|
||||
assert assertMsg
|
||||
(originalValid -> absConcatOrig == absConcatNormalised)
|
||||
"For valid subpath \"${str}\", appending to an absolute Nix path value gives \"${absConcatOrig}\", but appending the normalised result \"${tryOnce.value}\" gives a different value \"${absConcatNormalised}\"";
|
||||
|
||||
# Return an empty string when failed
|
||||
if tryOnce.success then tryOnce.value else "";
|
||||
|
||||
in lib.genAttrs strings normaliseAndCheck
|
257
pesto/test_data/assets/path/tests/unit.nix
Normal file
257
pesto/test_data/assets/path/tests/unit.nix
Normal file
@ -0,0 +1,257 @@
|
||||
# Unit tests for lib.path functions. Use `nix-build` in this directory to
|
||||
# run these
|
||||
{ libpath }:
|
||||
let
|
||||
lib = import libpath;
|
||||
inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;
|
||||
|
||||
cases = lib.runTests {
|
||||
# Test examples from the lib.path.append documentation
|
||||
testAppendExample1 = {
|
||||
expr = append /foo "bar/baz";
|
||||
expected = /foo/bar/baz;
|
||||
};
|
||||
testAppendExample2 = {
|
||||
expr = append /foo "./bar//baz/./";
|
||||
expected = /foo/bar/baz;
|
||||
};
|
||||
testAppendExample3 = {
|
||||
expr = append /. "foo/bar";
|
||||
expected = /foo/bar;
|
||||
};
|
||||
testAppendExample4 = {
|
||||
expr = (builtins.tryEval (append "/foo" "bar")).success;
|
||||
expected = false;
|
||||
};
|
||||
testAppendExample5 = {
|
||||
expr = (builtins.tryEval (append /foo /bar)).success;
|
||||
expected = false;
|
||||
};
|
||||
testAppendExample6 = {
|
||||
expr = (builtins.tryEval (append /foo "")).success;
|
||||
expected = false;
|
||||
};
|
||||
testAppendExample7 = {
|
||||
expr = (builtins.tryEval (append /foo "/bar")).success;
|
||||
expected = false;
|
||||
};
|
||||
testAppendExample8 = {
|
||||
expr = (builtins.tryEval (append /foo "../bar")).success;
|
||||
expected = false;
|
||||
};
|
||||
|
||||
testHasPrefixExample1 = {
|
||||
expr = hasPrefix /foo /foo/bar;
|
||||
expected = true;
|
||||
};
|
||||
testHasPrefixExample2 = {
|
||||
expr = hasPrefix /foo /foo;
|
||||
expected = true;
|
||||
};
|
||||
testHasPrefixExample3 = {
|
||||
expr = hasPrefix /foo/bar /foo;
|
||||
expected = false;
|
||||
};
|
||||
testHasPrefixExample4 = {
|
||||
expr = hasPrefix /. /foo;
|
||||
expected = true;
|
||||
};
|
||||
|
||||
testRemovePrefixExample1 = {
|
||||
expr = removePrefix /foo /foo/bar/baz;
|
||||
expected = "./bar/baz";
|
||||
};
|
||||
testRemovePrefixExample2 = {
|
||||
expr = removePrefix /foo /foo;
|
||||
expected = "./.";
|
||||
};
|
||||
testRemovePrefixExample3 = {
|
||||
expr = (builtins.tryEval (removePrefix /foo/bar /foo)).success;
|
||||
expected = false;
|
||||
};
|
||||
testRemovePrefixExample4 = {
|
||||
expr = removePrefix /. /foo;
|
||||
expected = "./foo";
|
||||
};
|
||||
|
||||
testSplitRootExample1 = {
|
||||
expr = splitRoot /foo/bar;
|
||||
expected = { root = /.; subpath = "./foo/bar"; };
|
||||
};
|
||||
testSplitRootExample2 = {
|
||||
expr = splitRoot /.;
|
||||
expected = { root = /.; subpath = "./."; };
|
||||
};
|
||||
testSplitRootExample3 = {
|
||||
expr = splitRoot /foo/../bar;
|
||||
expected = { root = /.; subpath = "./bar"; };
|
||||
};
|
||||
testSplitRootExample4 = {
|
||||
expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
|
||||
expected = false;
|
||||
};
|
||||
|
||||
# Test examples from the lib.path.subpath.isValid documentation
|
||||
testSubpathIsValidExample1 = {
|
||||
expr = subpath.isValid null;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathIsValidExample2 = {
|
||||
expr = subpath.isValid "";
|
||||
expected = false;
|
||||
};
|
||||
testSubpathIsValidExample3 = {
|
||||
expr = subpath.isValid "/foo";
|
||||
expected = false;
|
||||
};
|
||||
testSubpathIsValidExample4 = {
|
||||
expr = subpath.isValid "../foo";
|
||||
expected = false;
|
||||
};
|
||||
testSubpathIsValidExample5 = {
|
||||
expr = subpath.isValid "foo/bar";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidExample6 = {
|
||||
expr = subpath.isValid "./foo//bar/";
|
||||
expected = true;
|
||||
};
|
||||
# Some extra tests
|
||||
testSubpathIsValidTwoDotsEnd = {
|
||||
expr = subpath.isValid "foo/..";
|
||||
expected = false;
|
||||
};
|
||||
testSubpathIsValidTwoDotsMiddle = {
|
||||
expr = subpath.isValid "foo/../bar";
|
||||
expected = false;
|
||||
};
|
||||
testSubpathIsValidTwoDotsPrefix = {
|
||||
expr = subpath.isValid "..foo";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidTwoDotsSuffix = {
|
||||
expr = subpath.isValid "foo..";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidTwoDotsPrefixComponent = {
|
||||
expr = subpath.isValid "foo/..bar/baz";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidTwoDotsSuffixComponent = {
|
||||
expr = subpath.isValid "foo/bar../baz";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidThreeDots = {
|
||||
expr = subpath.isValid "...";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidFourDots = {
|
||||
expr = subpath.isValid "....";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidThreeDotsComponent = {
|
||||
expr = subpath.isValid "foo/.../bar";
|
||||
expected = true;
|
||||
};
|
||||
testSubpathIsValidFourDotsComponent = {
|
||||
expr = subpath.isValid "foo/..../bar";
|
||||
expected = true;
|
||||
};
|
||||
|
||||
# Test examples from the lib.path.subpath.join documentation
|
||||
testSubpathJoinExample1 = {
|
||||
expr = subpath.join [ "foo" "bar/baz" ];
|
||||
expected = "./foo/bar/baz";
|
||||
};
|
||||
testSubpathJoinExample2 = {
|
||||
expr = subpath.join [ "./foo" "." "bar//./baz/" ];
|
||||
expected = "./foo/bar/baz";
|
||||
};
|
||||
testSubpathJoinExample3 = {
|
||||
expr = subpath.join [ ];
|
||||
expected = "./.";
|
||||
};
|
||||
testSubpathJoinExample4 = {
|
||||
expr = (builtins.tryEval (subpath.join [ /foo ])).success;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathJoinExample5 = {
|
||||
expr = (builtins.tryEval (subpath.join [ "" ])).success;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathJoinExample6 = {
|
||||
expr = (builtins.tryEval (subpath.join [ "/foo" ])).success;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathJoinExample7 = {
|
||||
expr = (builtins.tryEval (subpath.join [ "../foo" ])).success;
|
||||
expected = false;
|
||||
};
|
||||
|
||||
# Test examples from the lib.path.subpath.normalise documentation
|
||||
testSubpathNormaliseExample1 = {
|
||||
expr = subpath.normalise "foo//bar";
|
||||
expected = "./foo/bar";
|
||||
};
|
||||
testSubpathNormaliseExample2 = {
|
||||
expr = subpath.normalise "foo/./bar";
|
||||
expected = "./foo/bar";
|
||||
};
|
||||
testSubpathNormaliseExample3 = {
|
||||
expr = subpath.normalise "foo/bar";
|
||||
expected = "./foo/bar";
|
||||
};
|
||||
testSubpathNormaliseExample4 = {
|
||||
expr = subpath.normalise "foo/bar/";
|
||||
expected = "./foo/bar";
|
||||
};
|
||||
testSubpathNormaliseExample5 = {
|
||||
expr = subpath.normalise "foo/bar/.";
|
||||
expected = "./foo/bar";
|
||||
};
|
||||
testSubpathNormaliseExample6 = {
|
||||
expr = subpath.normalise ".";
|
||||
expected = "./.";
|
||||
};
|
||||
testSubpathNormaliseExample7 = {
|
||||
expr = (builtins.tryEval (subpath.normalise "foo/../bar")).success;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathNormaliseExample8 = {
|
||||
expr = (builtins.tryEval (subpath.normalise "")).success;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathNormaliseExample9 = {
|
||||
expr = (builtins.tryEval (subpath.normalise "/foo")).success;
|
||||
expected = false;
|
||||
};
|
||||
# Some extra tests
|
||||
testSubpathNormaliseIsValidDots = {
|
||||
expr = subpath.normalise "./foo/.bar/.../baz...qux";
|
||||
expected = "./foo/.bar/.../baz...qux";
|
||||
};
|
||||
testSubpathNormaliseWrongType = {
|
||||
expr = (builtins.tryEval (subpath.normalise null)).success;
|
||||
expected = false;
|
||||
};
|
||||
testSubpathNormaliseTwoDots = {
|
||||
expr = (builtins.tryEval (subpath.normalise "..")).success;
|
||||
expected = false;
|
||||
};
|
||||
|
||||
testSubpathComponentsExample1 = {
|
||||
expr = subpath.components ".";
|
||||
expected = [ ];
|
||||
};
|
||||
testSubpathComponentsExample2 = {
|
||||
expr = subpath.components "./foo//bar/./baz/";
|
||||
expected = [ "foo" "bar" "baz" ];
|
||||
};
|
||||
testSubpathComponentsExample3 = {
|
||||
expr = (builtins.tryEval (subpath.components "/foo")).success;
|
||||
expected = false;
|
||||
};
|
||||
};
|
||||
in
|
||||
if cases == [] then "Unit tests successful"
|
||||
else throw "Path unit tests failed: ${lib.generators.toPretty {} cases}"
|
19
pesto/test_data/assets/source-types.nix
Normal file
19
pesto/test_data/assets/source-types.nix
Normal file
@ -0,0 +1,19 @@
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
defaultSourceType = tname: {
|
||||
shortName = tname;
|
||||
isSource = false;
|
||||
};
|
||||
in lib.mapAttrs (tname: tset: defaultSourceType tname // tset) {
|
||||
|
||||
fromSource = {
|
||||
isSource = true;
|
||||
};
|
||||
|
||||
binaryNativeCode = {};
|
||||
|
||||
binaryBytecode = {};
|
||||
|
||||
binaryFirmware = {};
|
||||
}
|
346
pesto/test_data/assets/sources.nix
Normal file
346
pesto/test_data/assets/sources.nix
Normal file
@ -0,0 +1,346 @@
|
||||
# Functions for copying sources to the Nix store.
|
||||
{ lib }:
|
||||
|
||||
# Tested in lib/tests/sources.sh
|
||||
let
|
||||
inherit (builtins)
|
||||
match
|
||||
split
|
||||
storeDir
|
||||
;
|
||||
inherit (lib)
|
||||
boolToString
|
||||
filter
|
||||
isString
|
||||
readFile
|
||||
;
|
||||
inherit (lib.filesystem)
|
||||
pathIsRegularFile
|
||||
;
|
||||
|
||||
/**
|
||||
A basic filter for `cleanSourceWith` that removes
|
||||
directories of version control system, backup files (*~)
|
||||
and some generated files.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [name]
|
||||
- [type]
|
||||
|
||||
*/
|
||||
cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! (
|
||||
# Filter out version control software files/directories
|
||||
(baseName == ".git" || type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) ||
|
||||
# Filter out editor backup / swap files.
|
||||
lib.hasSuffix "~" baseName ||
|
||||
match "^\\.sw[a-z]$" baseName != null ||
|
||||
match "^\\..*\\.sw[a-z]$" baseName != null ||
|
||||
|
||||
# Filter out generates files.
|
||||
lib.hasSuffix ".o" baseName ||
|
||||
lib.hasSuffix ".so" baseName ||
|
||||
# Filter out nix-build result symlinks
|
||||
(type == "symlink" && lib.hasPrefix "result" baseName) ||
|
||||
# Filter out sockets and other types of files we can't have in the store.
|
||||
(type == "unknown")
|
||||
);
|
||||
|
||||
/**
|
||||
Filters a source tree removing version control files and directories using cleanSourceFilter.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
cleanSource ./.
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [src]
|
||||
|
||||
*/
|
||||
cleanSource = src: cleanSourceWith { filter = cleanSourceFilter; inherit src; };
|
||||
|
||||
/**
|
||||
Like `builtins.filterSource`, except it will compose with itself,
|
||||
allowing you to chain multiple calls together without any
|
||||
intermediate copies being put in the nix store.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
lib.cleanSourceWith {
|
||||
filter = f;
|
||||
src = lib.cleanSourceWith {
|
||||
filter = g;
|
||||
src = ./.;
|
||||
};
|
||||
}
|
||||
# Succeeds!
|
||||
builtins.filterSource f (builtins.filterSource g ./.)
|
||||
# Fails!
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
|
||||
*/
|
||||
cleanSourceWith =
|
||||
{
|
||||
# A path or cleanSourceWith result to filter and/or rename.
|
||||
src,
|
||||
# Optional with default value: constant true (include everything)
|
||||
# The function will be combined with the && operator such
|
||||
# that src.filter is called lazily.
|
||||
# For implementing a filter, see
|
||||
# https://nixos.org/nix/manual/#builtin-filterSource
|
||||
# Type: A function (path -> type -> bool)
|
||||
filter ? _path: _type: true,
|
||||
# Optional name to use as part of the store path.
|
||||
# This defaults to `src.name` or otherwise `"source"`.
|
||||
name ? null
|
||||
}:
|
||||
let
|
||||
orig = toSourceAttributes src;
|
||||
in fromSourceAttributes {
|
||||
inherit (orig) origSrc;
|
||||
filter = path: type: filter path type && orig.filter path type;
|
||||
name = if name != null then name else orig.name;
|
||||
};
|
||||
|
||||
/**
|
||||
Add logging to a source, for troubleshooting the filtering behavior.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
sources.trace :: sourceLike -> Source
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [src] Source to debug. The returned source will behave like this source, but also log its filter invocations.
|
||||
|
||||
*/
|
||||
trace =
|
||||
# Source to debug. The returned source will behave like this source, but also log its filter invocations.
|
||||
src:
|
||||
let
|
||||
attrs = toSourceAttributes src;
|
||||
in
|
||||
fromSourceAttributes (
|
||||
attrs // {
|
||||
filter = path: type:
|
||||
let
|
||||
r = attrs.filter path type;
|
||||
in
|
||||
builtins.trace "${attrs.name}.filter ${path} = ${boolToString r}" r;
|
||||
}
|
||||
) // {
|
||||
satisfiesSubpathInvariant = src ? satisfiesSubpathInvariant && src.satisfiesSubpathInvariant;
|
||||
};
|
||||
|
||||
/**
|
||||
Filter sources by a list of regular expressions.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [src]
|
||||
- [regexes]
|
||||
|
||||
*/
|
||||
sourceByRegex = src: regexes:
|
||||
let
|
||||
isFiltered = src ? _isLibCleanSourceWith;
|
||||
origSrc = if isFiltered then src.origSrc else src;
|
||||
in lib.cleanSourceWith {
|
||||
filter = (path: type:
|
||||
let relPath = lib.removePrefix (toString origSrc + "/") (toString path);
|
||||
in lib.any (re: match re relPath != null) regexes);
|
||||
inherit src;
|
||||
};
|
||||
|
||||
/**
|
||||
Get all files ending with the specified suffices from the given
|
||||
source directory or its descendants, omitting files that do not match
|
||||
any suffix. The result of the example below will include files like
|
||||
`./dir/module.c` and `./dir/subdir/doc.xml` if present.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
sourceFilesBySuffices ./. [ ".xml" ".c" ]
|
||||
```
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
sourceLike -> [String] -> Source
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [src] Path or source containing the files to be returned
|
||||
- [exts] A list of file suffix strings
|
||||
|
||||
*/
|
||||
sourceFilesBySuffices =
|
||||
# Path or source containing the files to be returned
|
||||
src:
|
||||
# A list of file suffix strings
|
||||
exts:
|
||||
let filter = name: type:
|
||||
let base = baseNameOf (toString name);
|
||||
in type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts;
|
||||
in cleanSourceWith { inherit filter src; };
|
||||
|
||||
pathIsGitRepo = path: (_commitIdFromGitRepoOrError path)?value;
|
||||
|
||||
/**
|
||||
Get the commit id of a git repo.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
commitIdFromGitRepo <nixpkgs/.git>
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [path]
|
||||
|
||||
*/
|
||||
commitIdFromGitRepo = path:
|
||||
let commitIdOrError = _commitIdFromGitRepoOrError path;
|
||||
in commitIdOrError.value or (throw commitIdOrError.error);
|
||||
|
||||
# Get the commit id of a git repo.
|
||||
|
||||
# Returns `{ value = commitHash }` or `{ error = "... message ..." }`.
|
||||
|
||||
# Example: commitIdFromGitRepo <nixpkgs/.git>
|
||||
# not exported, used for commitIdFromGitRepo
|
||||
_commitIdFromGitRepoOrError =
|
||||
let readCommitFromFile = file: path:
|
||||
let fileName = path + "/${file}";
|
||||
packedRefsName = path + "/packed-refs";
|
||||
absolutePath = base: path:
|
||||
if lib.hasPrefix "/" path
|
||||
then path
|
||||
else toString (/. + "${base}/${path}");
|
||||
in if pathIsRegularFile path
|
||||
# Resolve git worktrees. See gitrepository-layout(5)
|
||||
then
|
||||
let m = match "^gitdir: (.*)$" (lib.fileContents path);
|
||||
in if m == null
|
||||
then { error = "File contains no gitdir reference: " + path; }
|
||||
else
|
||||
let gitDir = absolutePath (dirOf path) (lib.head m);
|
||||
commonDir'' = if pathIsRegularFile "${gitDir}/commondir"
|
||||
then lib.fileContents "${gitDir}/commondir"
|
||||
else gitDir;
|
||||
commonDir' = lib.removeSuffix "/" commonDir'';
|
||||
commonDir = absolutePath gitDir commonDir';
|
||||
refFile = lib.removePrefix "${commonDir}/" "${gitDir}/${file}";
|
||||
in readCommitFromFile refFile commonDir
|
||||
|
||||
else if pathIsRegularFile fileName
|
||||
# Sometimes git stores the commitId directly in the file but
|
||||
# sometimes it stores something like: «ref: refs/heads/branch-name»
|
||||
then
|
||||
let fileContent = lib.fileContents fileName;
|
||||
matchRef = match "^ref: (.*)$" fileContent;
|
||||
in if matchRef == null
|
||||
then { value = fileContent; }
|
||||
else readCommitFromFile (lib.head matchRef) path
|
||||
|
||||
else if pathIsRegularFile packedRefsName
|
||||
# Sometimes, the file isn't there at all and has been packed away in the
|
||||
# packed-refs file, so we have to grep through it:
|
||||
then
|
||||
let fileContent = readFile packedRefsName;
|
||||
matchRef = match "([a-z0-9]+) ${file}";
|
||||
isRef = s: isString s && (matchRef s) != null;
|
||||
# there is a bug in libstdc++ leading to stackoverflow for long strings:
|
||||
# https://github.com/NixOS/nix/issues/2147#issuecomment-659868795
|
||||
refs = filter isRef (split "\n" fileContent);
|
||||
in if refs == []
|
||||
then { error = "Could not find " + file + " in " + packedRefsName; }
|
||||
else { value = lib.head (matchRef (lib.head refs)); }
|
||||
|
||||
else { error = "Not a .git directory: " + toString path; };
|
||||
in readCommitFromFile "HEAD";
|
||||
|
||||
pathHasContext = builtins.hasContext or (lib.hasPrefix storeDir);
|
||||
|
||||
canCleanSource = src: src ? _isLibCleanSourceWith || !(pathHasContext (toString src));
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Internal functions
|
||||
#
|
||||
|
||||
# toSourceAttributes : sourceLike -> SourceAttrs
|
||||
#
|
||||
# Convert any source-like object into a simple, singular representation.
|
||||
# We don't expose this representation in order to avoid having a fifth path-
|
||||
# like class of objects in the wild.
|
||||
# (Existing ones being: paths, strings, sources and x//{outPath})
|
||||
# So instead of exposing internals, we build a library of combinator functions.
|
||||
toSourceAttributes = src:
|
||||
let
|
||||
isFiltered = src ? _isLibCleanSourceWith;
|
||||
in
|
||||
{
|
||||
# The original path
|
||||
origSrc = if isFiltered then src.origSrc else src;
|
||||
filter = if isFiltered then src.filter else _: _: true;
|
||||
name = if isFiltered then src.name else "source";
|
||||
};
|
||||
|
||||
# fromSourceAttributes : SourceAttrs -> Source
|
||||
#
|
||||
# Inverse of toSourceAttributes for Source objects.
|
||||
fromSourceAttributes = { origSrc, filter, name }:
|
||||
{
|
||||
_isLibCleanSourceWith = true;
|
||||
inherit origSrc filter name;
|
||||
outPath = builtins.path { inherit filter name; path = origSrc; };
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
pathType = lib.warnIf (lib.isInOldestRelease 2305)
|
||||
"lib.sources.pathType has been moved to lib.filesystem.pathType."
|
||||
lib.filesystem.pathType;
|
||||
|
||||
pathIsDirectory = lib.warnIf (lib.isInOldestRelease 2305)
|
||||
"lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory."
|
||||
lib.filesystem.pathIsDirectory;
|
||||
|
||||
pathIsRegularFile = lib.warnIf (lib.isInOldestRelease 2305)
|
||||
"lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile."
|
||||
lib.filesystem.pathIsRegularFile;
|
||||
|
||||
inherit
|
||||
pathIsGitRepo
|
||||
commitIdFromGitRepo
|
||||
|
||||
cleanSource
|
||||
cleanSourceWith
|
||||
cleanSourceFilter
|
||||
pathHasContext
|
||||
canCleanSource
|
||||
|
||||
sourceByRegex
|
||||
sourceFilesBySuffices
|
||||
|
||||
trace
|
||||
;
|
||||
}
|
83
pesto/test_data/assets/strings-with-deps.nix
Normal file
83
pesto/test_data/assets/strings-with-deps.nix
Normal file
@ -0,0 +1,83 @@
|
||||
{ lib }:
|
||||
/**
|
||||
Usage:
|
||||
You define you custom builder script by adding all build steps to a list.
|
||||
for example:
|
||||
builder = writeScript "fsg-4.4-builder"
|
||||
(textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]);
|
||||
a step is defined by noDepEntry, fullDepEntry or packEntry.
|
||||
To ensure that prerequisite are met those are added before the task itself by
|
||||
textClosureDupList. Duplicated items are removed again.
|
||||
See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps
|
||||
Attention:
|
||||
let
|
||||
pkgs = (import <nixpkgs>) {};
|
||||
in let
|
||||
inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap;
|
||||
inherit (pkgs.lib) id;
|
||||
nameA = noDepEntry "Text a";
|
||||
nameB = fullDepEntry "Text b" ["nameA"];
|
||||
nameC = fullDepEntry "Text c" ["nameA"];
|
||||
stages = {
|
||||
nameHeader = noDepEntry "#! /bin/sh \n";
|
||||
inherit nameA nameB nameC;
|
||||
};
|
||||
in
|
||||
textClosureMap id stages
|
||||
[ "nameHeader" "nameA" "nameB" "nameC"
|
||||
nameC # <- added twice. add a dep entry if you know that it will be added once only [1]
|
||||
"nameB" # <- this will not be added again because the attr name (reference) is used
|
||||
]
|
||||
# result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[])
|
||||
[1] maybe this behaviour should be removed to keep things simple (?)
|
||||
*/
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
concatStringsSep
|
||||
head
|
||||
isAttrs
|
||||
listToAttrs
|
||||
tail
|
||||
;
|
||||
in
|
||||
rec {
|
||||
|
||||
/**
|
||||
!!! The interface of this function is kind of messed up, since
|
||||
it's way too overloaded and almost but not quite computes a
|
||||
topological sort of the depstrings.
|
||||
|
||||
# Arguments
|
||||
|
||||
- [predefined]
|
||||
- [arg]
|
||||
|
||||
*/
|
||||
|
||||
textClosureList = predefined: arg:
|
||||
let
|
||||
f = done: todo:
|
||||
if todo == [] then {result = []; inherit done;}
|
||||
else
|
||||
let entry = head todo; in
|
||||
if isAttrs entry then
|
||||
let x = f done entry.deps;
|
||||
y = f x.done (tail todo);
|
||||
in { result = x.result ++ [entry.text] ++ y.result;
|
||||
done = y.done;
|
||||
}
|
||||
else if done ? ${entry} then f done (tail todo)
|
||||
else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
|
||||
in (f {} arg).result;
|
||||
|
||||
textClosureMap = f: predefined: names:
|
||||
concatStringsSep "\n" (map f (textClosureList predefined names));
|
||||
|
||||
noDepEntry = text: {inherit text; deps = [];};
|
||||
fullDepEntry = text: deps: {inherit text deps;};
|
||||
packEntry = deps: {inherit deps; text="";};
|
||||
|
||||
stringAfter = deps: text: { inherit text deps; };
|
||||
|
||||
}
|
2044
pesto/test_data/assets/strings.nix
Normal file
2044
pesto/test_data/assets/strings.nix
Normal file
File diff suppressed because it is too large
Load Diff
136
pesto/test_data/assets/systems/architectures.nix
Normal file
136
pesto/test_data/assets/systems/architectures.nix
Normal file
@ -0,0 +1,136 @@
|
||||
{ lib }:
|
||||
|
||||
rec {
|
||||
# gcc.arch to its features (as in /proc/cpuinfo)
|
||||
features = {
|
||||
# x86_64 Generic
|
||||
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
|
||||
default = [ ];
|
||||
x86-64 = [ ];
|
||||
x86-64-v2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
||||
x86-64-v3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
|
||||
x86-64-v4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "avx512" "fma" ];
|
||||
# x86_64 Intel
|
||||
nehalem = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ];
|
||||
westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ];
|
||||
sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
||||
ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
||||
haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
skylake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
skylake-avx512 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
cannonlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
icelake-client = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
icelake-server = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
cascadelake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
cooperlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
tigerlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
alderlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
# x86_64 AMD
|
||||
btver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
||||
btver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
||||
bdver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
||||
bdver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
||||
bdver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
||||
bdver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" "fma4" ];
|
||||
znver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
||||
znver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
||||
znver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
||||
znver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
# other
|
||||
armv5te = [ ];
|
||||
armv6 = [ ];
|
||||
armv7-a = [ ];
|
||||
armv8-a = [ ];
|
||||
mips32 = [ ];
|
||||
loongson2f = [ ];
|
||||
};
|
||||
|
||||
# a superior CPU has all the features of an inferior and is able to build and test code for it
|
||||
inferiors = {
|
||||
# x86_64 Generic
|
||||
default = [ ];
|
||||
x86-64 = [ ];
|
||||
x86-64-v2 = [ "x86-64" ];
|
||||
x86-64-v3 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
|
||||
x86-64-v4 = [ "x86-64-v3" ] ++ inferiors.x86-64-v3;
|
||||
|
||||
# x86_64 Intel
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
||||
nehalem = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
|
||||
westmere = [ "nehalem" ] ++ inferiors.nehalem;
|
||||
sandybridge = [ "westmere" ] ++ inferiors.westmere;
|
||||
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
|
||||
|
||||
haswell = lib.unique ([ "ivybridge" "x86-64-v3" ] ++ inferiors.ivybridge ++ inferiors.x86-64-v3);
|
||||
broadwell = [ "haswell" ] ++ inferiors.haswell;
|
||||
skylake = [ "broadwell" ] ++ inferiors.broadwell;
|
||||
|
||||
skylake-avx512 = lib.unique ([ "skylake" "x86-64-v4" ] ++ inferiors.skylake ++ inferiors.x86-64-v4);
|
||||
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
|
||||
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
|
||||
cascadelake = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||
cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake;
|
||||
tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server;
|
||||
|
||||
# CX16 does not exist on alderlake, while it does on nearly all other intel CPUs
|
||||
alderlake = [ ];
|
||||
|
||||
# x86_64 AMD
|
||||
# TODO: fill this (need testing)
|
||||
btver1 = [ ];
|
||||
btver2 = [ ];
|
||||
bdver1 = [ ];
|
||||
bdver2 = [ ];
|
||||
bdver3 = [ ];
|
||||
bdver4 = [ ];
|
||||
# Regarding `skylake` as inferior of `znver1`, there are reports of
|
||||
# successful usage by Gentoo users and Phoronix benchmarking of different
|
||||
# `-march` targets.
|
||||
#
|
||||
# The GCC documentation on extensions used and wikichip documentation
|
||||
# regarding supperted extensions on znver1 and skylake was used to create
|
||||
# this partial order.
|
||||
#
|
||||
# Note:
|
||||
#
|
||||
# - The successors of `skylake` (`cannonlake`, `icelake`, etc) use `avx512`
|
||||
# which no current AMD Zen michroarch support.
|
||||
# - `znver1` uses `ABM`, `CLZERO`, `CX16`, `MWAITX`, and `SSE4A` which no
|
||||
# current Intel microarch support.
|
||||
#
|
||||
# https://www.phoronix.com/scan.php?page=article&item=amd-znver3-gcc11&num=1
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
||||
# https://en.wikichip.org/wiki/amd/microarchitectures/zen
|
||||
# https://en.wikichip.org/wiki/intel/microarchitectures/skylake
|
||||
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
|
||||
znver2 = [ "znver1" ] ++ inferiors.znver1;
|
||||
znver3 = [ "znver2" ] ++ inferiors.znver2;
|
||||
znver4 = lib.unique ([ "znver3" "x86-64-v4" ] ++ inferiors.znver3 ++ inferiors.x86-64-v4);
|
||||
|
||||
# other
|
||||
armv5te = [ ];
|
||||
armv6 = [ ];
|
||||
armv7-a = [ ];
|
||||
armv8-a = [ ];
|
||||
mips32 = [ ];
|
||||
loongson2f = [ ];
|
||||
};
|
||||
|
||||
predicates = let
|
||||
featureSupport = feature: x: builtins.elem feature features.${x} or [];
|
||||
in {
|
||||
sse3Support = featureSupport "sse3";
|
||||
ssse3Support = featureSupport "ssse3";
|
||||
sse4_1Support = featureSupport "sse4_1";
|
||||
sse4_2Support = featureSupport "sse4_2";
|
||||
sse4_aSupport = featureSupport "sse4a";
|
||||
avxSupport = featureSupport "avx";
|
||||
avx2Support = featureSupport "avx2";
|
||||
avx512Support = featureSupport "avx512";
|
||||
aesSupport = featureSupport "aes";
|
||||
fmaSupport = featureSupport "fma";
|
||||
fma4Support = featureSupport "fma4";
|
||||
};
|
||||
}
|
272
pesto/test_data/assets/systems/default.nix
Normal file
272
pesto/test_data/assets/systems/default.nix
Normal file
@ -0,0 +1,272 @@
|
||||
{ lib }:
|
||||
let inherit (lib.attrsets) mapAttrs; in
|
||||
|
||||
rec {
|
||||
doubles = import ./doubles.nix { inherit lib; };
|
||||
parse = import ./parse.nix { inherit lib; };
|
||||
inspect = import ./inspect.nix { inherit lib; };
|
||||
platforms = import ./platforms.nix { inherit lib; };
|
||||
examples = import ./examples.nix { inherit lib; };
|
||||
architectures = import ./architectures.nix { inherit lib; };
|
||||
|
||||
/**
|
||||
Elaborated systems contain functions, which means that they don't satisfy
|
||||
`==` for a lack of reflexivity.
|
||||
They might *appear* to satisfy `==` reflexivity when the same exact value is
|
||||
compared to itself, because object identity is used as an "optimization";
|
||||
compare the value with a reconstruction of itself, e.g. with `f == a: f a`,
|
||||
or perhaps calling `elaborate` twice, and one will see reflexivity fail as described.
|
||||
Hence a custom equality test.
|
||||
Note that this does not canonicalize the systems, so you'll want to make sure
|
||||
both arguments have been `elaborate`-d.
|
||||
*/
|
||||
equals =
|
||||
let removeFunctions = a: lib.filterAttrs (_: v: !builtins.isFunction v) a;
|
||||
in a: b: removeFunctions a == removeFunctions b;
|
||||
|
||||
/**
|
||||
List of all Nix system doubles the nixpkgs flake will expose the package set
|
||||
for. All systems listed here must be supported by nixpkgs as `localSystem`.
|
||||
**Warning**: This attribute is considered experimental and is subject to change.
|
||||
*/
|
||||
flakeExposed = import ./flake-systems.nix { };
|
||||
|
||||
# Elaborate a `localSystem` or `crossSystem` so that it contains everything
|
||||
# necessary.
|
||||
#
|
||||
# `parsed` is inferred from args, both because there are two options with one
|
||||
# clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
|
||||
# always just used `final.*` would fail on both counts.
|
||||
elaborate = args': let
|
||||
args = if lib.isString args' then { system = args'; }
|
||||
else args';
|
||||
final = {
|
||||
# Prefer to parse `config` as it is strictly more informative.
|
||||
parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
|
||||
# Either of these can be losslessly-extracted from `parsed` iff parsing succeeds.
|
||||
system = parse.doubleFromSystem final.parsed;
|
||||
config = parse.tripleFromSystem final.parsed;
|
||||
# Determine whether we can execute binaries built for the provided platform.
|
||||
canExecute = platform:
|
||||
final.isAndroid == platform.isAndroid &&
|
||||
parse.isCompatible final.parsed.cpu platform.parsed.cpu
|
||||
&& final.parsed.kernel == platform.parsed.kernel;
|
||||
isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
|
||||
# Derived meta-data
|
||||
libc =
|
||||
/**/ if final.isDarwin then "libSystem"
|
||||
else if final.isMinGW then "msvcrt"
|
||||
else if final.isWasi then "wasilibc"
|
||||
else if final.isRedox then "relibc"
|
||||
else if final.isMusl then "musl"
|
||||
else if final.isUClibc then "uclibc"
|
||||
else if final.isAndroid then "bionic"
|
||||
else if final.isLinux /**
|
||||
default
|
||||
*/ then "glibc"
|
||||
else if final.isFreeBSD then "fblibc"
|
||||
else if final.isNetBSD then "nblibc"
|
||||
else if final.isAvr then "avrlibc"
|
||||
else if final.isGhcjs then null
|
||||
else if final.isNone then "newlib"
|
||||
# TODO(@Ericson2314) think more about other operating systems
|
||||
else "native/impure";
|
||||
# Choose what linker we wish to use by default. Someday we might also
|
||||
# choose the C compiler, runtime library, C++ standard library, etc. in
|
||||
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
|
||||
# the monolithic GCC build we cannot actually make those choices
|
||||
# independently, so we are just doing `linker` and keeping `useLLVM` for
|
||||
# now.
|
||||
linker =
|
||||
/**/ if final.useLLVM or false then "lld"
|
||||
else if final.isDarwin then "cctools"
|
||||
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
|
||||
# is why we use the more obscure "bfd" and not "binutils" for this
|
||||
# choice.
|
||||
else "bfd";
|
||||
extensions = lib.optionalAttrs final.hasSharedLibraries {
|
||||
sharedLibrary =
|
||||
if final.isDarwin then ".dylib"
|
||||
else if final.isWindows then ".dll"
|
||||
else ".so";
|
||||
} // {
|
||||
staticLibrary =
|
||||
/**/ if final.isWindows then ".lib"
|
||||
else ".a";
|
||||
library =
|
||||
/**/ if final.isStatic then final.extensions.staticLibrary
|
||||
else final.extensions.sharedLibrary;
|
||||
executable =
|
||||
/**/ if final.isWindows then ".exe"
|
||||
else "";
|
||||
};
|
||||
# Misc boolean options
|
||||
useAndroidPrebuilt = false;
|
||||
useiOSPrebuilt = false;
|
||||
|
||||
# Output from uname
|
||||
uname = {
|
||||
# uname -s
|
||||
system = {
|
||||
linux = "Linux";
|
||||
windows = "Windows";
|
||||
darwin = "Darwin";
|
||||
netbsd = "NetBSD";
|
||||
freebsd = "FreeBSD";
|
||||
openbsd = "OpenBSD";
|
||||
wasi = "Wasi";
|
||||
redox = "Redox";
|
||||
genode = "Genode";
|
||||
}.${final.parsed.kernel.name} or null;
|
||||
|
||||
# uname -m
|
||||
processor =
|
||||
if final.isPower64
|
||||
then "ppc64${lib.optionalString final.isLittleEndian "le"}"
|
||||
else if final.isPower
|
||||
then "ppc${lib.optionalString final.isLittleEndian "le"}"
|
||||
else if final.isMips64
|
||||
then "mips64" # endianness is *not* included on mips64
|
||||
else final.parsed.cpu.name;
|
||||
|
||||
# uname -r
|
||||
release = null;
|
||||
};
|
||||
|
||||
# It is important that hasSharedLibraries==false when the platform has no
|
||||
# dynamic library loader. Various tools (including the gcc build system)
|
||||
# have knowledge of which platforms are incapable of dynamic linking, and
|
||||
# will still build on/for those platforms with --enable-shared, but simply
|
||||
# omit any `.so` build products such as libgcc_s.so. When that happens,
|
||||
# it causes hard-to-troubleshoot build failures.
|
||||
hasSharedLibraries = with final;
|
||||
(isAndroid || isGnu || isMusl # Linux (allows multiple libcs)
|
||||
|| isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs
|
||||
|| isCygwin || isMinGW # Windows
|
||||
) && !isStatic;
|
||||
|
||||
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
|
||||
# addition of the `staticMarker` (see make-derivation.nix). Some
|
||||
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
|
||||
# don't support dynamic linking, but don't get the `staticMarker`.
|
||||
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
|
||||
# has the `staticMarker`.
|
||||
isStatic = final.isWasm || final.isRedox;
|
||||
|
||||
# Just a guess, based on `system`
|
||||
inherit
|
||||
({
|
||||
linux-kernel = args.linux-kernel or {};
|
||||
gcc = args.gcc or {};
|
||||
rustc = args.rustc or {};
|
||||
} // platforms.select final)
|
||||
linux-kernel gcc rustc;
|
||||
|
||||
linuxArch =
|
||||
if final.isAarch32 then "arm"
|
||||
else if final.isAarch64 then "arm64"
|
||||
else if final.isx86_32 then "i386"
|
||||
else if final.isx86_64 then "x86_64"
|
||||
# linux kernel does not distinguish microblaze/microblazeel
|
||||
else if final.isMicroBlaze then "microblaze"
|
||||
else if final.isMips32 then "mips"
|
||||
else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64
|
||||
else if final.isPower then "powerpc"
|
||||
else if final.isRiscV then "riscv"
|
||||
else if final.isS390 then "s390"
|
||||
else if final.isLoongArch64 then "loongarch"
|
||||
else final.parsed.cpu.name;
|
||||
|
||||
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
|
||||
ubootArch =
|
||||
if final.isx86_32 then "x86" # not i386
|
||||
else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64
|
||||
else final.linuxArch; # other cases appear to agree with linuxArch
|
||||
|
||||
qemuArch =
|
||||
if final.isAarch32 then "arm"
|
||||
else if final.isS390 && !final.isS390x then null
|
||||
else if final.isx86_64 then "x86_64"
|
||||
else if final.isx86 then "i386"
|
||||
else if final.isMips64n32 then "mipsn32${lib.optionalString final.isLittleEndian "el"}"
|
||||
else if final.isMips64 then "mips64${lib.optionalString final.isLittleEndian "el"}"
|
||||
else final.uname.processor;
|
||||
|
||||
# Name used by UEFI for architectures.
|
||||
efiArch =
|
||||
if final.isx86_32 then "ia32"
|
||||
else if final.isx86_64 then "x64"
|
||||
else if final.isAarch32 then "arm"
|
||||
else if final.isAarch64 then "aa64"
|
||||
else final.parsed.cpu.name;
|
||||
|
||||
darwinArch = {
|
||||
armv7a = "armv7";
|
||||
aarch64 = "arm64";
|
||||
}.${final.parsed.cpu.name} or final.parsed.cpu.name;
|
||||
|
||||
darwinPlatform =
|
||||
if final.isMacOS then "macos"
|
||||
else if final.isiOS then "ios"
|
||||
else null;
|
||||
# The canonical name for this attribute is darwinSdkVersion, but some
|
||||
# platforms define the old name "sdkVer".
|
||||
darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12");
|
||||
darwinMinVersion = final.darwinSdkVersion;
|
||||
darwinMinVersionVariable =
|
||||
if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET"
|
||||
else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET"
|
||||
else null;
|
||||
} // (
|
||||
let
|
||||
selectEmulator = pkgs:
|
||||
let
|
||||
qemu-user = pkgs.qemu.override {
|
||||
smartcardSupport = false;
|
||||
spiceSupport = false;
|
||||
openGLSupport = false;
|
||||
virglSupport = false;
|
||||
vncSupport = false;
|
||||
gtkSupport = false;
|
||||
sdlSupport = false;
|
||||
pulseSupport = false;
|
||||
pipewireSupport = false;
|
||||
smbdSupport = false;
|
||||
seccompSupport = false;
|
||||
enableDocs = false;
|
||||
hostCpuTargets = [ "${final.qemuArch}-linux-user" ];
|
||||
};
|
||||
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
|
||||
in
|
||||
if pkgs.stdenv.hostPlatform.canExecute final
|
||||
then "${pkgs.runtimeShell} -c '\"$@\"' --"
|
||||
else if final.isWindows
|
||||
then "${wine}/bin/wine${lib.optionalString (final.parsed.cpu.bits == 64) "64"}"
|
||||
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
|
||||
then "${qemu-user}/bin/qemu-${final.qemuArch}"
|
||||
else if final.isWasi
|
||||
then "${pkgs.wasmtime}/bin/wasmtime"
|
||||
else if final.isMmix
|
||||
then "${pkgs.mmixware}/bin/mmix"
|
||||
else null;
|
||||
in {
|
||||
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
|
||||
|
||||
emulator = pkgs:
|
||||
if (final.emulatorAvailable pkgs)
|
||||
then selectEmulator pkgs
|
||||
else throw "Don't know how to run ${final.config} executables.";
|
||||
|
||||
}) // mapAttrs (n: v: v final.parsed) inspect.predicates
|
||||
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
|
||||
// args;
|
||||
in assert final.useAndroidPrebuilt -> final.isAndroid;
|
||||
assert lib.foldl
|
||||
(pass: { assertion, message }:
|
||||
if assertion final
|
||||
then pass
|
||||
else throw message)
|
||||
true
|
||||
(final.parsed.abi.assertions or []);
|
||||
final;
|
||||
}
|
119
pesto/test_data/assets/systems/doubles.nix
Normal file
119
pesto/test_data/assets/systems/doubles.nix
Normal file
@ -0,0 +1,119 @@
|
||||
{ lib }:
|
||||
let
|
||||
inherit (lib) lists;
|
||||
inherit (lib.systems) parse;
|
||||
inherit (lib.systems.inspect) predicates;
|
||||
inherit (lib.attrsets) matchAttrs;
|
||||
|
||||
all = [
|
||||
# Cygwin
|
||||
"i686-cygwin" "x86_64-cygwin"
|
||||
|
||||
# Darwin
|
||||
"x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin"
|
||||
|
||||
# FreeBSD
|
||||
"i686-freebsd13" "x86_64-freebsd13"
|
||||
|
||||
# Genode
|
||||
"aarch64-genode" "i686-genode" "x86_64-genode"
|
||||
|
||||
# illumos
|
||||
"x86_64-solaris"
|
||||
|
||||
# JS
|
||||
"javascript-ghcjs"
|
||||
|
||||
# Linux
|
||||
"aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux"
|
||||
"armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux"
|
||||
"microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux"
|
||||
"mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux"
|
||||
"riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux"
|
||||
|
||||
# MMIXware
|
||||
"mmix-mmixware"
|
||||
|
||||
# NetBSD
|
||||
"aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd"
|
||||
"i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd"
|
||||
"riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd"
|
||||
|
||||
# none
|
||||
"aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
|
||||
"microblaze-none" "microblazeel-none" "mips-none" "mips64-none" "msp430-none" "or1k-none" "m68k-none"
|
||||
"powerpc-none" "powerpcle-none" "riscv32-none" "riscv64-none" "rx-none"
|
||||
"s390-none" "s390x-none" "vc4-none" "x86_64-none"
|
||||
|
||||
# OpenBSD
|
||||
"i686-openbsd" "x86_64-openbsd"
|
||||
|
||||
# Redox
|
||||
"x86_64-redox"
|
||||
|
||||
# WASI
|
||||
"wasm64-wasi" "wasm32-wasi"
|
||||
|
||||
# Windows
|
||||
"x86_64-windows" "i686-windows"
|
||||
];
|
||||
|
||||
allParsed = map parse.mkSystemFromString all;
|
||||
|
||||
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
|
||||
|
||||
in {
|
||||
inherit all;
|
||||
|
||||
none = [];
|
||||
|
||||
arm = filterDoubles predicates.isAarch32;
|
||||
armv7 = filterDoubles predicates.isArmv7;
|
||||
aarch64 = filterDoubles predicates.isAarch64;
|
||||
x86 = filterDoubles predicates.isx86;
|
||||
i686 = filterDoubles predicates.isi686;
|
||||
x86_64 = filterDoubles predicates.isx86_64;
|
||||
microblaze = filterDoubles predicates.isMicroBlaze;
|
||||
mips = filterDoubles predicates.isMips;
|
||||
mmix = filterDoubles predicates.isMmix;
|
||||
power = filterDoubles predicates.isPower;
|
||||
riscv = filterDoubles predicates.isRiscV;
|
||||
riscv32 = filterDoubles predicates.isRiscV32;
|
||||
riscv64 = filterDoubles predicates.isRiscV64;
|
||||
rx = filterDoubles predicates.isRx;
|
||||
vc4 = filterDoubles predicates.isVc4;
|
||||
or1k = filterDoubles predicates.isOr1k;
|
||||
m68k = filterDoubles predicates.isM68k;
|
||||
s390 = filterDoubles predicates.isS390;
|
||||
s390x = filterDoubles predicates.isS390x;
|
||||
loongarch64 = filterDoubles predicates.isLoongArch64;
|
||||
js = filterDoubles predicates.isJavaScript;
|
||||
|
||||
bigEndian = filterDoubles predicates.isBigEndian;
|
||||
littleEndian = filterDoubles predicates.isLittleEndian;
|
||||
|
||||
cygwin = filterDoubles predicates.isCygwin;
|
||||
darwin = filterDoubles predicates.isDarwin;
|
||||
freebsd = filterDoubles predicates.isFreeBSD;
|
||||
# Should be better, but MinGW is unclear.
|
||||
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabin32; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabi64; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv1; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv2; });
|
||||
illumos = filterDoubles predicates.isSunOS;
|
||||
linux = filterDoubles predicates.isLinux;
|
||||
netbsd = filterDoubles predicates.isNetBSD;
|
||||
openbsd = filterDoubles predicates.isOpenBSD;
|
||||
unix = filterDoubles predicates.isUnix;
|
||||
wasi = filterDoubles predicates.isWasi;
|
||||
redox = filterDoubles predicates.isRedox;
|
||||
windows = filterDoubles predicates.isWindows;
|
||||
genode = filterDoubles predicates.isGenode;
|
||||
|
||||
embedded = filterDoubles predicates.isNone;
|
||||
|
||||
mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "armv7a-linux" "aarch64-linux" "powerpc64-linux" "powerpc64le-linux" "aarch64-darwin" "riscv64-linux"];
|
||||
}
|
355
pesto/test_data/assets/systems/examples.nix
Normal file
355
pesto/test_data/assets/systems/examples.nix
Normal file
@ -0,0 +1,355 @@
|
||||
# These can be passed to nixpkgs as either the `localSystem` or
|
||||
# `crossSystem`. They are put here for user convenience, but also used by cross
|
||||
# tests and linux cross stdenv building, so handle with care!
|
||||
{ lib }:
|
||||
let
|
||||
platforms = import ./platforms.nix { inherit lib; };
|
||||
|
||||
riscv = bits: {
|
||||
config = "riscv${bits}-unknown-linux-gnu";
|
||||
};
|
||||
in
|
||||
|
||||
rec {
|
||||
#
|
||||
# Linux
|
||||
#
|
||||
powernv = {
|
||||
config = "powerpc64le-unknown-linux-gnu";
|
||||
};
|
||||
musl-power = {
|
||||
config = "powerpc64le-unknown-linux-musl";
|
||||
};
|
||||
|
||||
ppc64 = {
|
||||
config = "powerpc64-unknown-linux-gnuabielfv2";
|
||||
};
|
||||
ppc64-musl = {
|
||||
config = "powerpc64-unknown-linux-musl";
|
||||
gcc = { abi = "elfv2"; };
|
||||
};
|
||||
|
||||
sheevaplug = {
|
||||
config = "armv5tel-unknown-linux-gnueabi";
|
||||
} // platforms.sheevaplug;
|
||||
|
||||
raspberryPi = {
|
||||
config = "armv6l-unknown-linux-gnueabihf";
|
||||
} // platforms.raspberrypi;
|
||||
|
||||
bluefield2 = {
|
||||
config = "aarch64-unknown-linux-gnu";
|
||||
} // platforms.bluefield2;
|
||||
|
||||
remarkable1 = {
|
||||
config = "armv7l-unknown-linux-gnueabihf";
|
||||
} // platforms.zero-gravitas;
|
||||
|
||||
remarkable2 = {
|
||||
config = "armv7l-unknown-linux-gnueabihf";
|
||||
} // platforms.zero-sugar;
|
||||
|
||||
armv7l-hf-multiplatform = {
|
||||
config = "armv7l-unknown-linux-gnueabihf";
|
||||
};
|
||||
|
||||
aarch64-multiplatform = {
|
||||
config = "aarch64-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
armv7a-android-prebuilt = {
|
||||
config = "armv7a-unknown-linux-androideabi";
|
||||
rustc.config = "armv7-linux-androideabi";
|
||||
sdkVer = "28";
|
||||
ndkVer = "24";
|
||||
useAndroidPrebuilt = true;
|
||||
} // platforms.armv7a-android;
|
||||
|
||||
aarch64-android-prebuilt = {
|
||||
config = "aarch64-unknown-linux-android";
|
||||
rustc.config = "aarch64-linux-android";
|
||||
sdkVer = "28";
|
||||
ndkVer = "24";
|
||||
useAndroidPrebuilt = true;
|
||||
};
|
||||
|
||||
aarch64-android = {
|
||||
config = "aarch64-unknown-linux-android";
|
||||
sdkVer = "30";
|
||||
ndkVer = "24";
|
||||
libc = "bionic";
|
||||
useAndroidPrebuilt = false;
|
||||
useLLVM = true;
|
||||
};
|
||||
|
||||
pogoplug4 = {
|
||||
config = "armv5tel-unknown-linux-gnueabi";
|
||||
} // platforms.pogoplug4;
|
||||
|
||||
ben-nanonote = {
|
||||
config = "mipsel-unknown-linux-uclibc";
|
||||
} // platforms.ben_nanonote;
|
||||
|
||||
fuloongminipc = {
|
||||
config = "mipsel-unknown-linux-gnu";
|
||||
} // platforms.fuloong2f_n32;
|
||||
|
||||
# can execute on 32bit chip
|
||||
mips-linux-gnu = { config = "mips-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
|
||||
mipsel-linux-gnu = { config = "mipsel-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
|
||||
|
||||
# require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
|
||||
mips64-linux-gnuabin32 = { config = "mips64-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
|
||||
mips64el-linux-gnuabin32 = { config = "mips64el-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
|
||||
|
||||
# 64bit pointers
|
||||
mips64-linux-gnuabi64 = { config = "mips64-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
|
||||
mips64el-linux-gnuabi64 = { config = "mips64el-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
|
||||
|
||||
muslpi = raspberryPi // {
|
||||
config = "armv6l-unknown-linux-musleabihf";
|
||||
};
|
||||
|
||||
aarch64-multiplatform-musl = {
|
||||
config = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
|
||||
gnu64 = { config = "x86_64-unknown-linux-gnu"; };
|
||||
gnu32 = { config = "i686-unknown-linux-gnu"; };
|
||||
|
||||
musl64 = { config = "x86_64-unknown-linux-musl"; };
|
||||
musl32 = { config = "i686-unknown-linux-musl"; };
|
||||
|
||||
riscv64 = riscv "64";
|
||||
riscv32 = riscv "32";
|
||||
|
||||
riscv64-embedded = {
|
||||
config = "riscv64-none-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
riscv32-embedded = {
|
||||
config = "riscv32-none-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
mips64-embedded = {
|
||||
config = "mips64-none-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
mips-embedded = {
|
||||
config = "mips-none-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
loongarch64-linux = {
|
||||
config = "loongarch64-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
mmix = {
|
||||
config = "mmix-unknown-mmixware";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
rx-embedded = {
|
||||
config = "rx-none-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
msp430 = {
|
||||
config = "msp430-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
avr = {
|
||||
config = "avr";
|
||||
};
|
||||
|
||||
vc4 = {
|
||||
config = "vc4-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
or1k = {
|
||||
config = "or1k-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
m68k = {
|
||||
config = "m68k-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
s390 = {
|
||||
config = "s390-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
s390x = {
|
||||
config = "s390x-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
arm-embedded = {
|
||||
config = "arm-none-eabi";
|
||||
libc = "newlib";
|
||||
};
|
||||
armhf-embedded = {
|
||||
config = "arm-none-eabihf";
|
||||
libc = "newlib";
|
||||
# GCC8+ does not build without this
|
||||
# (https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg552339.html):
|
||||
gcc = {
|
||||
arch = "armv5t";
|
||||
fpu = "vfp";
|
||||
};
|
||||
};
|
||||
|
||||
aarch64-embedded = {
|
||||
config = "aarch64-none-elf";
|
||||
libc = "newlib";
|
||||
rustc.config = "aarch64-unknown-none";
|
||||
};
|
||||
|
||||
aarch64be-embedded = {
|
||||
config = "aarch64_be-none-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
ppc-embedded = {
|
||||
config = "powerpc-none-eabi";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
ppcle-embedded = {
|
||||
config = "powerpcle-none-eabi";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
i686-embedded = {
|
||||
config = "i686-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
x86_64-embedded = {
|
||||
config = "x86_64-elf";
|
||||
libc = "newlib";
|
||||
};
|
||||
|
||||
#
|
||||
# Redox
|
||||
#
|
||||
|
||||
x86_64-unknown-redox = {
|
||||
config = "x86_64-unknown-redox";
|
||||
libc = "relibc";
|
||||
};
|
||||
|
||||
#
|
||||
# Darwin
|
||||
#
|
||||
|
||||
iphone64 = {
|
||||
config = "aarch64-apple-ios";
|
||||
# config = "aarch64-apple-darwin14";
|
||||
sdkVer = "14.3";
|
||||
xcodeVer = "12.3";
|
||||
xcodePlatform = "iPhoneOS";
|
||||
useiOSPrebuilt = true;
|
||||
};
|
||||
|
||||
iphone32 = {
|
||||
config = "armv7a-apple-ios";
|
||||
# config = "arm-apple-darwin10";
|
||||
sdkVer = "14.3";
|
||||
xcodeVer = "12.3";
|
||||
xcodePlatform = "iPhoneOS";
|
||||
useiOSPrebuilt = true;
|
||||
};
|
||||
|
||||
iphone64-simulator = {
|
||||
config = "x86_64-apple-ios";
|
||||
# config = "x86_64-apple-darwin14";
|
||||
sdkVer = "14.3";
|
||||
xcodeVer = "12.3";
|
||||
xcodePlatform = "iPhoneSimulator";
|
||||
darwinPlatform = "ios-simulator";
|
||||
useiOSPrebuilt = true;
|
||||
};
|
||||
|
||||
iphone32-simulator = {
|
||||
config = "i686-apple-ios";
|
||||
# config = "i386-apple-darwin11";
|
||||
sdkVer = "14.3";
|
||||
xcodeVer = "12.3";
|
||||
xcodePlatform = "iPhoneSimulator";
|
||||
darwinPlatform = "ios-simulator";
|
||||
useiOSPrebuilt = true;
|
||||
};
|
||||
|
||||
aarch64-darwin = {
|
||||
config = "aarch64-apple-darwin";
|
||||
xcodePlatform = "MacOSX";
|
||||
platform = {};
|
||||
};
|
||||
|
||||
x86_64-darwin = {
|
||||
config = "x86_64-apple-darwin";
|
||||
xcodePlatform = "MacOSX";
|
||||
platform = {};
|
||||
};
|
||||
|
||||
#
|
||||
# Windows
|
||||
#
|
||||
|
||||
# 32 bit mingw-w64
|
||||
mingw32 = {
|
||||
config = "i686-w64-mingw32";
|
||||
libc = "msvcrt"; # This distinguishes the mingw (non posix) toolchain
|
||||
};
|
||||
|
||||
# 64 bit mingw-w64
|
||||
mingwW64 = {
|
||||
# That's the triplet they use in the mingw-w64 docs.
|
||||
config = "x86_64-w64-mingw32";
|
||||
libc = "msvcrt"; # This distinguishes the mingw (non posix) toolchain
|
||||
};
|
||||
|
||||
ucrt64 = {
|
||||
config = "x86_64-w64-mingw32";
|
||||
libc = "ucrt"; # This distinguishes the mingw (non posix) toolchain
|
||||
};
|
||||
|
||||
# BSDs
|
||||
|
||||
x86_64-freebsd = {
|
||||
config = "x86_64-unknown-freebsd13";
|
||||
useLLVM = true;
|
||||
};
|
||||
|
||||
x86_64-netbsd = {
|
||||
config = "x86_64-unknown-netbsd";
|
||||
};
|
||||
|
||||
# this is broken and never worked fully
|
||||
x86_64-netbsd-llvm = {
|
||||
config = "x86_64-unknown-netbsd";
|
||||
useLLVM = true;
|
||||
};
|
||||
|
||||
#
|
||||
# WASM
|
||||
#
|
||||
|
||||
wasi32 = {
|
||||
config = "wasm32-unknown-wasi";
|
||||
useLLVM = true;
|
||||
};
|
||||
|
||||
# Ghcjs
|
||||
ghcjs = {
|
||||
# This triple is special to GHC/Cabal/GHCJS and not recognized by autotools
|
||||
# See: https://gitlab.haskell.org/ghc/ghc/-/commit/6636b670233522f01d002c9b97827d00289dbf5c
|
||||
# https://github.com/ghcjs/ghcjs/issues/53
|
||||
config = "javascript-unknown-ghcjs";
|
||||
};
|
||||
}
|
29
pesto/test_data/assets/systems/flake-systems.nix
Normal file
29
pesto/test_data/assets/systems/flake-systems.nix
Normal file
@ -0,0 +1,29 @@
|
||||
# See [RFC 46] for mandated platform support and ../../pkgs/stdenv for
|
||||
# implemented platform support. This list is mainly descriptive, i.e. all
|
||||
# system doubles for platforms where nixpkgs can do native compilation
|
||||
# reasonably well are included.
|
||||
#
|
||||
# [RFC 46]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md
|
||||
{ }:
|
||||
|
||||
[
|
||||
# Tier 1
|
||||
"x86_64-linux"
|
||||
# Tier 2
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
# Tier 3
|
||||
"armv6l-linux"
|
||||
"armv7l-linux"
|
||||
"i686-linux"
|
||||
"mipsel-linux"
|
||||
|
||||
# Other platforms with sufficient support in stdenv which is not formally
|
||||
# mandated by their platform tier.
|
||||
"aarch64-darwin"
|
||||
"armv5tel-linux"
|
||||
"powerpc64le-linux"
|
||||
"riscv64-linux"
|
||||
|
||||
# "x86_64-freebsd" is excluded because it is mostly broken
|
||||
]
|
117
pesto/test_data/assets/systems/inspect.nix
Normal file
117
pesto/test_data/assets/systems/inspect.nix
Normal file
@ -0,0 +1,117 @@
|
||||
{ lib }:
|
||||
with import ./parse.nix { inherit lib; };
|
||||
with lib.attrsets;
|
||||
with lib.lists;
|
||||
|
||||
let abis_ = abis; in
|
||||
let abis = lib.mapAttrs (_: abi: builtins.removeAttrs abi [ "assertions" ]) abis_; in
|
||||
|
||||
rec {
|
||||
# these patterns are to be matched against {host,build,target}Platform.parsed
|
||||
patterns = rec {
|
||||
# The patterns below are lists in sum-of-products form.
|
||||
#
|
||||
# Each attribute is list of product conditions; non-list values are treated
|
||||
# as a singleton list. If *any* product condition in the list matches then
|
||||
# the predicate matches. Each product condition is tested by
|
||||
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
|
||||
# the product.
|
||||
|
||||
isi686 = { cpu = cpuTypes.i686; };
|
||||
isx86_32 = { cpu = { family = "x86"; bits = 32; }; };
|
||||
isx86_64 = { cpu = { family = "x86"; bits = 64; }; };
|
||||
isPower = { cpu = { family = "power"; }; };
|
||||
isPower64 = { cpu = { family = "power"; bits = 64; }; };
|
||||
# This ABI is the default in NixOS PowerPC64 BE, but not on mainline GCC,
|
||||
# so it sometimes causes issues in certain packages that makes the wrong
|
||||
# assumption on the used ABI.
|
||||
isAbiElfv2 = [
|
||||
{ abi = { abi = "elfv2"; }; }
|
||||
{ abi = { name = "musl"; }; cpu = { family = "power"; bits = 64; }; }
|
||||
];
|
||||
isx86 = { cpu = { family = "x86"; }; };
|
||||
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
|
||||
isArmv7 = map ({ arch, ... }: { cpu = { inherit arch; }; })
|
||||
(lib.filter (cpu: lib.hasPrefix "armv7" cpu.arch or "")
|
||||
(lib.attrValues cpuTypes));
|
||||
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
|
||||
isAarch = { cpu = { family = "arm"; }; };
|
||||
isMicroBlaze = { cpu = { family = "microblaze"; }; };
|
||||
isMips = { cpu = { family = "mips"; }; };
|
||||
isMips32 = { cpu = { family = "mips"; bits = 32; }; };
|
||||
isMips64 = { cpu = { family = "mips"; bits = 64; }; };
|
||||
isMips64n32 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
|
||||
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
|
||||
isMmix = { cpu = { family = "mmix"; }; };
|
||||
isRiscV = { cpu = { family = "riscv"; }; };
|
||||
isRiscV32 = { cpu = { family = "riscv"; bits = 32; }; };
|
||||
isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
|
||||
isRx = { cpu = { family = "rx"; }; };
|
||||
isSparc = { cpu = { family = "sparc"; }; };
|
||||
isWasm = { cpu = { family = "wasm"; }; };
|
||||
isMsp430 = { cpu = { family = "msp430"; }; };
|
||||
isVc4 = { cpu = { family = "vc4"; }; };
|
||||
isAvr = { cpu = { family = "avr"; }; };
|
||||
isAlpha = { cpu = { family = "alpha"; }; };
|
||||
isOr1k = { cpu = { family = "or1k"; }; };
|
||||
isM68k = { cpu = { family = "m68k"; }; };
|
||||
isS390 = { cpu = { family = "s390"; }; };
|
||||
isS390x = { cpu = { family = "s390"; bits = 64; }; };
|
||||
isLoongArch64 = { cpu = { family = "loongarch"; bits = 64; }; };
|
||||
isJavaScript = { cpu = cpuTypes.javascript; };
|
||||
|
||||
is32bit = { cpu = { bits = 32; }; };
|
||||
is64bit = { cpu = { bits = 64; }; };
|
||||
isILP32 = map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
|
||||
isBigEndian = { cpu = { significantByte = significantBytes.bigEndian; }; };
|
||||
isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
|
||||
|
||||
isBSD = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
|
||||
isDarwin = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
|
||||
isUnix = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ];
|
||||
|
||||
isMacOS = { kernel = kernels.macos; };
|
||||
isiOS = { kernel = kernels.ios; };
|
||||
isLinux = { kernel = kernels.linux; };
|
||||
isSunOS = { kernel = kernels.solaris; };
|
||||
isFreeBSD = { kernel = { name = "freebsd"; }; };
|
||||
isNetBSD = { kernel = kernels.netbsd; };
|
||||
isOpenBSD = { kernel = kernels.openbsd; };
|
||||
isWindows = { kernel = kernels.windows; };
|
||||
isCygwin = { kernel = kernels.windows; abi = abis.cygnus; };
|
||||
isMinGW = { kernel = kernels.windows; abi = abis.gnu; };
|
||||
isWasi = { kernel = kernels.wasi; };
|
||||
isRedox = { kernel = kernels.redox; };
|
||||
isGhcjs = { kernel = kernels.ghcjs; };
|
||||
isGenode = { kernel = kernels.genode; };
|
||||
isNone = { kernel = kernels.none; };
|
||||
|
||||
isAndroid = [ { abi = abis.android; } { abi = abis.androideabi; } ];
|
||||
isGnu = with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 gnu gnueabi gnueabihf gnuabielfv1 gnuabielfv2 ];
|
||||
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
|
||||
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];
|
||||
|
||||
isEfi = [
|
||||
{ cpu = { family = "arm"; version = "6"; }; }
|
||||
{ cpu = { family = "arm"; version = "7"; }; }
|
||||
{ cpu = { family = "arm"; version = "8"; }; }
|
||||
{ cpu = { family = "riscv"; }; }
|
||||
{ cpu = { family = "x86"; }; }
|
||||
];
|
||||
};
|
||||
|
||||
matchAnyAttrs = patterns:
|
||||
if builtins.isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
|
||||
else matchAttrs patterns;
|
||||
|
||||
predicates = mapAttrs (_: matchAnyAttrs) patterns;
|
||||
|
||||
# these patterns are to be matched against the entire
|
||||
# {host,build,target}Platform structure; they include a `parsed={}` marker so
|
||||
# that `lib.meta.availableOn` can distinguish them from the patterns which
|
||||
# apply only to the `parsed` field.
|
||||
|
||||
platformPatterns = mapAttrs (_: p: { parsed = {}; } // p) {
|
||||
isStatic = { isStatic = true; };
|
||||
};
|
||||
}
|
505
pesto/test_data/assets/systems/parse.nix
Normal file
505
pesto/test_data/assets/systems/parse.nix
Normal file
@ -0,0 +1,505 @@
|
||||
# Define the list of system with their properties.
|
||||
#
|
||||
# See https://clang.llvm.org/docs/CrossCompilation.html and
|
||||
# http://llvm.org/docs/doxygen/html/Triple_8cpp_source.html especially
|
||||
# Triple::normalize. Parsing should essentially act as a more conservative
|
||||
# version of that last function.
|
||||
#
|
||||
# Most of the types below come in "open" and "closed" pairs. The open ones
|
||||
# specify what information we need to know about systems in general, and the
|
||||
# closed ones are sub-types representing the whitelist of systems we support in
|
||||
# practice.
|
||||
#
|
||||
# Code in the remainder of nixpkgs shouldn't rely on the closed ones in
|
||||
# e.g. exhaustive cases. Its more a sanity check to make sure nobody defines
|
||||
# systems that overlap with existing ones and won't notice something amiss.
|
||||
#
|
||||
{ lib }:
|
||||
with lib.lists;
|
||||
with lib.types;
|
||||
with lib.attrsets;
|
||||
with lib.strings;
|
||||
with (import ./inspect.nix { inherit lib; }).predicates;
|
||||
|
||||
let
|
||||
inherit (lib.options) mergeOneOption;
|
||||
|
||||
setTypes = type:
|
||||
mapAttrs (name: value:
|
||||
assert type.check value;
|
||||
setType type.name ({ inherit name; } // value));
|
||||
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openSignificantByte = mkOptionType {
|
||||
name = "significant-byte";
|
||||
description = "Endianness";
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
types.significantByte = enum (attrValues significantBytes);
|
||||
|
||||
significantBytes = setTypes types.openSignificantByte {
|
||||
bigEndian = {};
|
||||
littleEndian = {};
|
||||
};
|
||||
|
||||
################################################################################
|
||||
|
||||
# Reasonable power of 2
|
||||
types.bitWidth = enum [ 8 16 32 64 128 ];
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openCpuType = mkOptionType {
|
||||
name = "cpu-type";
|
||||
description = "instruction set architecture name and information";
|
||||
merge = mergeOneOption;
|
||||
check = x: types.bitWidth.check x.bits
|
||||
&& (if 8 < x.bits
|
||||
then types.significantByte.check x.significantByte
|
||||
else !(x ? significantByte));
|
||||
};
|
||||
|
||||
types.cpuType = enum (attrValues cpuTypes);
|
||||
|
||||
cpuTypes = with significantBytes; setTypes types.openCpuType {
|
||||
arm = { bits = 32; significantByte = littleEndian; family = "arm"; };
|
||||
armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; arch = "armv5t"; };
|
||||
armv6m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6-m"; };
|
||||
armv6l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6"; };
|
||||
armv7a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-a"; };
|
||||
armv7r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-r"; };
|
||||
armv7m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-m"; };
|
||||
armv7l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7"; };
|
||||
armv8a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
||||
armv8r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
||||
armv8m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-m"; };
|
||||
aarch64 = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
||||
aarch64_be = { bits = 64; significantByte = bigEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
||||
|
||||
i386 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i386"; };
|
||||
i486 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i486"; };
|
||||
i586 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i586"; };
|
||||
i686 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i686"; };
|
||||
x86_64 = { bits = 64; significantByte = littleEndian; family = "x86"; arch = "x86-64"; };
|
||||
|
||||
microblaze = { bits = 32; significantByte = bigEndian; family = "microblaze"; };
|
||||
microblazeel = { bits = 32; significantByte = littleEndian; family = "microblaze"; };
|
||||
|
||||
mips = { bits = 32; significantByte = bigEndian; family = "mips"; };
|
||||
mipsel = { bits = 32; significantByte = littleEndian; family = "mips"; };
|
||||
mips64 = { bits = 64; significantByte = bigEndian; family = "mips"; };
|
||||
mips64el = { bits = 64; significantByte = littleEndian; family = "mips"; };
|
||||
|
||||
mmix = { bits = 64; significantByte = bigEndian; family = "mmix"; };
|
||||
|
||||
m68k = { bits = 32; significantByte = bigEndian; family = "m68k"; };
|
||||
|
||||
powerpc = { bits = 32; significantByte = bigEndian; family = "power"; };
|
||||
powerpc64 = { bits = 64; significantByte = bigEndian; family = "power"; };
|
||||
powerpc64le = { bits = 64; significantByte = littleEndian; family = "power"; };
|
||||
powerpcle = { bits = 32; significantByte = littleEndian; family = "power"; };
|
||||
|
||||
riscv32 = { bits = 32; significantByte = littleEndian; family = "riscv"; };
|
||||
riscv64 = { bits = 64; significantByte = littleEndian; family = "riscv"; };
|
||||
|
||||
s390 = { bits = 32; significantByte = bigEndian; family = "s390"; };
|
||||
s390x = { bits = 64; significantByte = bigEndian; family = "s390"; };
|
||||
|
||||
sparc = { bits = 32; significantByte = bigEndian; family = "sparc"; };
|
||||
sparc64 = { bits = 64; significantByte = bigEndian; family = "sparc"; };
|
||||
|
||||
wasm32 = { bits = 32; significantByte = littleEndian; family = "wasm"; };
|
||||
wasm64 = { bits = 64; significantByte = littleEndian; family = "wasm"; };
|
||||
|
||||
alpha = { bits = 64; significantByte = littleEndian; family = "alpha"; };
|
||||
|
||||
rx = { bits = 32; significantByte = littleEndian; family = "rx"; };
|
||||
msp430 = { bits = 16; significantByte = littleEndian; family = "msp430"; };
|
||||
avr = { bits = 8; family = "avr"; };
|
||||
|
||||
vc4 = { bits = 32; significantByte = littleEndian; family = "vc4"; };
|
||||
|
||||
or1k = { bits = 32; significantByte = bigEndian; family = "or1k"; };
|
||||
|
||||
loongarch64 = { bits = 64; significantByte = littleEndian; family = "loongarch"; };
|
||||
|
||||
javascript = { bits = 32; significantByte = littleEndian; family = "javascript"; };
|
||||
};
|
||||
|
||||
# GNU build systems assume that older NetBSD architectures are using a.out.
|
||||
gnuNetBSDDefaultExecFormat = cpu:
|
||||
if (cpu.family == "arm" && cpu.bits == 32) ||
|
||||
(cpu.family == "sparc" && cpu.bits == 32) ||
|
||||
(cpu.family == "m68k" && cpu.bits == 32) ||
|
||||
(cpu.family == "x86" && cpu.bits == 32)
|
||||
then execFormats.aout
|
||||
else execFormats.elf;
|
||||
|
||||
# Determine when two CPUs are compatible with each other. That is,
|
||||
# can code built for system B run on system A? For that to happen,
|
||||
# the programs that system B accepts must be a subset of the
|
||||
# programs that system A accepts.
|
||||
#
|
||||
# We have the following properties of the compatibility relation,
|
||||
# which must be preserved when adding compatibility information for
|
||||
# additional CPUs.
|
||||
# - (reflexivity)
|
||||
# Every CPU is compatible with itself.
|
||||
# - (transitivity)
|
||||
# If A is compatible with B and B is compatible with C then A is compatible with C.
|
||||
#
|
||||
# Note: Since 22.11 the archs of a mode switching CPU are no longer considered
|
||||
# pairwise compatible. Mode switching implies that binaries built for A
|
||||
# and B respectively can't be executed at the same time.
|
||||
isCompatible = a: b: with cpuTypes; lib.any lib.id [
|
||||
# x86
|
||||
(b == i386 && isCompatible a i486)
|
||||
(b == i486 && isCompatible a i586)
|
||||
(b == i586 && isCompatible a i686)
|
||||
|
||||
# XXX: Not true in some cases. Like in WSL mode.
|
||||
(b == i686 && isCompatible a x86_64)
|
||||
|
||||
# ARMv4
|
||||
(b == arm && isCompatible a armv5tel)
|
||||
|
||||
# ARMv5
|
||||
(b == armv5tel && isCompatible a armv6l)
|
||||
|
||||
# ARMv6
|
||||
(b == armv6l && isCompatible a armv6m)
|
||||
(b == armv6m && isCompatible a armv7l)
|
||||
|
||||
# ARMv7
|
||||
(b == armv7l && isCompatible a armv7a)
|
||||
(b == armv7l && isCompatible a armv7r)
|
||||
(b == armv7l && isCompatible a armv7m)
|
||||
|
||||
# ARMv8
|
||||
(b == aarch64 && a == armv8a)
|
||||
(b == armv8a && isCompatible a aarch64)
|
||||
(b == armv8r && isCompatible a armv8a)
|
||||
(b == armv8m && isCompatible a armv8a)
|
||||
|
||||
# PowerPC
|
||||
(b == powerpc && isCompatible a powerpc64)
|
||||
(b == powerpcle && isCompatible a powerpc64le)
|
||||
|
||||
# MIPS
|
||||
(b == mips && isCompatible a mips64)
|
||||
(b == mipsel && isCompatible a mips64el)
|
||||
|
||||
# RISCV
|
||||
(b == riscv32 && isCompatible a riscv64)
|
||||
|
||||
# SPARC
|
||||
(b == sparc && isCompatible a sparc64)
|
||||
|
||||
# WASM
|
||||
(b == wasm32 && isCompatible a wasm64)
|
||||
|
||||
# identity
|
||||
(b == a)
|
||||
];
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openVendor = mkOptionType {
|
||||
name = "vendor";
|
||||
description = "vendor for the platform";
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
types.vendor = enum (attrValues vendors);
|
||||
|
||||
vendors = setTypes types.openVendor {
|
||||
apple = {};
|
||||
pc = {};
|
||||
knuth = {};
|
||||
|
||||
# Actually matters, unlocking some MinGW-w64-specific options in GCC. See
|
||||
# bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
|
||||
w64 = {};
|
||||
|
||||
none = {};
|
||||
unknown = {};
|
||||
};
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openExecFormat = mkOptionType {
|
||||
name = "exec-format";
|
||||
description = "executable container used by the kernel";
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
types.execFormat = enum (attrValues execFormats);
|
||||
|
||||
execFormats = setTypes types.openExecFormat {
|
||||
aout = {}; # a.out
|
||||
elf = {};
|
||||
macho = {};
|
||||
pe = {};
|
||||
wasm = {};
|
||||
|
||||
unknown = {};
|
||||
};
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openKernelFamily = mkOptionType {
|
||||
name = "exec-format";
|
||||
description = "executable container used by the kernel";
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
types.kernelFamily = enum (attrValues kernelFamilies);
|
||||
|
||||
kernelFamilies = setTypes types.openKernelFamily {
|
||||
bsd = {};
|
||||
darwin = {};
|
||||
};
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openKernel = mkOptionType {
|
||||
name = "kernel";
|
||||
description = "kernel name and information";
|
||||
merge = mergeOneOption;
|
||||
check = x: types.execFormat.check x.execFormat
|
||||
&& all types.kernelFamily.check (attrValues x.families);
|
||||
};
|
||||
|
||||
types.kernel = enum (attrValues kernels);
|
||||
|
||||
kernels = with execFormats; with kernelFamilies; setTypes types.openKernel {
|
||||
# TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
|
||||
# the normalized name for macOS.
|
||||
macos = { execFormat = macho; families = { inherit darwin; }; name = "darwin"; };
|
||||
ios = { execFormat = macho; families = { inherit darwin; }; };
|
||||
# A tricky thing about FreeBSD is that there is no stable ABI across
|
||||
# versions. That means that putting in the version as part of the
|
||||
# config string is paramount.
|
||||
freebsd12 = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; version = 12; };
|
||||
freebsd13 = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; version = 13; };
|
||||
linux = { execFormat = elf; families = { }; };
|
||||
netbsd = { execFormat = elf; families = { inherit bsd; }; };
|
||||
none = { execFormat = unknown; families = { }; };
|
||||
openbsd = { execFormat = elf; families = { inherit bsd; }; };
|
||||
solaris = { execFormat = elf; families = { }; };
|
||||
wasi = { execFormat = wasm; families = { }; };
|
||||
redox = { execFormat = elf; families = { }; };
|
||||
windows = { execFormat = pe; families = { }; };
|
||||
ghcjs = { execFormat = unknown; families = { }; };
|
||||
genode = { execFormat = elf; families = { }; };
|
||||
mmixware = { execFormat = unknown; families = { }; };
|
||||
} // { # aliases
|
||||
# 'darwin' is the kernel for all of them. We choose macOS by default.
|
||||
darwin = kernels.macos;
|
||||
watchos = kernels.ios;
|
||||
tvos = kernels.ios;
|
||||
win32 = kernels.windows;
|
||||
};
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openAbi = mkOptionType {
|
||||
name = "abi";
|
||||
description = "binary interface for compiled code and syscalls";
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
types.abi = enum (attrValues abis);
|
||||
|
||||
abis = setTypes types.openAbi {
|
||||
cygnus = {};
|
||||
msvc = {};
|
||||
|
||||
# Note: eabi is specific to ARM and PowerPC.
|
||||
# On PowerPC, this corresponds to PPCEABI.
|
||||
# On ARM, this corresponds to ARMEABI.
|
||||
eabi = { float = "soft"; };
|
||||
eabihf = { float = "hard"; };
|
||||
|
||||
# Other architectures should use ELF in embedded situations.
|
||||
elf = {};
|
||||
|
||||
androideabi = {};
|
||||
android = {
|
||||
assertions = [
|
||||
{ assertion = platform: !platform.isAarch32;
|
||||
message = ''
|
||||
The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
gnueabi = { float = "soft"; };
|
||||
gnueabihf = { float = "hard"; };
|
||||
gnu = {
|
||||
assertions = [
|
||||
{ assertion = platform: !platform.isAarch32;
|
||||
message = ''
|
||||
The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
|
||||
'';
|
||||
}
|
||||
{ assertion = platform: with platform; !(isPower64 && isBigEndian);
|
||||
message = ''
|
||||
The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
gnuabi64 = { abi = "64"; };
|
||||
muslabi64 = { abi = "64"; };
|
||||
|
||||
# NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
|
||||
# It is basically the 64-bit abi with 32-bit pointers. Details:
|
||||
# https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
|
||||
gnuabin32 = { abi = "n32"; };
|
||||
muslabin32 = { abi = "n32"; };
|
||||
|
||||
gnuabielfv2 = { abi = "elfv2"; };
|
||||
gnuabielfv1 = { abi = "elfv1"; };
|
||||
|
||||
musleabi = { float = "soft"; };
|
||||
musleabihf = { float = "hard"; };
|
||||
musl = {};
|
||||
|
||||
uclibceabi = { float = "soft"; };
|
||||
uclibceabihf = { float = "hard"; };
|
||||
uclibc = {};
|
||||
|
||||
unknown = {};
|
||||
};
|
||||
|
||||
################################################################################
|
||||
|
||||
types.parsedPlatform = mkOptionType {
|
||||
name = "system";
|
||||
description = "fully parsed representation of llvm- or nix-style platform tuple";
|
||||
merge = mergeOneOption;
|
||||
check = { cpu, vendor, kernel, abi }:
|
||||
types.cpuType.check cpu
|
||||
&& types.vendor.check vendor
|
||||
&& types.kernel.check kernel
|
||||
&& types.abi.check abi;
|
||||
};
|
||||
|
||||
isSystem = isType "system";
|
||||
|
||||
mkSystem = components:
|
||||
assert types.parsedPlatform.check components;
|
||||
setType "system" components;
|
||||
|
||||
mkSkeletonFromList = l: {
|
||||
"1" = if elemAt l 0 == "avr"
|
||||
then { cpu = elemAt l 0; kernel = "none"; abi = "unknown"; }
|
||||
else throw "Target specification with 1 components is ambiguous";
|
||||
"2" = # We only do 2-part hacks for things Nix already supports
|
||||
if elemAt l 1 == "cygwin"
|
||||
then { cpu = elemAt l 0; kernel = "windows"; abi = "cygnus"; }
|
||||
# MSVC ought to be the default ABI so this case isn't needed. But then it
|
||||
# becomes difficult to handle the gnu* variants for Aarch32 correctly for
|
||||
# minGW. So it's easier to make gnu* the default for the MinGW, but
|
||||
# hack-in MSVC for the non-MinGW case right here.
|
||||
else if elemAt l 1 == "windows"
|
||||
then { cpu = elemAt l 0; kernel = "windows"; abi = "msvc"; }
|
||||
else if (elemAt l 1) == "elf"
|
||||
then { cpu = elemAt l 0; vendor = "unknown"; kernel = "none"; abi = elemAt l 1; }
|
||||
else { cpu = elemAt l 0; kernel = elemAt l 1; };
|
||||
"3" =
|
||||
# cpu-kernel-environment
|
||||
if elemAt l 1 == "linux" ||
|
||||
elem (elemAt l 2) ["eabi" "eabihf" "elf" "gnu"]
|
||||
then {
|
||||
cpu = elemAt l 0;
|
||||
kernel = elemAt l 1;
|
||||
abi = elemAt l 2;
|
||||
vendor = "unknown";
|
||||
}
|
||||
# cpu-vendor-os
|
||||
else if elemAt l 1 == "apple" ||
|
||||
elem (elemAt l 2) [ "wasi" "redox" "mmixware" "ghcjs" "mingw32" ] ||
|
||||
hasPrefix "freebsd" (elemAt l 2) ||
|
||||
hasPrefix "netbsd" (elemAt l 2) ||
|
||||
hasPrefix "genode" (elemAt l 2)
|
||||
then {
|
||||
cpu = elemAt l 0;
|
||||
vendor = elemAt l 1;
|
||||
kernel = if elemAt l 2 == "mingw32"
|
||||
then "windows" # autotools breaks on -gnu for window
|
||||
else elemAt l 2;
|
||||
}
|
||||
else throw "Target specification with 3 components is ambiguous";
|
||||
"4" = { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; abi = elemAt l 3; };
|
||||
}.${toString (length l)}
|
||||
or (throw "system string has invalid number of hyphen-separated components");
|
||||
|
||||
# This should revert the job done by config.guess from the gcc compiler.
|
||||
mkSystemFromSkeleton = { cpu
|
||||
, # Optional, but fallback too complex for here.
|
||||
# Inferred below instead.
|
||||
vendor ? assert false; null
|
||||
, kernel
|
||||
, # Also inferred below
|
||||
abi ? assert false; null
|
||||
} @ args: let
|
||||
getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
|
||||
getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
|
||||
getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
|
||||
getAbi = name: abis.${name} or (throw "Unknown ABI: ${name}");
|
||||
|
||||
parsed = {
|
||||
cpu = getCpu args.cpu;
|
||||
vendor =
|
||||
/**/ if args ? vendor then getVendor args.vendor
|
||||
else if isDarwin parsed then vendors.apple
|
||||
else if isWindows parsed then vendors.pc
|
||||
else vendors.unknown;
|
||||
kernel = if hasPrefix "darwin" args.kernel then getKernel "darwin"
|
||||
else if hasPrefix "netbsd" args.kernel then getKernel "netbsd"
|
||||
else getKernel args.kernel;
|
||||
abi =
|
||||
/**/ if args ? abi then getAbi args.abi
|
||||
else if isLinux parsed || isWindows parsed then
|
||||
if isAarch32 parsed then
|
||||
if lib.versionAtLeast (parsed.cpu.version or "0") "6"
|
||||
then abis.gnueabihf
|
||||
else abis.gnueabi
|
||||
# Default ppc64 BE to ELFv2
|
||||
else if isPower64 parsed && isBigEndian parsed then abis.gnuabielfv2
|
||||
else abis.gnu
|
||||
else abis.unknown;
|
||||
};
|
||||
|
||||
in mkSystem parsed;
|
||||
|
||||
mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
|
||||
|
||||
kernelName = kernel:
|
||||
kernel.name + toString (kernel.version or "");
|
||||
|
||||
doubleFromSystem = { cpu, kernel, abi, ... }:
|
||||
/**/ if abi == abis.cygnus then "${cpu.name}-cygwin"
|
||||
else if kernel.families ? darwin then "${cpu.name}-darwin"
|
||||
else "${cpu.name}-${kernelName kernel}";
|
||||
|
||||
tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
|
||||
optExecFormat =
|
||||
lib.optionalString (kernel.name == "netbsd" &&
|
||||
gnuNetBSDDefaultExecFormat cpu != kernel.execFormat)
|
||||
kernel.execFormat.name;
|
||||
optAbi = lib.optionalString (abi != abis.unknown) "-${abi.name}";
|
||||
in "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
|
||||
|
||||
################################################################################
|
||||
|
||||
}
|
572
pesto/test_data/assets/systems/platforms.nix
Normal file
572
pesto/test_data/assets/systems/platforms.nix
Normal file
@ -0,0 +1,572 @@
|
||||
# Note: lib/systems/default.nix takes care of producing valid,
|
||||
# fully-formed "platform" values (e.g. hostPlatform, buildPlatform,
|
||||
# targetPlatform, etc) containing at least the minimal set of attrs
|
||||
# required (see types.parsedPlatform in lib/systems/parse.nix). This
|
||||
# file takes an already-valid platform and further elaborates it with
|
||||
# optional fields; currently these are: linux-kernel, gcc, and rustc.
|
||||
|
||||
{ lib }:
|
||||
rec {
|
||||
pc = {
|
||||
linux-kernel = {
|
||||
name = "pc";
|
||||
|
||||
baseConfig = "defconfig";
|
||||
# Build whatever possible as a module, if not stated in the extra config.
|
||||
autoModules = true;
|
||||
target = "bzImage";
|
||||
};
|
||||
};
|
||||
|
||||
pc_simplekernel = lib.recursiveUpdate pc {
|
||||
linux-kernel.autoModules = false;
|
||||
};
|
||||
|
||||
powernv = {
|
||||
linux-kernel = {
|
||||
name = "PowerNV";
|
||||
|
||||
baseConfig = "powernv_defconfig";
|
||||
target = "vmlinux";
|
||||
autoModules = true;
|
||||
# avoid driver/FS trouble arising from unusual page size
|
||||
extraConfig = ''
|
||||
PPC_64K_PAGES n
|
||||
PPC_4K_PAGES y
|
||||
IPV6 y
|
||||
|
||||
ATA_BMDMA y
|
||||
ATA_SFF y
|
||||
VIRTIO_MENU y
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
##
|
||||
## ARM
|
||||
##
|
||||
|
||||
pogoplug4 = {
|
||||
linux-kernel = {
|
||||
name = "pogoplug4";
|
||||
|
||||
baseConfig = "multi_v5_defconfig";
|
||||
autoModules = false;
|
||||
extraConfig = ''
|
||||
# Ubi for the mtd
|
||||
MTD_UBI y
|
||||
UBIFS_FS y
|
||||
UBIFS_FS_XATTR y
|
||||
UBIFS_FS_ADVANCED_COMPR y
|
||||
UBIFS_FS_LZO y
|
||||
UBIFS_FS_ZLIB y
|
||||
UBIFS_FS_DEBUG n
|
||||
'';
|
||||
makeFlags = [ "LOADADDR=0x8000" ];
|
||||
target = "uImage";
|
||||
# TODO reenable once manual-config's config actually builds a .dtb and this is checked to be working
|
||||
#DTB = true;
|
||||
};
|
||||
gcc = {
|
||||
arch = "armv5te";
|
||||
};
|
||||
};
|
||||
|
||||
sheevaplug = {
|
||||
linux-kernel = {
|
||||
name = "sheevaplug";
|
||||
|
||||
baseConfig = "multi_v5_defconfig";
|
||||
autoModules = false;
|
||||
extraConfig = ''
|
||||
BLK_DEV_RAM y
|
||||
BLK_DEV_INITRD y
|
||||
BLK_DEV_CRYPTOLOOP m
|
||||
BLK_DEV_DM m
|
||||
DM_CRYPT m
|
||||
MD y
|
||||
REISERFS_FS m
|
||||
BTRFS_FS m
|
||||
XFS_FS m
|
||||
JFS_FS m
|
||||
EXT4_FS m
|
||||
USB_STORAGE_CYPRESS_ATACB m
|
||||
|
||||
# mv cesa requires this sw fallback, for mv-sha1
|
||||
CRYPTO_SHA1 y
|
||||
# Fast crypto
|
||||
CRYPTO_TWOFISH y
|
||||
CRYPTO_TWOFISH_COMMON y
|
||||
CRYPTO_BLOWFISH y
|
||||
CRYPTO_BLOWFISH_COMMON y
|
||||
|
||||
IP_PNP y
|
||||
IP_PNP_DHCP y
|
||||
NFS_FS y
|
||||
ROOT_NFS y
|
||||
TUN m
|
||||
NFS_V4 y
|
||||
NFS_V4_1 y
|
||||
NFS_FSCACHE y
|
||||
NFSD m
|
||||
NFSD_V2_ACL y
|
||||
NFSD_V3 y
|
||||
NFSD_V3_ACL y
|
||||
NFSD_V4 y
|
||||
NETFILTER y
|
||||
IP_NF_IPTABLES y
|
||||
IP_NF_FILTER y
|
||||
IP_NF_MATCH_ADDRTYPE y
|
||||
IP_NF_TARGET_LOG y
|
||||
IP_NF_MANGLE y
|
||||
IPV6 m
|
||||
VLAN_8021Q m
|
||||
|
||||
CIFS y
|
||||
CIFS_XATTR y
|
||||
CIFS_POSIX y
|
||||
CIFS_FSCACHE y
|
||||
CIFS_ACL y
|
||||
|
||||
WATCHDOG y
|
||||
WATCHDOG_CORE y
|
||||
ORION_WATCHDOG m
|
||||
|
||||
ZRAM m
|
||||
NETCONSOLE m
|
||||
|
||||
# Disable OABI to have seccomp_filter (required for systemd)
|
||||
# https://github.com/raspberrypi/firmware/issues/651
|
||||
OABI_COMPAT n
|
||||
|
||||
# Fail to build
|
||||
DRM n
|
||||
SCSI_ADVANSYS n
|
||||
USB_ISP1362_HCD n
|
||||
SND_SOC n
|
||||
SND_ALI5451 n
|
||||
FB_SAVAGE n
|
||||
SCSI_NSP32 n
|
||||
ATA_SFF n
|
||||
SUNGEM n
|
||||
IRDA n
|
||||
ATM_HE n
|
||||
SCSI_ACARD n
|
||||
BLK_DEV_CMD640_ENHANCED n
|
||||
|
||||
FUSE_FS m
|
||||
|
||||
# systemd uses cgroups
|
||||
CGROUPS y
|
||||
|
||||
# Latencytop
|
||||
LATENCYTOP y
|
||||
|
||||
# Ubi for the mtd
|
||||
MTD_UBI y
|
||||
UBIFS_FS y
|
||||
UBIFS_FS_XATTR y
|
||||
UBIFS_FS_ADVANCED_COMPR y
|
||||
UBIFS_FS_LZO y
|
||||
UBIFS_FS_ZLIB y
|
||||
UBIFS_FS_DEBUG n
|
||||
|
||||
# Kdb, for kernel troubles
|
||||
KGDB y
|
||||
KGDB_SERIAL_CONSOLE y
|
||||
KGDB_KDB y
|
||||
'';
|
||||
makeFlags = [ "LOADADDR=0x0200000" ];
|
||||
target = "uImage";
|
||||
DTB = true; # Beyond 3.10
|
||||
};
|
||||
gcc = {
|
||||
arch = "armv5te";
|
||||
};
|
||||
};
|
||||
|
||||
raspberrypi = {
|
||||
linux-kernel = {
|
||||
name = "raspberrypi";
|
||||
|
||||
baseConfig = "bcm2835_defconfig";
|
||||
DTB = true;
|
||||
autoModules = true;
|
||||
preferBuiltin = true;
|
||||
extraConfig = ''
|
||||
# Disable OABI to have seccomp_filter (required for systemd)
|
||||
# https://github.com/raspberrypi/firmware/issues/651
|
||||
OABI_COMPAT n
|
||||
'';
|
||||
target = "zImage";
|
||||
};
|
||||
gcc = {
|
||||
arch = "armv6";
|
||||
fpu = "vfp";
|
||||
};
|
||||
};
|
||||
|
||||
# Legacy attribute, for compatibility with existing configs only.
|
||||
raspberrypi2 = armv7l-hf-multiplatform;
|
||||
|
||||
# Nvidia Bluefield 2 (w. crypto support)
|
||||
bluefield2 = {
|
||||
gcc = {
|
||||
arch = "armv8-a+fp+simd+crc+crypto";
|
||||
};
|
||||
};
|
||||
|
||||
zero-gravitas = {
|
||||
linux-kernel = {
|
||||
name = "zero-gravitas";
|
||||
|
||||
baseConfig = "zero-gravitas_defconfig";
|
||||
# Target verified by checking /boot on reMarkable 1 device
|
||||
target = "zImage";
|
||||
autoModules = false;
|
||||
DTB = true;
|
||||
};
|
||||
gcc = {
|
||||
fpu = "neon";
|
||||
cpu = "cortex-a9";
|
||||
};
|
||||
};
|
||||
|
||||
zero-sugar = {
|
||||
linux-kernel = {
|
||||
name = "zero-sugar";
|
||||
|
||||
baseConfig = "zero-sugar_defconfig";
|
||||
DTB = true;
|
||||
autoModules = false;
|
||||
preferBuiltin = true;
|
||||
target = "zImage";
|
||||
};
|
||||
gcc = {
|
||||
cpu = "cortex-a7";
|
||||
fpu = "neon-vfpv4";
|
||||
float-abi = "hard";
|
||||
};
|
||||
};
|
||||
|
||||
utilite = {
|
||||
linux-kernel = {
|
||||
name = "utilite";
|
||||
maseConfig = "multi_v7_defconfig";
|
||||
autoModules = false;
|
||||
extraConfig = ''
|
||||
# Ubi for the mtd
|
||||
MTD_UBI y
|
||||
UBIFS_FS y
|
||||
UBIFS_FS_XATTR y
|
||||
UBIFS_FS_ADVANCED_COMPR y
|
||||
UBIFS_FS_LZO y
|
||||
UBIFS_FS_ZLIB y
|
||||
UBIFS_FS_DEBUG n
|
||||
'';
|
||||
makeFlags = [ "LOADADDR=0x10800000" ];
|
||||
target = "uImage";
|
||||
DTB = true;
|
||||
};
|
||||
gcc = {
|
||||
cpu = "cortex-a9";
|
||||
fpu = "neon";
|
||||
};
|
||||
};
|
||||
|
||||
guruplug = lib.recursiveUpdate sheevaplug {
|
||||
# Define `CONFIG_MACH_GURUPLUG' (see
|
||||
# <http://kerneltrap.org/mailarchive/git-commits-head/2010/5/19/33618>)
|
||||
# and other GuruPlug-specific things. Requires the `guruplug-defconfig'
|
||||
# patch.
|
||||
linux-kernel.baseConfig = "guruplug_defconfig";
|
||||
};
|
||||
|
||||
beaglebone = lib.recursiveUpdate armv7l-hf-multiplatform {
|
||||
linux-kernel = {
|
||||
name = "beaglebone";
|
||||
baseConfig = "bb.org_defconfig";
|
||||
autoModules = false;
|
||||
extraConfig = ""; # TBD kernel config
|
||||
target = "zImage";
|
||||
};
|
||||
};
|
||||
|
||||
# https://developer.android.com/ndk/guides/abis#v7a
|
||||
armv7a-android = {
|
||||
linux-kernel.name = "armeabi-v7a";
|
||||
gcc = {
|
||||
arch = "armv7-a";
|
||||
float-abi = "softfp";
|
||||
fpu = "vfpv3-d16";
|
||||
};
|
||||
};
|
||||
|
||||
armv7l-hf-multiplatform = {
|
||||
linux-kernel = {
|
||||
name = "armv7l-hf-multiplatform";
|
||||
Major = "2.6"; # Using "2.6" enables 2.6 kernel syscalls in glibc.
|
||||
baseConfig = "multi_v7_defconfig";
|
||||
DTB = true;
|
||||
autoModules = true;
|
||||
preferBuiltin = true;
|
||||
target = "zImage";
|
||||
extraConfig = ''
|
||||
# Serial port for Raspberry Pi 3. Wasn't included in ARMv7 defconfig
|
||||
# until 4.17.
|
||||
SERIAL_8250_BCM2835AUX y
|
||||
SERIAL_8250_EXTENDED y
|
||||
SERIAL_8250_SHARE_IRQ y
|
||||
|
||||
# Hangs ODROID-XU4
|
||||
ARM_BIG_LITTLE_CPUIDLE n
|
||||
|
||||
# Disable OABI to have seccomp_filter (required for systemd)
|
||||
# https://github.com/raspberrypi/firmware/issues/651
|
||||
OABI_COMPAT n
|
||||
|
||||
# >=5.12 fails with:
|
||||
# drivers/net/ethernet/micrel/ks8851_common.o: in function `ks8851_probe_common':
|
||||
# ks8851_common.c:(.text+0x179c): undefined reference to `__this_module'
|
||||
# See: https://lore.kernel.org/netdev/20210116164828.40545-1-marex@denx.de/T/
|
||||
KS8851_MLL y
|
||||
'';
|
||||
};
|
||||
gcc = {
|
||||
# Some table about fpu flags:
|
||||
# http://community.arm.com/servlet/JiveServlet/showImage/38-1981-3827/blogentry-103749-004812900+1365712953_thumb.png
|
||||
# Cortex-A5: -mfpu=neon-fp16
|
||||
# Cortex-A7 (rpi2): -mfpu=neon-vfpv4
|
||||
# Cortex-A8 (beaglebone): -mfpu=neon
|
||||
# Cortex-A9: -mfpu=neon-fp16
|
||||
# Cortex-A15: -mfpu=neon-vfpv4
|
||||
|
||||
# More about FPU:
|
||||
# https://wiki.debian.org/ArmHardFloatPort/VfpComparison
|
||||
|
||||
# vfpv3-d16 is what Debian uses and seems to be the best compromise: NEON is not supported in e.g. Scaleway or Tegra 2,
|
||||
# and the above page suggests NEON is only an improvement with hand-written assembly.
|
||||
arch = "armv7-a";
|
||||
fpu = "vfpv3-d16";
|
||||
|
||||
# For Raspberry Pi the 2 the best would be:
|
||||
# cpu = "cortex-a7";
|
||||
# fpu = "neon-vfpv4";
|
||||
};
|
||||
};
|
||||
|
||||
aarch64-multiplatform = {
|
||||
linux-kernel = {
|
||||
name = "aarch64-multiplatform";
|
||||
baseConfig = "defconfig";
|
||||
DTB = true;
|
||||
autoModules = true;
|
||||
preferBuiltin = true;
|
||||
extraConfig = ''
|
||||
# Raspberry Pi 3 stuff. Not needed for s >= 4.10.
|
||||
ARCH_BCM2835 y
|
||||
BCM2835_MBOX y
|
||||
BCM2835_WDT y
|
||||
RASPBERRYPI_FIRMWARE y
|
||||
RASPBERRYPI_POWER y
|
||||
SERIAL_8250_BCM2835AUX y
|
||||
SERIAL_8250_EXTENDED y
|
||||
SERIAL_8250_SHARE_IRQ y
|
||||
|
||||
# Cavium ThunderX stuff.
|
||||
PCI_HOST_THUNDER_ECAM y
|
||||
|
||||
# Nvidia Tegra stuff.
|
||||
PCI_TEGRA y
|
||||
|
||||
# The default (=y) forces us to have the XHCI firmware available in initrd,
|
||||
# which our initrd builder can't currently do easily.
|
||||
USB_XHCI_TEGRA m
|
||||
'';
|
||||
target = "Image";
|
||||
};
|
||||
gcc = {
|
||||
arch = "armv8-a";
|
||||
};
|
||||
};
|
||||
|
||||
apple-m1 = {
|
||||
gcc = {
|
||||
arch = "armv8.3-a+crypto+sha2+aes+crc+fp16+lse+simd+ras+rdm+rcpc";
|
||||
cpu = "apple-a13";
|
||||
};
|
||||
};
|
||||
|
||||
##
|
||||
## MIPS
|
||||
##
|
||||
|
||||
ben_nanonote = {
|
||||
linux-kernel = {
|
||||
name = "ben_nanonote";
|
||||
};
|
||||
gcc = {
|
||||
arch = "mips32";
|
||||
float = "soft";
|
||||
};
|
||||
};
|
||||
|
||||
fuloong2f_n32 = {
|
||||
linux-kernel = {
|
||||
name = "fuloong2f_n32";
|
||||
baseConfig = "lemote2f_defconfig";
|
||||
autoModules = false;
|
||||
extraConfig = ''
|
||||
MIGRATION n
|
||||
COMPACTION n
|
||||
|
||||
# nixos mounts some cgroup
|
||||
CGROUPS y
|
||||
|
||||
BLK_DEV_RAM y
|
||||
BLK_DEV_INITRD y
|
||||
BLK_DEV_CRYPTOLOOP m
|
||||
BLK_DEV_DM m
|
||||
DM_CRYPT m
|
||||
MD y
|
||||
REISERFS_FS m
|
||||
EXT4_FS m
|
||||
USB_STORAGE_CYPRESS_ATACB m
|
||||
|
||||
IP_PNP y
|
||||
IP_PNP_DHCP y
|
||||
IP_PNP_BOOTP y
|
||||
NFS_FS y
|
||||
ROOT_NFS y
|
||||
TUN m
|
||||
NFS_V4 y
|
||||
NFS_V4_1 y
|
||||
NFS_FSCACHE y
|
||||
NFSD m
|
||||
NFSD_V2_ACL y
|
||||
NFSD_V3 y
|
||||
NFSD_V3_ACL y
|
||||
NFSD_V4 y
|
||||
|
||||
# Fail to build
|
||||
DRM n
|
||||
SCSI_ADVANSYS n
|
||||
USB_ISP1362_HCD n
|
||||
SND_SOC n
|
||||
SND_ALI5451 n
|
||||
FB_SAVAGE n
|
||||
SCSI_NSP32 n
|
||||
ATA_SFF n
|
||||
SUNGEM n
|
||||
IRDA n
|
||||
ATM_HE n
|
||||
SCSI_ACARD n
|
||||
BLK_DEV_CMD640_ENHANCED n
|
||||
|
||||
FUSE_FS m
|
||||
|
||||
# Needed for udev >= 150
|
||||
SYSFS_DEPRECATED_V2 n
|
||||
|
||||
VGA_CONSOLE n
|
||||
VT_HW_CONSOLE_BINDING y
|
||||
SERIAL_8250_CONSOLE y
|
||||
FRAMEBUFFER_CONSOLE y
|
||||
EXT2_FS y
|
||||
EXT3_FS y
|
||||
REISERFS_FS y
|
||||
MAGIC_SYSRQ y
|
||||
|
||||
# The kernel doesn't boot at all, with FTRACE
|
||||
FTRACE n
|
||||
'';
|
||||
target = "vmlinux";
|
||||
};
|
||||
gcc = {
|
||||
arch = "loongson2f";
|
||||
float = "hard";
|
||||
abi = "n32";
|
||||
};
|
||||
};
|
||||
|
||||
# can execute on 32bit chip
|
||||
gcc_mips32r2_o32 = { gcc = { arch = "mips32r2"; abi = "32"; }; };
|
||||
gcc_mips32r6_o32 = { gcc = { arch = "mips32r6"; abi = "32"; }; };
|
||||
gcc_mips64r2_n32 = { gcc = { arch = "mips64r2"; abi = "n32"; }; };
|
||||
gcc_mips64r6_n32 = { gcc = { arch = "mips64r6"; abi = "n32"; }; };
|
||||
gcc_mips64r2_64 = { gcc = { arch = "mips64r2"; abi = "64"; }; };
|
||||
gcc_mips64r6_64 = { gcc = { arch = "mips64r6"; abi = "64"; }; };
|
||||
|
||||
# based on:
|
||||
# https://www.mail-archive.com/qemu-discuss@nongnu.org/msg05179.html
|
||||
# https://gmplib.org/~tege/qemu.html#mips64-debian
|
||||
mips64el-qemu-linux-gnuabi64 = {
|
||||
linux-kernel = {
|
||||
name = "mips64el";
|
||||
baseConfig = "64r2el_defconfig";
|
||||
target = "vmlinuz";
|
||||
autoModules = false;
|
||||
DTB = true;
|
||||
# for qemu 9p passthrough filesystem
|
||||
extraConfig = ''
|
||||
MIPS_MALTA y
|
||||
PAGE_SIZE_4KB y
|
||||
CPU_LITTLE_ENDIAN y
|
||||
CPU_MIPS64_R2 y
|
||||
64BIT y
|
||||
CPU_MIPS64_R2 y
|
||||
|
||||
NET_9P y
|
||||
NET_9P_VIRTIO y
|
||||
9P_FS y
|
||||
9P_FS_POSIX_ACL y
|
||||
PCI y
|
||||
VIRTIO_PCI y
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
##
|
||||
## Other
|
||||
##
|
||||
|
||||
riscv-multiplatform = {
|
||||
linux-kernel = {
|
||||
name = "riscv-multiplatform";
|
||||
target = "Image";
|
||||
autoModules = true;
|
||||
baseConfig = "defconfig";
|
||||
DTB = true;
|
||||
extraConfig = ''
|
||||
SERIAL_OF_PLATFORM y
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# This function takes a minimally-valid "platform" and returns an
|
||||
# attrset containing zero or more additional attrs which should be
|
||||
# included in the platform in order to further elaborate it.
|
||||
select = platform:
|
||||
# x86
|
||||
/**/ if platform.isx86 then pc
|
||||
|
||||
# ARM
|
||||
else if platform.isAarch32 then let
|
||||
version = platform.parsed.cpu.version or null;
|
||||
in if version == null then pc
|
||||
else if lib.versionOlder version "6" then sheevaplug
|
||||
else if lib.versionOlder version "7" then raspberrypi
|
||||
else armv7l-hf-multiplatform
|
||||
|
||||
else if platform.isAarch64 then
|
||||
if platform.isDarwin then apple-m1
|
||||
else aarch64-multiplatform
|
||||
|
||||
else if platform.isRiscV then riscv-multiplatform
|
||||
|
||||
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.mipsel then (import ./examples.nix { inherit lib; }).mipsel-linux-gnu
|
||||
|
||||
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.powerpc64le then powernv
|
||||
|
||||
else { };
|
||||
}
|
7
pesto/test_data/assets/tests/check-eval.nix
Normal file
7
pesto/test_data/assets/tests/check-eval.nix
Normal file
@ -0,0 +1,7 @@
|
||||
# Throws an error if any of our lib tests fail.
|
||||
|
||||
let tests = [ "misc" "systems" ];
|
||||
all = builtins.concatLists (map (f: import (./. + "/${f}.nix")) tests);
|
||||
in if all == []
|
||||
then null
|
||||
else throw (builtins.toJSON all)
|
32
pesto/test_data/assets/tests/maintainer-module.nix
Normal file
32
pesto/test_data/assets/tests/maintainer-module.nix
Normal file
@ -0,0 +1,32 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) types;
|
||||
in {
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
email = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
matrix = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
github = lib.mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
githubId = lib.mkOption {
|
||||
type = types.nullOr types.ints.unsigned;
|
||||
default = null;
|
||||
};
|
||||
keys = lib.mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
options.fingerprint = lib.mkOption { type = types.str; };
|
||||
});
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
}
|
53
pesto/test_data/assets/tests/maintainers.nix
Normal file
53
pesto/test_data/assets/tests/maintainers.nix
Normal file
@ -0,0 +1,53 @@
|
||||
# to run these tests (and the others)
|
||||
# nix-build nixpkgs/lib/tests/release.nix
|
||||
# These tests should stay in sync with the comment in maintainers/maintainers-list.nix
|
||||
{ # The pkgs used for dependencies for the testing itself
|
||||
pkgs ? import ../.. {}
|
||||
, lib ? pkgs.lib
|
||||
}:
|
||||
|
||||
let
|
||||
checkMaintainer = handle: uncheckedAttrs:
|
||||
let
|
||||
prefix = [ "lib" "maintainers" handle ];
|
||||
checkedAttrs = (lib.modules.evalModules {
|
||||
inherit prefix;
|
||||
modules = [
|
||||
./maintainer-module.nix
|
||||
{
|
||||
_file = toString ../../maintainers/maintainer-list.nix;
|
||||
config = uncheckedAttrs;
|
||||
}
|
||||
];
|
||||
}).config;
|
||||
|
||||
checks = lib.optional (checkedAttrs.github != null && checkedAttrs.githubId == null) ''
|
||||
echo ${lib.escapeShellArg (lib.showOption prefix)}': If `github` is specified, `githubId` must be too.'
|
||||
# Calling this too often would hit non-authenticated API limits, but this
|
||||
# shouldn't happen since such errors will get fixed rather quickly
|
||||
info=$(curl -sS https://api.github.com/users/${checkedAttrs.github})
|
||||
id=$(jq -r '.id' <<< "$info")
|
||||
echo "The GitHub ID for GitHub user ${checkedAttrs.github} is $id:"
|
||||
echo -e " githubId = $id;\n"
|
||||
'' ++ lib.optional (checkedAttrs.email == null && checkedAttrs.github == null && checkedAttrs.matrix == null) ''
|
||||
echo ${lib.escapeShellArg (lib.showOption prefix)}': At least one of `email`, `github` or `matrix` must be specified, so that users know how to reach you.'
|
||||
'' ++ lib.optional (checkedAttrs.email != null && lib.hasSuffix "noreply.github.com" checkedAttrs.email) ''
|
||||
echo ${lib.escapeShellArg (lib.showOption prefix)}': If an email address is given, it should allow people to reach you. If you do not want that, you can just provide `github` or `matrix` instead.'
|
||||
'';
|
||||
in lib.deepSeq checkedAttrs checks;
|
||||
|
||||
missingGithubIds = lib.concatLists (lib.mapAttrsToList checkMaintainer lib.maintainers);
|
||||
|
||||
success = pkgs.runCommand "checked-maintainers-success" {} ">$out";
|
||||
|
||||
failure = pkgs.runCommand "checked-maintainers-failure" {
|
||||
nativeBuildInputs = [ pkgs.curl pkgs.jq ];
|
||||
outputHash = "sha256:${lib.fakeSha256}";
|
||||
outputHAlgo = "sha256";
|
||||
outputHashMode = "flat";
|
||||
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
} ''
|
||||
${lib.concatStringsSep "\n" missingGithubIds}
|
||||
exit 1
|
||||
'';
|
||||
in if missingGithubIds == [] then success else failure
|
1908
pesto/test_data/assets/tests/misc.nix
Normal file
1908
pesto/test_data/assets/tests/misc.nix
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@
|
||||
{ lib, ... }: {
|
||||
options.dummy = lib.mkOption { type = lib.types.anything; default = {}; };
|
||||
freeformType =
|
||||
let
|
||||
a = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; });
|
||||
in
|
||||
# modifying types like this breaks type merging.
|
||||
# This test makes sure that type merging is not performed when only a single declaration exists.
|
||||
# Don't modify types in practice!
|
||||
a // {
|
||||
merge = loc: defs: { freeformItems = a.merge loc defs; };
|
||||
};
|
||||
config.foo.bar = "ok";
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
# This is a test to show that mkAliasOptionModule sets the priority correctly
|
||||
# for aliased options.
|
||||
#
|
||||
# This test shows that an alias with a high priority is able to override
|
||||
# a non-aliased option.
|
||||
|
||||
{ config, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options = {
|
||||
# A simple boolean option that can be enabled or disabled.
|
||||
enable = lib.mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = ''
|
||||
Some descriptive text
|
||||
'';
|
||||
};
|
||||
|
||||
# mkAliasOptionModule sets warnings, so this has to be defined.
|
||||
warnings = mkOption {
|
||||
internal = true;
|
||||
default = [];
|
||||
type = types.listOf types.str;
|
||||
example = [ "The `foo' service is deprecated and will go away soon!" ];
|
||||
description = ''
|
||||
This option allows modules to show warnings to users during
|
||||
the evaluation of the system configuration.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
# Create an alias for the "enable" option.
|
||||
(mkAliasOptionModule [ "enableAlias" ] [ "enable" ])
|
||||
|
||||
# Disable the aliased option with a high priority so it
|
||||
# should override the next import.
|
||||
( { config, lib, ... }:
|
||||
{
|
||||
enableAlias = lib.mkForce false;
|
||||
}
|
||||
)
|
||||
|
||||
# Enable the normal (non-aliased) option.
|
||||
( { config, lib, ... }:
|
||||
{
|
||||
enable = true;
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
55
pesto/test_data/assets/tests/modules/alias-with-priority.nix
Normal file
55
pesto/test_data/assets/tests/modules/alias-with-priority.nix
Normal file
@ -0,0 +1,55 @@
|
||||
# This is a test to show that mkAliasOptionModule sets the priority correctly
|
||||
# for aliased options.
|
||||
#
|
||||
# This test shows that an alias with a low priority is able to be overridden
|
||||
# with a non-aliased option.
|
||||
|
||||
{ config, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options = {
|
||||
# A simple boolean option that can be enabled or disabled.
|
||||
enable = lib.mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = null;
|
||||
example = true;
|
||||
description = ''
|
||||
Some descriptive text
|
||||
'';
|
||||
};
|
||||
|
||||
# mkAliasOptionModule sets warnings, so this has to be defined.
|
||||
warnings = mkOption {
|
||||
internal = true;
|
||||
default = [];
|
||||
type = types.listOf types.str;
|
||||
example = [ "The `foo' service is deprecated and will go away soon!" ];
|
||||
description = ''
|
||||
This option allows modules to show warnings to users during
|
||||
the evaluation of the system configuration.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
# Create an alias for the "enable" option.
|
||||
(mkAliasOptionModule [ "enableAlias" ] [ "enable" ])
|
||||
|
||||
# Disable the aliased option, but with a default (low) priority so it
|
||||
# should be able to be overridden by the next import.
|
||||
( { config, lib, ... }:
|
||||
{
|
||||
enableAlias = lib.mkDefault false;
|
||||
}
|
||||
)
|
||||
|
||||
# Enable the normal (non-aliased) option.
|
||||
( { config, lib, ... }:
|
||||
{
|
||||
enable = true;
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
{ lib, config, ... }: {
|
||||
options.conditionalWorks = lib.mkOption {
|
||||
default = ! config.value ? foo;
|
||||
};
|
||||
|
||||
config.value.foo = lib.mkIf false "should not be defined";
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
{ lib, config, ... }: {
|
||||
options.isLazy = lib.mkOption {
|
||||
default = ! config.value ? foo;
|
||||
};
|
||||
|
||||
config.value.bar = throw "is not lazy";
|
||||
}
|
76
pesto/test_data/assets/tests/modules/class-check.nix
Normal file
76
pesto/test_data/assets/tests/modules/class-check.nix
Normal file
@ -0,0 +1,76 @@
|
||||
{ lib, ... }: {
|
||||
options = {
|
||||
sub = {
|
||||
nixosOk = lib.mkOption {
|
||||
type = lib.types.submoduleWith {
|
||||
class = "nixos";
|
||||
modules = [ ];
|
||||
};
|
||||
};
|
||||
# Same but will have bad definition
|
||||
nixosFail = lib.mkOption {
|
||||
type = lib.types.submoduleWith {
|
||||
class = "nixos";
|
||||
modules = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
mergeFail = lib.mkOption {
|
||||
type = lib.types.submoduleWith {
|
||||
class = "nixos";
|
||||
modules = [ ];
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
imports = [
|
||||
{
|
||||
options = {
|
||||
sub = {
|
||||
mergeFail = lib.mkOption {
|
||||
type = lib.types.submoduleWith {
|
||||
class = "darwin";
|
||||
modules = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
config = {
|
||||
_module.freeformType = lib.types.anything;
|
||||
ok =
|
||||
lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
./module-class-is-nixos.nix
|
||||
];
|
||||
};
|
||||
|
||||
fail =
|
||||
lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
./module-class-is-nixos.nix
|
||||
./module-class-is-darwin.nix
|
||||
];
|
||||
};
|
||||
|
||||
fail-anon =
|
||||
lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
./module-class-is-nixos.nix
|
||||
{ _file = "foo.nix#darwinModules.default";
|
||||
_class = "darwin";
|
||||
config = {};
|
||||
imports = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
sub.nixosOk = { _class = "nixos"; };
|
||||
sub.nixosFail = { imports = [ ./module-class-is-darwin.nix ]; };
|
||||
};
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
{ lib, options, ... }:
|
||||
let discardPositions = lib.mapAttrs (k: v: v);
|
||||
in
|
||||
# unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function.
|
||||
assert builtins.unsafeGetAttrPos "a" { a = true; } != null;
|
||||
assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null;
|
||||
{
|
||||
imports = [
|
||||
{
|
||||
options.imported.line10 = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
};
|
||||
|
||||
# Simulates various patterns of generating modules such as
|
||||
# programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get
|
||||
# line numbers for these, but we can fall back on knowing the file.
|
||||
options.generated = discardPositions {
|
||||
line18 = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
};
|
||||
};
|
||||
|
||||
options.submoduleLine34.extraOptLine23 = lib.mkOption {
|
||||
default = 1;
|
||||
type = lib.types.int;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
options.nested.nestedLine30 = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
};
|
||||
|
||||
options.submoduleLine34 = lib.mkOption {
|
||||
default = { };
|
||||
type = lib.types.submoduleWith {
|
||||
modules = [
|
||||
({ options, ... }: {
|
||||
options.submodDeclLine39 = lib.mkOption { };
|
||||
})
|
||||
{ freeformType = with lib.types; lazyAttrsOf (uniq unspecified); }
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
submoduleLine34.submodDeclLine39 = (options.submoduleLine34.type.getSubOptions [ ]).submodDeclLine39.declarationPositions;
|
||||
};
|
||||
}
|
13
pesto/test_data/assets/tests/modules/declare-attrsOf.nix
Normal file
13
pesto/test_data/assets/tests/modules/declare-attrsOf.nix
Normal file
@ -0,0 +1,13 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
deathtrapArgs = lib.mapAttrs
|
||||
(k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute.")
|
||||
(lib.functionArgs lib.mkOption);
|
||||
in
|
||||
{
|
||||
options.value = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = {};
|
||||
};
|
||||
options.testing-laziness-so-don't-read-me = lib.mkOption deathtrapArgs;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
{ lib, ... }:
|
||||
|
||||
let
|
||||
submod = { ... }: {
|
||||
options = {
|
||||
enable = lib.mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Some descriptive text
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
attrsOfSub = lib.mkOption {
|
||||
default = {};
|
||||
example = {};
|
||||
type = lib.types.attrsOf (lib.types.submodule [ submod ]);
|
||||
description = ''
|
||||
Some descriptive text
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options.bare-submodule.deep = mkOption {
|
||||
type = types.int;
|
||||
default = 2;
|
||||
};
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options.bare-submodule.deep = mkOption {
|
||||
type = types.int;
|
||||
default = 2;
|
||||
};
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options.bare-submodule = mkOption {
|
||||
type = types.submoduleWith {
|
||||
shorthandOnlyDefinesConfig = config.shorthandOnlyDefinesConfig;
|
||||
modules = [
|
||||
{
|
||||
options.nested = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options.bare-submodule = mkOption {
|
||||
type = types.submoduleWith {
|
||||
modules = [ ];
|
||||
shorthandOnlyDefinesConfig = config.shorthandOnlyDefinesConfig;
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
# config-dependent options: won't recommend, but useful for making this test parameterized
|
||||
options.shorthandOnlyDefinesConfig = mkOption {
|
||||
default = false;
|
||||
};
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
default = "12";
|
||||
type = lib.types.coercedTo lib.types.str lib.toInt lib.types.ints.s8;
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
default = 42;
|
||||
type = lib.types.coercedTo lib.types.int builtins.toString lib.types.str;
|
||||
};
|
||||
};
|
||||
}
|
5
pesto/test_data/assets/tests/modules/declare-either.nix
Normal file
5
pesto/test_data/assets/tests/modules/declare-either.nix
Normal file
@ -0,0 +1,5 @@
|
||||
{ lib, ... }: {
|
||||
options.value = lib.mkOption {
|
||||
type = lib.types.either lib.types.int lib.types.str;
|
||||
};
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options.set = {
|
||||
enable = lib.mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Some descriptive text
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
14
pesto/test_data/assets/tests/modules/declare-enable.nix
Normal file
14
pesto/test_data/assets/tests/modules/declare-enable.nix
Normal file
@ -0,0 +1,14 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
enable = lib.mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Some descriptive text
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
type = lib.types.ints.between (-21) 43;
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options.set = {
|
||||
value = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
type = lib.types.ints.unsigned;
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{ lib, ... }: {
|
||||
options.value = lib.mkOption {
|
||||
type = lib.types.lazyAttrsOf (lib.types.str // { emptyValue.value = "empty"; });
|
||||
default = {};
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user