diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/flake/call-flake.nix index a63e09dc41e..798196f14b3 100644 --- a/src/libexpr/flake/call-flake.nix +++ b/src/libexpr/flake/call-flake.nix @@ -37,29 +37,42 @@ let builtins.mapAttrs (key: node: let - - sourceInfo = + # FIXME: remove obsolete node.info. + tree0 = if overrides ? ${key} then overrides.${key}.sourceInfo else - # FIXME: remove obsolete node.info. - let - tree = - fetchTree (node.info or {} // removeAttrs node.locked ["dir"]); - in tree // { - # TODO: return the path value without fetching to the store? - # removing this will improve performance, but may break - # one or two flakes, that rely on - # `builtins.typeOf outPath` for some reason, or perhaps - # something more subtle than that, despite our conservative - # choice of lazy path semantics. - outPath = "${tree.outPath}"; - }; + fetchTree (node.info or {} // removeAttrs node.locked [ + # attributes that are applied after fetching + "dir" + "outPathIsString" + ]); + + # TODO: split this logic into stand-alone functions + getSourceInfo = coerceOutPathToString: + tree0 // { + # TODO: return the path value without fetching to the store? + # removing this will improve performance, but may break + # one or two flakes, that rely on + # `builtins.typeOf outPath` for some reason, or perhaps + # something more subtle than that, despite our conservative + # choice of lazy path semantics. + outPath = if coerceOutPathToString then "${tree0.outPath}" else tree0.outPath; + }; subdir = overrides.${key}.dir or node.locked.dir or ""; - outPath = sourceInfo + ((if subdir == "" then "" else "/") + subdir); + getOutPath = coerceOutPathToString: + getSourceInfo coerceOutPathToString + ((if subdir == "" then "" else "/") + subdir); + + outPath = getOutPath outPathIsString; + sourceInfo = getSourceInfo outPathIsString; + + flake0 = import (getOutPath false + "/flake.nix"); + + # Users can opt in to the legacy behavior of outPath being a string. + outPathIsString = flake0.inputs.self.outPathIsString or false; flake = import (outPath + "/flake.nix"); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 14fd1666bed..e430cd1fc40 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -118,6 +118,8 @@ static FlakeInput parseFlakeInput( } else { attrs.erase("url"); + // TODO: if (isRoot) ... + attrs.erase("outPathIsString"); if (!attrs.empty()) throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]); if (url) @@ -411,7 +413,16 @@ LockedFlake lockFlake( from what's in the lock file). */ for (auto & [id, input2] : flakeInputs) { auto inputPath(inputPathPrefix); - inputPath.push_back(id); + if (id == "self") { + // TODO validate that it only has `outPathIsString` (for now) + // TODO accept hints for fetching schemas such as `git` or `github` + // so that the fetching of the flake can be customized as + // needed in the flake itself. + // TODO allow certain schemas to be rejected, e.g. inputs.self.hints."github" = throw "this flake must be fetch with the `git:` scheme in order to load submodules"; + continue; + } else { + inputPath.push_back(id); + } auto inputPathS = printInputPath(inputPath); debug("computing input '%s'", inputPathS);