this repo has no description
at main 699 lines 25 kB view raw
1{ lib, silicon }: 2with lib; 3with silicon; 4rec { 5 getFlake = 6 args@{ src, ... }: 7 let 8 yesFlake = false; # builtins ? getFlake; 9 flake = 10 if yesFlake then 11 (builtins.getFlake (toString src)) 12 else 13 ( 14 let 15 nodes = 16 if (builtins.pathExists ./flake.lock) then 17 (builtins.fromJSON (builtins.readFile ./flake.lock)) 18 else 19 { 20 root.inputs.flake-compat = "flake-compat"; 21 flake-compat.locked.rev = "master"; 22 }; 23 inherit (nodes.${nodes.root.inputs.flake-compat}) locked; 24 url = locked.url or "https://github.com/edolstra/flake-compat/archive/${locked.rev}.tar.gz"; 25 flake-compat = 26 if (args ? flake-compat) then 27 (import args.flake-compat) 28 else if yesFlake then 29 (builtins.getFlake url) 30 else 31 (builtins.import (fetchTarball { 32 inherit url; 33 ${if (locked ? narHash) then "sha256" else null} = locked.narHash; 34 })); 35 36 # builtins.unsafeDiscardStringContext 37 in 38 flake-compat { inherit src; } 39 ).defaultNix; 40 in 41 formatOutputs { 42 inherit flake; 43 getFlake = true; 44 }; 45 46 isLambda = f: (!(isAttrs f)) && (isFunction f); 47 isFunctor = f: (isAttrs f) && (isFunction f); 48 isFunctionTo = f: (f ? __functionArgs) && (f ? __functor) && ((length (attrNames f)) == 2); 49 isFunctionToLambda = f: (isLambda f) || (isFunctionTo f); 50 51 hasAnyAttrs = attrs: any (flip hasAttr attrs); 52 hasAllAttrs = attrs: all (flip hasAttr attrs); 53 54 mk = 55 let 56 defaulter = 57 config: options: attr: 58 if (config.${attr} != options.${attr}.default) then 59 config.${attr} 60 else if (hasAttr attr args) then 61 args.${attr} 62 else 63 config.${attr}; 64 in 65 { 66 # Adapted From: https://github.com/hercules-ci/flake-parts/blob/4524271976b625a4a605beefd893f270620fd751/lib.nix#L72C5-L125C11 67 __functor = self: mk.simple; 68 69 simple = 70 module: 71 let 72 defaultSystems = lib.systems.flakeExposed; 73 systemType = types.enum defaultSystems; 74 75 # TODO: Which option types need to be replaced with `update'? 76 updateType = mkOptionType { 77 name = "update"; 78 description = "update"; 79 check = isAttrs; 80 merge = loc: recursiveFold; 81 emptyValue.value = { }; 82 }; 83 84 modules = { 85 lib = 86 { config, ... }: 87 let 88 89 # TODO: Test thoroughly. Can this use a `mks' function instead of `composeManyMergedExtensions'? 90 libType = mkOptionType { 91 name = "lib"; 92 description = "library"; 93 check = isAttrs; 94 merge = 95 loc: defs: 96 let 97 predicate = def: isFunction (def.extend or null); 98 values = map (getAttr "value") defs; 99 in 100 recursiveFold ( 101 [ 102 ( 103 let 104 libs = filter predicate values; 105 in 106 if (libs == [ ]) then { } else ((head libs).extend (composeManyMergedExtensions (tail libs))) 107 ) 108 ] 109 ++ (filter (def: !(predicate def)) values) 110 ); 111 emptyValue.value = { }; 112 }; 113 114 in 115 { 116 options = { 117 118 # TODO: If not already, make libs passed to this extensible using a variation of `mks.extend'. 119 # TODO: Extend the lib manually, facilitating the use of `libType' in `libs' below. 120 lib = mkOption { 121 type = libType; 122 default = lib; 123 apply = lib: if (config.libs == { }) then lib else (mks.libs lib config.inheritances config.libs); 124 }; 125 126 # TODO: Merge this and `libs' into a module following the format: 127 # { syvl = { lib = ...; inheritance = { ... }; }; }; 128 # Modify the `mks.libs' call accordingly. 129 inheritances = mkOption { 130 type = types.attrsOf types.attrs; 131 default = { }; 132 }; 133 134 # TODO: Use `libType' here somehow. Also make these sublibs extensible if not already. 135 libs = mkOption { 136 type = 137 with types; 138 attrsOf (oneOf [ 139 path 140 attrs 141 list 142 (functionTo attrs) 143 ]); 144 default = { }; 145 }; 146 147 }; 148 }; 149 # TODO: Maybe add a `specialArgs' for this as well? 150 nixosConfigurations = 151 { config, options, ... }: 152 { 153 options = { 154 hosts = 155 let 156 hostOptions = { 157 system = mkOption { 158 type = types.nullOr systemType; 159 default = null; 160 }; 161 modules = mkOption { 162 type = options.modules.type.nestedTypes.finalType; 163 }; 164 }; 165 hostOptionNames = attrNames hostOptions; 166 hostModule = 167 { ... }: 168 { 169 options = hostOptions; 170 config.modules = config.modules; 171 }; 172 in 173 mkOption { 174 type = 175 with types; 176 coercedTo (either str (listOf str)) (compose [ 177 toList 178 (flip genAttrs (host: { })) 179 ]) attrs; 180 default = { }; 181 apply = mapAttrs ( 182 n: v: 183 recursiveUpdateAll 184 (evalModules { 185 class = "host"; 186 modules = [ 187 hostModule 188 (filterAttrs (n: v: elem n hostOptionNames) v) 189 ]; 190 }).config 191 (filterAttrs (n: v: !(elem n hostOptionNames)) v) 192 ); 193 }; 194 modules = mkOption { 195 type = 196 with types; 197 let 198 moduleType = oneOf [ 199 path 200 attrs 201 (functionTo attrs) 202 ]; 203 in 204 coercedTo moduleType toList (listOf moduleType); 205 default = [ ]; 206 }; 207 nixosConfigurations = mkOption { 208 type = 209 with types; 210 let 211 configType = either attrs (functionTo attrs); 212 in 213 coercedTo configType toList (listOf configType); 214 default = [ ]; 215 }; 216 __nixosHosts = mkOption { 217 type = types.attrs; 218 internal = true; 219 }; 220 }; 221 config = mkIf (config.hosts != { }) { 222 __nixosHosts = mkMerge ( 223 map ( 224 nc: 225 mapAttrs ( 226 hostName: host: 227 if (isFunctionToLambda nc) then 228 (recursiveUpdateAll (nc ( 229 let 230 inherit (host) system; 231 in 232 { 233 inherit hostName system; 234 nixosArgs = 235 let 236 settings = mk.settings config system; 237 in 238 { 239 inherit hostName system settings; 240 inherit (config) inputs; 241 inherit (settings) overlays; 242 }; 243 } 244 // config 245 )) host) 246 else 247 nc 248 ) config.hosts 249 ) config.nixosConfigurations 250 ); 251 outputs.nixosConfigurations = mapAttrs (n: config.nixpkgs.lib.nixosSystem) config.__nixosHosts; 252 }; 253 }; 254 }; 255 parent = 256 args@{ config, options, ... }: 257 let 258 makerType = optionTypes.functionArgsTo 2 types.attrs; 259 in 260 { 261 _file = ./flake.nix; 262 imports = attrValues modules; 263 # TODO: Organize these into subsets. 264 options = { 265 src = mkOption { 266 type = types.path; 267 default = args.src or (dirOf (unsafeGetAttrPos (head (attrNames module)) module).file); 268 apply = toString; 269 }; 270 sources = mkOption { 271 type = 272 with types; 273 coercedTo 274 (oneOf [ 275 str 276 path 277 (listOf str) 278 ]) 279 (compose [ 280 toList 281 (map (src: config.src + "/" + src)) 282 (filter pathExists) 283 ]) 284 (listOf path); 285 }; 286 inputs = mkOption { 287 type = types.attrs; 288 289 # TODO: This might slow things down. 290 apply = mapAttrs (n: v: if (isAttrsOnly v) then (formatOutputs { flake = v; }) else v); 291 292 }; 293 self = mkOption { 294 type = types.attrs; 295 default = config.inputs.self; 296 }; 297 makers = mkOption { type = types.attrsOf makerType; }; 298 maker = mkOption { 299 type = 300 with types; 301 coercedTo (enum (builtins.attrNames config.makers)) (flip getAttr config.makers) makerType; 302 default = "default"; 303 }; 304 305 # NOTE: Remember, `anything' matches first, so everything will be converted: 306 # https://github.com/NixOS/nixpkgs/blob/eba10d3bc41567ef00c0f7afd6ff4a763d578c1b/lib/types.nix#L1532-L1537 307 outputs = mkOption { 308 type = 309 with types; 310 coercedTo anything (compose [ 311 (outputs: if (path.check outputs) then (builtins.import outputs) else outputs) 312 (config.maker config) 313 ]) attrs; 314 default = { }; 315 }; 316 passthru = { 317 system = mkOption { 318 type = types.nullOr systemType; 319 default = null; 320 }; 321 outputs = mkOption { 322 type = types.attrs; 323 default = { }; 324 }; 325 }; 326 327 systems = 328 let 329 default = defaultSystems; 330 in 331 mkOption { 332 inherit default; 333 type = 334 with types; 335 let 336 systemType = enum default; 337 in 338 coercedTo (either systemType (listOf systemType)) (compose [ 339 toList 340 (flip genAttrs (system: { })) 341 ]) attrs; 342 }; 343 nixpkgs = mkOption { 344 type = with types; nullOr (either attrs (functionTo attrs)); 345 default = 346 let 347 nixpkgs = attrValues (filterAttrs (n: v: hasPrefix "nixpkgs" n) config.inputs); 348 in 349 if (nixpkgs == [ ]) then null else (head nixpkgs); 350 }; 351 settings = mkOption { 352 type = 353 with types; 354 submodule ( 355 { ... }: 356 { 357 options = { 358 config = mkOption { 359 type = attrs; 360 default = { }; 361 }; 362 overlays = mkOption { 363 type = coercedTo (either str (listOf str)) ( 364 pkgs: 365 genAttrs (toList pkgs) ( 366 pkg: 367 config.inputs.${pkg}.overlays.default or (final: prev: { 368 ${pkg} = config.inputs.${pkg}.packages.${final.stdenv.targetPlatform.system}.default; 369 }) 370 ) 371 ) (attrsOf ((optionTypes.functionArgsTo 2 attrs))); 372 default = { }; 373 }; 374 }; 375 } 376 ); 377 }; 378 specialArgs = mkOption { 379 type = types.attrs; 380 default = { }; 381 }; 382 }; 383 config = { 384 sources = [ 385 "nix/sources.nix" 386 "npins" 387 ]; 388 inputs = mkMerge (map builtins.import config.sources); 389 makers = mapAttrs (n: mkDefault) { 390 default = 391 config: outputs: 392 let 393 evalModule = 394 specialArgs: 395 let 396 system = specialArgs.system or null; 397 module = 398 (lib.evalModules { 399 class = "outputs"; 400 modules = [ 401 child 402 403 # Adapted From: https://github.com/NixOS/nixpkgs/blob/df3de70468b743b371a7191ea61d1040fe3bf399/lib/modules.nix#L567-L570 404 (setDefaultModuleLocation "${config.src}/flake.nix" outputs) 405 ]; 406 specialArgs = 407 config 408 // { 409 inherit (config) lib; 410 parent = { inherit config options; }; 411 } 412 // specialArgs 413 // config.specialArgs; 414 }).config.outputs; 415 in 416 mkMerge [ 417 (mapAttrs 418 ( 419 n: v: 420 if (!(specialArgs ? system)) then 421 module.${n} 422 else 423 { 424 ${system} = v; 425 426 # TODO: Modularize this. 427 } 428 ) 429 ( 430 removeAttrs module [ 431 "passthru" 432 "nixosConfigurations" 433 ] 434 ) 435 ) 436 (mkIf ((module.passthru.system or null) == system) module.passthru.outputs) 437 (mkIf (module ? nixosConfigurations) { 438 inherit (module) nixosConfigurations; 439 }) 440 441 ]; 442 in 443 if (isAttrs outputs) then 444 outputs 445 else if 446 (hasAnyAttrs (builtins.functionArgs outputs) [ 447 "system" 448 "pkgs" 449 ]) 450 then 451 (mkMerge ( 452 map (compose [ 453 (mk.system config) 454 evalModule 455 ]) (attrNames config.systems) 456 )) 457 else 458 (evalModule { }); 459 flake-parts = 460 config: outputs: 461 config.inputs.flake-parts.lib.mkFlake { 462 inherit (config) inputs; 463 } (if (isFunction outputs) then outputs else ({ systems = attrNames config.systems; } // outputs)); 464 }; 465 outputs = 466 let 467 srcDir = builtins.readDir config.src; 468 in 469 mkMerge [ 470 471 ( 472 let 473 outputs = config.src + "/outputs.nix"; 474 in 475 mkIf ((srcDir."outputs.nix" or "directory") != "directory") outputs 476 ) 477 478 ( 479 let 480 outputs = config.src + "/outputs"; 481 in 482 mkIf ( 483 ((srcDir.outputs or "file") == "directory") && ((builtins.readDir outputs) ? "default.nix") 484 ) outputs 485 ) 486 487 config.passthru.outputs 488 489 ]; 490 }; 491 }; 492 493 # TODO: Revise what the child needs, but shouldn't modify. 494 child = 495 args@{ 496 parent, 497 config, 498 options, 499 ... 500 }: 501 { 502 _file = ./flake.nix; 503 imports = attrValues modules; 504 options = { 505 inherit (parent.options) 506 maker 507 makers 508 specialArgs 509 settings 510 outputs 511 passthru 512 ; 513 src = mkOption { 514 inherit (parent.options.src) type; 515 readOnly = true; 516 }; 517 sources = mkOption { 518 type = parent.options.sources.nestedTypes.finalType; 519 readOnly = true; 520 }; 521 inputs = mkOption { 522 inherit (parent.options.inputs) type; 523 readOnly = true; 524 }; 525 self = mkOption { 526 inherit (parent.options.self) type; 527 readOnly = true; 528 }; 529 systems = mkOption { 530 inherit (parent.options.systems) type; 531 readOnly = true; 532 }; 533 nixpkgs = mkOption { 534 inherit (parent.options.nixpkgs) type; 535 default = 536 let 537 nixpkgs = attrValues (filterAttrs (n: v: hasPrefix "nixpkgs" n) config.inputs); 538 in 539 if (args.nixpkgs != null) then 540 args.nixpkgs 541 else if (nixpkgs == [ ]) then 542 null 543 else 544 (head nixpkgs); 545 }; 546 }; 547 config = 548 let 549 parentOpts = attrNames parent.options; 550 childOpts = attrNames options; 551 sameOpts = parentOpts == childOpts; 552 missingOpts = { 553 parent = filter (opt: !(elem opt childOpts)) parentOpts; 554 child = filter (opt: !(elem opt parentOpts)) childOpts; 555 }; 556 in 557 if sameOpts then 558 { 559 inherit (args) 560 src 561 sources 562 inputs 563 self 564 maker 565 makers 566 specialArgs 567 settings 568 systems 569 lib 570 modules 571 572 # TODO: Won't these duplicate lists when provided an attribute set, for example? 573 # And what about the options above? 574 hosts 575 nixosConfigurations 576 ; 577 outputs = mkIf ((args.system or null) == config.passthru.system) { 578 inherit (config) passthru; 579 }; 580 } 581 else 582 ( 583 let 584 childMessage = "Child options [${concatStringsSep " " missingOpts.child}] are missing from the parent module."; 585 parentMessage = "Parent options [${concatStringsSep " " missingOpts.parent}] are missing from the child module."; 586 in 587 throw ( 588 if (missingOpts.parent == [ ]) then 589 childMessage 590 else if (missingOpts.child == [ ]) then 591 parentMessage 592 else 593 '' 594 ${parentMessage} 595 ${childMessage} 596 '' 597 ) 598 ); 599 }; 600 in 601 formatOutputs { 602 flake = 603 (lib.evalModules { 604 class = "flake"; 605 modules = [ 606 parent 607 608 # Adapted From: https://github.com/NixOS/nixpkgs/blob/df3de70468b743b371a7191ea61d1040fe3bf399/lib/modules.nix#L567-L570 609 ( 610 { config, ... }: 611 { 612 imports = 613 let 614 moduleLocation = "${config.src}/flake.nix"; 615 in 616 [ 617 { 618 _file = moduleLocation; 619 _module.args = { 620 inherit moduleLocation; 621 } 622 // config.specialArgs; 623 imports = [ module ]; 624 } 625 ]; 626 config.outputs.simpleflake = config; 627 } 628 ) 629 ]; 630 }).config.outputs; 631 }; 632 633 settings = 634 config: system: 635 config.settings 636 // { 637 inherit system; 638 hostPlatform = system; 639 overlays = attrValues config.settings.overlays; 640 }; 641 642 system = 643 config: system: 644 let 645 settings = mk.settings config system; 646 in 647 { 648 inherit system; 649 } 650 // (optionalAttrs (config.nixpkgs != null) { 651 pkgs = import config.nixpkgs settings; 652 }); 653 }; 654 655 optionTypes.functionArgsTo = argNumber: compose (replicate argNumber types.functionTo); 656 657 formatOutputs = 658 args@{ 659 getFlake ? false, 660 ... 661 }: 662 let 663 _flake = args.flake; 664 systemsList = builtins.attrNames ( 665 _flake.simpleflake.systems or _flake.checks or _flake.packages or _flake.apps or _flake.formatter 666 or _flake.devShells or _flake.legacyPackages or (genAttrs lib.systems.flakeExposed id) 667 ); 668 flake = { 669 inherit lib; 670 } 671 // _flake; 672 systems = 673 let 674 allSystems = lib.genAttrs systemsList (system: lib.mapAttrs (n: v: v.${system} or v) flake); 675 in 676 allSystems 677 // { 678 currentSystem = allSystems.${builtins.currentSystem}; 679 }; 680 in 681 ( 682 optionalAttrs (!getFlake) { inherit systems; } 683 // (lib.mapAttrs ( 684 n: v: 685 if ((isAttrsOnly v) && (builtins.hasAttr (head systemsList) v)) then 686 (v // { currentSystem = systems.currentSystem.${n}; }) 687 else 688 v 689 ) flake) 690 ) 691 // { 692 inputs = 693 flake.simpleflake.inputs 694 or (mapAttrs (n: v: if (isAttrsOnly v) then (formatOutputs { flake = v; }) else v) ( 695 flake.inputs or { } 696 )); 697 }; 698 699}