forked from lix-project/lix
Improve path:// handling
In particular, doing 'nix build /path/to/dir' now works if /path/to/dir is not a Git tree (it only has to contain a flake.nix file). Also, 'nix flake init' no longer requires a Git tree (but it will do a 'git add flake.nix' if it's a Git tree)
This commit is contained in:
parent
829dcb35d5
commit
6521c92ce8
|
@ -176,7 +176,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
if (!attrs.empty())
|
if (!attrs.empty())
|
||||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
||||||
if (url)
|
if (url)
|
||||||
input.ref = parseFlakeRef(*url);
|
input.ref = parseFlakeRef(*url, {}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return input;
|
return input;
|
||||||
|
@ -630,7 +630,7 @@ void callFlake(EvalState & state,
|
||||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
auto flakeRefS = state.forceStringNoCtx(*args[0], pos);
|
auto flakeRefS = state.forceStringNoCtx(*args[0], pos);
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS);
|
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||||
if (evalSettings.pureEval && !flakeRef.input->isImmutable())
|
if (evalSettings.pureEval && !flakeRef.input->isImmutable())
|
||||||
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
|
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
|
||||||
|
|
||||||
|
|
|
@ -47,9 +47,9 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
|
||||||
}
|
}
|
||||||
|
|
||||||
FlakeRef parseFlakeRef(
|
FlakeRef parseFlakeRef(
|
||||||
const std::string & url, const std::optional<Path> & baseDir)
|
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
|
||||||
{
|
{
|
||||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir);
|
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing);
|
||||||
if (fragment != "")
|
if (fragment != "")
|
||||||
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
||||||
return flakeRef;
|
return flakeRef;
|
||||||
|
@ -66,7 +66,7 @@ std::optional<FlakeRef> maybeParseFlakeRef(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir)
|
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
|
||||||
{
|
{
|
||||||
using namespace fetchers;
|
using namespace fetchers;
|
||||||
|
|
||||||
|
@ -115,17 +115,16 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
if (!S_ISDIR(lstat(path).st_mode))
|
if (!S_ISDIR(lstat(path).st_mode))
|
||||||
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
||||||
|
|
||||||
|
if (!allowMissing && !pathExists(path + "/flake.nix"))
|
||||||
|
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
|
||||||
|
|
||||||
|
auto fragment = percentDecode(std::string(match[3]));
|
||||||
|
|
||||||
auto flakeRoot = path;
|
auto flakeRoot = path;
|
||||||
std::string subdir;
|
std::string subdir;
|
||||||
|
|
||||||
while (true) {
|
while (flakeRoot != "/") {
|
||||||
if (pathExists(flakeRoot + "/.git")) break;
|
if (pathExists(flakeRoot + "/.git")) {
|
||||||
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
|
||||||
flakeRoot = dirOf(flakeRoot);
|
|
||||||
if (flakeRoot == "/")
|
|
||||||
throw BadURL("path '%s' is not a flake (because it does not reference a Git repository)", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto base = std::string("git+file://") + flakeRoot;
|
auto base = std::string("git+file://") + flakeRoot;
|
||||||
|
|
||||||
auto parsedURL = ParsedURL{
|
auto parsedURL = ParsedURL{
|
||||||
|
@ -137,8 +136,6 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
.query = decodeQuery(match[2]),
|
.query = decodeQuery(match[2]),
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fragment = percentDecode(std::string(match[3]));
|
|
||||||
|
|
||||||
if (subdir != "") {
|
if (subdir != "") {
|
||||||
if (parsedURL.query.count("dir"))
|
if (parsedURL.query.count("dir"))
|
||||||
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
|
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
|
||||||
|
@ -150,6 +147,17 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
fragment);
|
fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
||||||
|
flakeRoot = dirOf(flakeRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchers::Attrs attrs;
|
||||||
|
attrs.insert_or_assign("type", "path");
|
||||||
|
attrs.insert_or_assign("path", path);
|
||||||
|
|
||||||
|
return std::make_pair(FlakeRef(inputFromAttrs(attrs), ""), fragment);
|
||||||
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
auto parsedURL = parseURL(url);
|
auto parsedURL = parseURL(url);
|
||||||
std::string fragment;
|
std::string fragment;
|
||||||
|
|
|
@ -41,13 +41,13 @@ struct FlakeRef
|
||||||
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
||||||
|
|
||||||
FlakeRef parseFlakeRef(
|
FlakeRef parseFlakeRef(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
|
||||||
|
|
||||||
std::optional<FlakeRef> maybeParseFlake(
|
std::optional<FlakeRef> maybeParseFlake(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||||
|
|
||||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
|
||||||
|
|
||||||
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct PathInput : Input
|
||||||
|
|
||||||
bool isImmutable() const override
|
bool isImmutable() const override
|
||||||
{
|
{
|
||||||
return (bool) narHash;
|
return narHash || rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParsedURL toURL() const override
|
ParsedURL toURL() const override
|
||||||
|
@ -56,9 +56,20 @@ struct PathInput : Input
|
||||||
attrs.emplace("revCount", *revCount);
|
attrs.emplace("revCount", *revCount);
|
||||||
if (lastModified)
|
if (lastModified)
|
||||||
attrs.emplace("lastModified", *lastModified);
|
attrs.emplace("lastModified", *lastModified);
|
||||||
|
if (!rev && narHash)
|
||||||
|
attrs.emplace("narHash", narHash->to_string(SRI));
|
||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Path> getSourcePath() const override
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void markChangedFile(std::string_view file, std::optional<std::string> commitMsg) const override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<Tree, std::shared_ptr<const Input>> fetchTreeInternal(nix::ref<Store> store) const override
|
std::pair<Tree, std::shared_ptr<const Input>> fetchTreeInternal(nix::ref<Store> store) const override
|
||||||
{
|
{
|
||||||
auto input = std::make_shared<PathInput>(*this);
|
auto input = std::make_shared<PathInput>(*this);
|
||||||
|
@ -74,6 +85,8 @@ struct PathInput : Input
|
||||||
// FIXME: try to substitute storePath.
|
// FIXME: try to substitute storePath.
|
||||||
storePath = store->addToStore("source", path);
|
storePath = store->addToStore("source", path);
|
||||||
|
|
||||||
|
input->narHash = store->queryPathInfo(*storePath)->narHash;
|
||||||
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
Tree {
|
Tree {
|
||||||
|
@ -99,6 +112,9 @@ struct PathInputScheme : InputScheme
|
||||||
auto input = std::make_unique<PathInput>();
|
auto input = std::make_unique<PathInput>();
|
||||||
input->path = url.path;
|
input->path = url.path;
|
||||||
|
|
||||||
|
if (url.authority && *url.authority != "")
|
||||||
|
throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority);
|
||||||
|
|
||||||
for (auto & [name, value] : url.query)
|
for (auto & [name, value] : url.query)
|
||||||
if (name == "rev")
|
if (name == "rev")
|
||||||
input->rev = Hash(value, htSHA1);
|
input->rev = Hash(value, htSHA1);
|
||||||
|
@ -114,6 +130,9 @@ struct PathInputScheme : InputScheme
|
||||||
throw Error("path URL '%s' has invalid parameter '%s'", url.to_string(), name);
|
throw Error("path URL '%s' has invalid parameter '%s'", url.to_string(), name);
|
||||||
input->lastModified = lastModified;
|
input->lastModified = lastModified;
|
||||||
}
|
}
|
||||||
|
else if (name == "narHash")
|
||||||
|
// FIXME: require SRI hash.
|
||||||
|
input->narHash = Hash(value);
|
||||||
else
|
else
|
||||||
throw Error("path URL '%s' has unsupported parameter '%s'", url.to_string(), name);
|
throw Error("path URL '%s' has unsupported parameter '%s'", url.to_string(), name);
|
||||||
|
|
||||||
|
@ -134,6 +153,9 @@ struct PathInputScheme : InputScheme
|
||||||
input->revCount = getIntAttr(attrs, "revCount");
|
input->revCount = getIntAttr(attrs, "revCount");
|
||||||
else if (name == "lastModified")
|
else if (name == "lastModified")
|
||||||
input->lastModified = getIntAttr(attrs, "lastModified");
|
input->lastModified = getIntAttr(attrs, "lastModified");
|
||||||
|
else if (name == "narHash")
|
||||||
|
// FIXME: require SRI hash.
|
||||||
|
input->narHash = Hash(getStrAttr(attrs, "narHash"));
|
||||||
else if (name == "type" || name == "path")
|
else if (name == "type" || name == "path")
|
||||||
;
|
;
|
||||||
else
|
else
|
||||||
|
|
|
@ -551,17 +551,18 @@ struct CmdFlakeInit : virtual Args, Command
|
||||||
{
|
{
|
||||||
Path flakeDir = absPath(".");
|
Path flakeDir = absPath(".");
|
||||||
|
|
||||||
if (!pathExists(flakeDir + "/.git"))
|
|
||||||
throw Error("the directory '%s' is not a Git repository", flakeDir);
|
|
||||||
|
|
||||||
Path flakePath = flakeDir + "/flake.nix";
|
Path flakePath = flakeDir + "/flake.nix";
|
||||||
|
|
||||||
if (pathExists(flakePath))
|
if (pathExists(flakePath))
|
||||||
throw Error("file '%s' already exists", flakePath);
|
throw Error("file '%s' already exists", flakePath);
|
||||||
|
|
||||||
writeFile(flakePath,
|
writeFile(flakePath,
|
||||||
#include "flake-template.nix.gen.hh"
|
#include "flake-template.nix.gen.hh"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (pathExists(flakeDir + "/.git"))
|
||||||
|
runProgram("git", true,
|
||||||
|
{ "-C", flakeDir, "add", "--intent-to-add", "flake.nix" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -438,14 +438,6 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
auto follow = [&](const std::string & s) -> std::optional<StorePath> {
|
|
||||||
try {
|
|
||||||
return store->followLinksToStorePath(s);
|
|
||||||
} catch (NotInStore &) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto & s : ss) {
|
for (auto & s : ss) {
|
||||||
if (hasPrefix(s, "nixpkgs.")) {
|
if (hasPrefix(s, "nixpkgs.")) {
|
||||||
bool static warned;
|
bool static warned;
|
||||||
|
@ -456,23 +448,38 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
auto res = maybeParseFlakeRefWithFragment(s, absPath("."));
|
std::exception_ptr ex;
|
||||||
if (res) {
|
|
||||||
auto &[flakeRef, fragment] = *res;
|
try {
|
||||||
|
auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath("."));
|
||||||
result.push_back(std::make_shared<InstallableFlake>(
|
result.push_back(std::make_shared<InstallableFlake>(
|
||||||
*this, std::move(flakeRef),
|
*this, std::move(flakeRef),
|
||||||
fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
|
fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
|
||||||
getDefaultFlakeAttrPathPrefixes()));
|
getDefaultFlakeAttrPathPrefixes()));
|
||||||
} else {
|
continue;
|
||||||
std::optional<StorePath> storePath;
|
} catch (...) {
|
||||||
if (s.find('/') != std::string::npos && (storePath = follow(s)))
|
ex = std::current_exception();
|
||||||
result.push_back(std::make_shared<InstallableStorePath>(store, store->printStorePath(*storePath)));
|
}
|
||||||
else
|
|
||||||
|
if (s.find('/') != std::string::npos) {
|
||||||
|
try {
|
||||||
|
result.push_back(std::make_shared<InstallableStorePath>(store, store->printStorePath(store->followLinksToStorePath(s))));
|
||||||
|
continue;
|
||||||
|
} catch (NotInStore &) {
|
||||||
|
} catch (...) {
|
||||||
|
if (!ex)
|
||||||
|
ex = std::current_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::rethrow_exception(ex);
|
||||||
|
|
||||||
|
/*
|
||||||
throw Error(
|
throw Error(
|
||||||
pathExists(s)
|
pathExists(s)
|
||||||
? "path '%s' is not a flake or a store path"
|
? "path '%s' is not a flake or a store path"
|
||||||
: "don't know how to handle argument '%s'", s);
|
: "don't know how to handle argument '%s'", s);
|
||||||
}
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue