diff --git a/builders.ncl b/builders.ncl index 546fbcb5..f7a05bb6 100644 --- a/builders.ncl +++ b/builders.ncl @@ -19,12 +19,12 @@ let { NickelDerivation, .. } = import "contracts.ncl" in name = "hello", version = "0.1", build_command = { - cmd = nix-s%"%{inputs.bash}/bin/bash"%, + cmd = nix-s%"%{inputs.nixpkgs.bash}/bin/bash"%, args = [ "-c", nix-s%" - %{inputs.gcc}/bin/gcc %{nix.lib.import_file "hello.c"} -o hello - %{inputs.coreutils}/bin/mkdir -p $out/bin - %{inputs.coreutils}/bin/cp hello $out/bin/hello + %{inputs.nixpkgs.gcc}/bin/gcc %{nix.lib.import_file "hello.c"} -o hello + %{inputs.nixpkgs.coreutils}/bin/mkdir -p $out/bin + %{inputs.nixpkgs.coreutils}/bin/cp hello $out/bin/hello "% ] }, @@ -35,16 +35,7 @@ let { NickelDerivation, .. } = import "contracts.ncl" in = NickelDerivation, BashShell = { - inputs_spec = { - bash = {}, - # Setting the following as a default value conflicts with InputsSpec's - # contract default value (`"nixpkgs"`). Maybe InputsSpec shouldn't set a - # default value at all? - # That being said, `naked_std_env` is an internal compatibility value: - # there doesn't seem to be a good reason to make it a default value. It - # can still be overridden using `force` if really needed - naked_std_env.input = "nickel-nix-internals", - }, + inputs_spec = {}, inputs, output = @@ -54,14 +45,14 @@ let { NickelDerivation, .. } = import "contracts.ncl" in # This is required otherwise nix develop will fail with a message that it # only supports bash. build_command = { - cmd = nix-s%"%{inputs.bash}/bin/bash"%, + cmd = nix-s%"%{inputs.nixpkgs.bash}/bin/bash"%, args = [], }, structured_env = { # TODO: handle naked derivations without having to interpolate - stdenv.naked_std_env = nix-s%"%{inputs.naked_std_env}"%, - PATH.bash = nix-s%"%{inputs.bash}/bin"%, + stdenv.naked_std_env = nix-s%"%{inputs.nickel-nix-internals.naked_std_env}"%, + PATH.bash = nix-s%"%{inputs.nixpkgs.bash}/bin"%, }, # Compute `env` from `structured_env`. Note that thanks to recursive @@ -85,20 +76,15 @@ let { NickelDerivation, .. } = import "contracts.ncl" in RustShell = BashShell & { - inputs_spec = { - cargo = {}, - rustc = {}, - rustfmt = {}, - rust-analyzer = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - cargo = nix-s%"%{inputs.cargo}/bin"%, - rustc = nix-s%"%{inputs.rustc}/bin"%, - rustfmt = nix-s%"%{inputs.rustfmt}/bin"%, - rust-analyzer = nix-s%"%{inputs.rust-analyzer}/bin"%, + cargo = nix-s%"%{inputs.nixpkgs.cargo}/bin"%, + rustc = nix-s%"%{inputs.nixpkgs.rustc}/bin"%, + rustfmt = nix-s%"%{inputs.nixpkgs.rustfmt}/bin"%, + rust-analyzer = nix-s%"%{inputs.nixpkgs.rust-analyzer}/bin"%, }, }, }, @@ -106,16 +92,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in GoShell = BashShell & { - inputs_spec = { - go = {}, - gopls = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - go = nix-s%"%{inputs.go}/bin"%, - gopls = nix-s%"%{inputs.gopls}/bin"%, + go = nix-s%"%{inputs.nixpkgs.go}/bin"%, + gopls = nix-s%"%{inputs.nixpkgs.gopls}/bin"%, }, }, }, @@ -123,16 +106,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in ClojureShell = BashShell & { - inputs_spec = { - clojure = {}, - clojure-lsp = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - clojure = nix-s%"%{inputs.clojure}/bin"%, - clojure-lsp = nix-s%"%{inputs.clojure-lsp}/bin"%, + clojure = nix-s%"%{inputs.nixpkgs.clojure}/bin"%, + clojure-lsp = nix-s%"%{inputs.nixpkgs.clojure-lsp}/bin"%, }, }, }, @@ -140,16 +120,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in CShell = BashShell & { - inputs_spec = { - clang = {}, - clang-tools = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - clang = nix-s%"%{inputs.clang}/bin"%, - clang-tools = nix-s%"%{inputs.clang-tools}/bin"%, + clang = nix-s%"%{inputs.nixpkgs.clang}/bin"%, + clang-tools = nix-s%"%{inputs.nixpkgs.clang-tools}/bin"%, }, }, }, @@ -158,18 +135,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in PhpShell = BashShell & { - inputs_spec = { - php = {}, - intelephense = { - path = "nodePackages"."intelephense", - }, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - php = nix-s%"%{inputs.php}/bin"%, - intelephense = nix-s%"%{inputs.intelephense}/bin"%, + php = nix-s%"%{inputs.nixpkgs.php}/bin"%, + intelephense = nix-s%"%{inputs.nixpkgs.nodePackages.intelephense}/bin"%, }, }, }, @@ -177,16 +149,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in ZigShell = BashShell & { - inputs_spec = { - zig = {}, - zls = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - zig = nix-s%"%{inputs.zig}/bin"%, - zls = nix-s%"%{inputs.zls}/bin"%, + zig = nix-s%"%{inputs.nixpkgs.zig}/bin"%, + zls = nix-s%"%{inputs.nixpkgs.zls}/bin"%, }, }, }, @@ -195,18 +164,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in JavascriptShell = BashShell & { - inputs_spec = { - nodejs = {}, - typescript-language-server = { - path = "nodePackages_latest.typescript-language-server", - } - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - nodejs = nix-s%"%{inputs.nodejs}/bin"%, - ts-lsp = nix-s%"%{inputs.typescript-language-server}/bin"%, + nodejs = nix-s%"%{inputs.nixpkgs.nodejs}/bin"%, + ts-lsp = nix-s%"%{inputs.nixpkgs.nodePackages_latest.typescript-language-server}/bin"%, }, }, }, @@ -214,14 +178,12 @@ let { NickelDerivation, .. } = import "contracts.ncl" in RacketShell = BashShell & { - inputs_spec = { - racket = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - racket = nix-s%"%{inputs.racket}/bin"%, + racket = nix-s%"%{inputs.nixpkgs.racket}/bin"%, }, }, }, @@ -229,16 +191,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in ScalaShell = BashShell & { - inputs_spec = { - scala = {}, - metals = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - scala = nix-s%"%{inputs.scala}/bin"%, - metals = nix-s%"%{inputs.metals}/bin"%, + scala = nix-s%"%{inputs.nixpkgs.scala}/bin"%, + metals = nix-s%"%{inputs.nixpkgs.metals}/bin"%, }, }, }, @@ -248,18 +207,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in Python310Shell = BashShell & { - inputs_spec = { - python310 = {}, - python-lsp-server = { - path = "python310Packages.python-lsp-server", - } - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - python = nix-s%"%{inputs.python310}/bin"%, - python-lsp = nix-s%"%{inputs.python-lsp-server}/bin"%, + python = nix-s%"%{inputs.nixpkgs.python310}/bin"%, + python-lsp = nix-s%"%{inputs.nixpkgs.python310Packages.python-lsp-server}/bin"%, }, }, }, @@ -267,16 +221,13 @@ let { NickelDerivation, .. } = import "contracts.ncl" in ErlangShell = BashShell & { - inputs_spec = { - erlang = {}, - erlang-ls = {}, - }, + inputs_spec = {}, inputs, output.structured_env = { PATH = { - erlang = nix-s%"%{inputs.erlang}/bin"%, - erlang-lsp = nix-s%"%{inputs.erlang-ls}/bin"%, + erlang = nix-s%"%{inputs.nixpkgs.erlang}/bin"%, + erlang-lsp = nix-s%"%{inputs.nixpkgs.erlang-ls}/bin"%, }, }, }, @@ -286,14 +237,6 @@ let { NickelDerivation, .. } = import "contracts.ncl" in & { ghcVersion, # User-defined. To keep in sync with the one used by stack inputs_spec = { - stack = {}, - ormolu = {}, - nix = {}, - git = {}, - coreutils = {}, - haskell-language-server = { - path = "haskell.packages.ghc%{ghcVersion}.haskell-language-server", - }, # This will point to a copy of nixpkgs in nix store path = {}, }, @@ -303,21 +246,21 @@ let { NickelDerivation, .. } = import "contracts.ncl" in let stack-wrapped = { name = "stack-wrapped", - version = inputs.stack.version, + version = inputs.nixpkgs.stack.version, build_command = { - cmd = nix-s%"%{inputs.bash}/bin/bash"%, + cmd = nix-s%"%{inputs.nixpkgs.bash}/bin/bash"%, args = [ "-c", # Sorry about Topiary formatting of the following lines nix-s%" - export PATH="%{inputs.coreutils}/bin:$PATH" + export PATH="%{inputs.nixpkgs.coreutils}/bin:$PATH" mkdir -p $out/bin echo "$0" > $out/bin/stack chmod a+x $out/bin/* "%, nix-s%" - #!%{inputs.bash}/bin/bash - %{inputs.stack}/bin/stack \ + #!%{inputs.nixpkgs.bash}/bin/bash + %{inputs.nixpkgs.stack}/bin/stack \ --nix \ --no-nix-pure \ --nix-path="nixpkgs=%{inputs.path}" \ @@ -330,11 +273,11 @@ let { NickelDerivation, .. } = import "contracts.ncl" in { PATH = { stack = nix-s%"%{stack-wrapped}/bin"%, - stack' = nix-s%"%{inputs.stack}/bin"%, - ormolu = nix-s%"%{inputs.ormolu}/bin"%, - nix = nix-s%"%{inputs.nix}/bin"%, - git = nix-s%"%{inputs.git}/bin"%, - haskell-language-server = nix-s%"%{inputs.haskell-language-server}/bin"%, + stack' = nix-s%"%{inputs.nixpkgs.stack}/bin"%, + ormolu = nix-s%"%{inputs.nixpkgs.ormolu}/bin"%, + nix = nix-s%"%{inputs.nixpkgs.nix}/bin"%, + git = nix-s%"%{inputs.nixpkgs.git}/bin"%, + haskell-language-server = nix-s%"%{inputs.nixpkgs.haskell.packages.ghc%{ghcVersion}.haskell-language-server}/bin"%, }, }, }, diff --git a/lib.nix b/lib.nix index a9d6c8f7..dcb5aba3 100644 --- a/lib.nix +++ b/lib.nix @@ -109,10 +109,10 @@ // value.env; # Import a Nickel value produced by the Nixel DSL - importFromNickel = context: system: baseDir: value: let + importFromNickel = flakeInputs: system: baseDir: value: let type = builtins.typeOf value; isNickelDerivation = type: type == "nickelDerivation"; - importFromNickel_ = importFromNickel context system baseDir; + importFromNickel_ = importFromNickel flakeInputs system baseDir; in if (type == "set") then @@ -128,7 +128,7 @@ in derivation prepared else if nixelType == "nixDerivation" - then context.${value.drvPath}.${value.outputName or "out"} + then lib.getAttrFromPath value.attrPath flakeInputs else if nixelType == "nixString" then builtins.concatStringsSep "" (builtins.map importFromNickel_ value.fragments) else if nixelType == "nixPath" @@ -145,6 +145,7 @@ baseDir, nickelFile, exportedPkgsNcl, + flakeInputs, }: let sources = builtins.path { path = baseDir; @@ -159,7 +160,15 @@ nickelWithImports = builtins.toFile "eval.ncl" '' let params = { - inputs = import "${exportedJSON}", + inputs = (import "${exportedJSON}") & { + ${lib.concatStrings (lib.mapAttrsToList (name: input: '' + "${name}" = import "${ + # FIXME: Should cache in Nix derivation, but evaluating Nix inside Nix is troublesome + builtins.toFile "${name}.ncl" (builtins.unsafeDiscardStringContext (collectPkgs name input)) + }", + '') + flakeInputs)} + }, system = "${system}", nix = import "${./.}/nix.ncl", } @@ -276,7 +285,7 @@ {inherit declaredInputs flakeInputs baseDir;}; pkgsExportResult = exportForNickel exportedPkgs.value; fileToCall = computeNickelFile { - inherit baseDir nickelFile; + inherit baseDir nickelFile flakeInputs; exportedPkgsNcl = pkgsExportResult.value; }; in @@ -295,7 +304,7 @@ flakeInputsWithInternals = flakeInputs // { - nickel-nix-internals = builtins.mapAttrs (_: f: f flakeInputs) nickel-nix-internals; + nickel-nix-internals = {packages.${system} = builtins.mapAttrs (_: f: f flakeInputs) nickel-nix-internals;}; }; nickelResult = callNickel { inherit nickelFile baseDir; @@ -303,6 +312,78 @@ }; in {rawNickel = nickelResult.value;} - // lib.traceVal (importFromNickel nickelResult.context system baseDir (builtins.fromJSON + // lib.traceVal (importFromNickel flakeInputsWithInternals system baseDir (builtins.fromJSON (builtins.unsafeDiscardStringContext (builtins.readFile nickelResult.value)))); + + doCollect = attrPath: drvF: attrs: let + try = builtins.tryEval attrs; + result = try.value; + in + if !try.success || !(lib.isAttrs result) + then [] + else if lib.isDerivation result + then [(drvF attrPath result)] + else if attrPath != [] && (!(result.recurseForDerivations or false)) + then [] + else + [(lib.optionalString (attrPath != []) "\"${lib.last attrPath}\" = ") "{\n"] + ++ lib.concatLists (lib.mapAttrsToList (name: doCollect (attrPath ++ [name]) drvF) result) + ++ ["}" (lib.optionalString (attrPath != []) ",\n")]; + + collectPkgs = name: input': let + input = + if name != "nixpkgs" + then input' + else smallNixpkgs input'; + listToNcl = sep: lst: lib.concatStringsSep sep (map (x: ''"${x}"'') lst); + drvF = attrPrefix: attrPath: drv: '' + "${lib.last attrPath}"={"${typeField}"="nixDerivation",outputPath="${drv.outPath}",attrPath=[${listToNcl "," (attrPrefix ++ attrPath)}]}, + ''; + drvsWithPaths = lib.concatLists ( + (lib.optional (input ? packages && input.packages ? ${system}) (doCollect [] (drvF [name "packages" system]) input.packages.${system})) + ++ (lib.optional (input ? legacyPackages && input.legacyPackages ? ${system}) (doCollect [] (drvF [name "legacyPackages" system]) input.legacyPackages.${system})) + ); + in + if builtins.length drvsWithPaths == 0 + then "{}" + else lib.concatStrings drvsWithPaths; + + # FIXME: This is only a small subset of nixpkgs required by our shells. + # Full nixpkgs cannot be handled by Nickel because of + # https://github.com/tweag/nickel/issues/1427 and https://github.com/tweag/nickel/issues/1428 + smallNixpkgs = nixpkgs: { + legacyPackages.${system} = { + inherit + (nixpkgs.legacyPackages.${system}) + bash + cargo + clang + clang-tools + clojure + clojure-lsp + coreutils + erlang + erlang-ls + git + go + gopls + haskell + metals + nix + nodejs + ormolu + path + php + python310 + racket + rust-analyzer + rustc + rustfmt + scala + stack + zig + zls + ; + }; + }; in {inherit importNcl;} diff --git a/naked-stdenv.ncl b/naked-stdenv.ncl index 34cf792b..c1363d77 100644 --- a/naked-stdenv.ncl +++ b/naked-stdenv.ncl @@ -1,8 +1,5 @@ { - inputs_spec = { - bash = { input = "nixpkgs" }, - coreutils = { input = "nixpkgs" }, - }, + inputs_spec = {}, # parameters inputs, @@ -13,14 +10,14 @@ name = "naked-stdenv", version = "0.1", build_command = { - cmd = nix-s%"%{inputs.bash}/bin/bash"%, + cmd = nix-s%"%{inputs.nixpkgs.bash}/bin/bash"%, args = [ "-c", nix-s%" - %{inputs.coreutils}/bin/mkdir -p $out + %{inputs.nixpkgs.coreutils}/bin/mkdir -p $out target=$out/setup - %{inputs.coreutils}/bin/touch $target - %{inputs.coreutils}/bin/cp %{nix.lib.import_file "naked-stdenv.sh"} $target + %{inputs.nixpkgs.coreutils}/bin/touch $target + %{inputs.nixpkgs.coreutils}/bin/cp %{nix.lib.import_file "naked-stdenv.sh"} $target "%, ], },