mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-23 22:46:16 +02:00
Checkpoint
This commit is contained in:
parent
e827e6288f
commit
3ec83565b1
11 changed files with 116 additions and 39 deletions
|
@ -2070,14 +2070,14 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
auto path2 = unpackPath(path);
|
auto path2 = unpackPath(path);
|
||||||
#if 0
|
#if 0
|
||||||
auto p = settings.readOnlyMode
|
auto p = settings.readOnlyMode
|
||||||
? store->computeStorePathForPath(std::string(baseNameOf(path)), canonPath(path)).first
|
? store->computeStorePathForPath(path2.baseName(), canonPath(path)).first
|
||||||
: store->addToStore(std::string(baseNameOf(path)), canonPath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
|
: store->addToStore(path2.baseName(), canonPath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
|
||||||
#endif
|
#endif
|
||||||
auto source = sinkToSource([&](Sink & sink) {
|
auto source = sinkToSource([&](Sink & sink) {
|
||||||
path2.accessor->dumpPath(path2.path, sink);
|
path2.accessor->dumpPath(path2.path, sink);
|
||||||
});
|
});
|
||||||
// FIXME: readOnlyMode
|
// FIXME: readOnlyMode
|
||||||
auto p = store->addToStoreFromDump(*source, std::string(baseNameOf(path)), FileIngestionMethod::Recursive, htSHA256, repair);
|
auto p = store->addToStoreFromDump(*source, path2.baseName(), FileIngestionMethod::Recursive, htSHA256, repair);
|
||||||
dstPath = store->printStorePath(p);
|
dstPath = store->printStorePath(p);
|
||||||
allowPath(p);
|
allowPath(p);
|
||||||
srcToStore.insert_or_assign(path, std::move(p));
|
srcToStore.insert_or_assign(path, std::move(p));
|
||||||
|
|
|
@ -209,7 +209,7 @@ static Flake getFlake(
|
||||||
.originalRef = originalRef,
|
.originalRef = originalRef,
|
||||||
.resolvedRef = resolvedRef,
|
.resolvedRef = resolvedRef,
|
||||||
.lockedRef = lockedRef,
|
.lockedRef = lockedRef,
|
||||||
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
//.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!pathExists(flakeFile))
|
if (!pathExists(flakeFile))
|
||||||
|
@ -326,6 +326,7 @@ LockedFlake lockFlake(
|
||||||
state.store->setOptions();
|
state.store->setOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// FIXME: symlink attack
|
// FIXME: symlink attack
|
||||||
|
@ -669,6 +670,9 @@ LockedFlake lockFlake(
|
||||||
e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string());
|
e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
throw UnimplementedError("lockFlake");
|
||||||
}
|
}
|
||||||
|
|
||||||
void callFlake(EvalState & state,
|
void callFlake(EvalState & state,
|
||||||
|
@ -683,13 +687,17 @@ void callFlake(EvalState & state,
|
||||||
|
|
||||||
vLocks->mkString(lockedFlake.lockFile.to_string());
|
vLocks->mkString(lockedFlake.lockFile.to_string());
|
||||||
|
|
||||||
|
#if 0
|
||||||
emitTreeAttrs(
|
emitTreeAttrs(
|
||||||
state,
|
state,
|
||||||
*lockedFlake.flake.sourceInfo,
|
//*lockedFlake.flake.sourceInfo,
|
||||||
lockedFlake.flake.lockedRef.input,
|
lockedFlake.flake.lockedRef.input,
|
||||||
*vRootSrc,
|
*vRootSrc,
|
||||||
false,
|
false,
|
||||||
lockedFlake.flake.forceDirty);
|
lockedFlake.flake.forceDirty);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
throw UnimplementedError("callFlake");
|
||||||
|
|
||||||
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
||||||
|
|
||||||
|
@ -756,7 +764,8 @@ Fingerprint LockedFlake::getFingerprint() const
|
||||||
// flake.sourceInfo.storePath for the fingerprint.
|
// flake.sourceInfo.storePath for the fingerprint.
|
||||||
return hashString(htSHA256,
|
return hashString(htSHA256,
|
||||||
fmt("%s;%s;%d;%d;%s",
|
fmt("%s;%s;%d;%d;%s",
|
||||||
flake.sourceInfo->storePath.to_string(),
|
"FIXME",
|
||||||
|
//flake.sourceInfo->storePath.to_string(),
|
||||||
flake.lockedRef.subdir,
|
flake.lockedRef.subdir,
|
||||||
flake.lockedRef.input.getRevCount().value_or(0),
|
flake.lockedRef.input.getRevCount().value_or(0),
|
||||||
flake.lockedRef.input.getLastModified().value_or(0),
|
flake.lockedRef.input.getLastModified().value_or(0),
|
||||||
|
|
|
@ -63,7 +63,6 @@ struct Flake
|
||||||
FlakeRef lockedRef; // the specific local store result of invoking the fetcher
|
FlakeRef lockedRef; // the specific local store result of invoking the fetcher
|
||||||
bool forceDirty = false; // pretend that 'lockedRef' is dirty
|
bool forceDirty = false; // pretend that 'lockedRef' is dirty
|
||||||
std::optional<std::string> description;
|
std::optional<std::string> description;
|
||||||
std::shared_ptr<const fetchers::Tree> sourceInfo;
|
|
||||||
FlakeInputs inputs;
|
FlakeInputs inputs;
|
||||||
ConfigFile config; // 'nixConfig' attribute
|
ConfigFile config; // 'nixConfig' attribute
|
||||||
~Flake();
|
~Flake();
|
||||||
|
@ -139,7 +138,7 @@ void callFlake(
|
||||||
|
|
||||||
void emitTreeAttrs(
|
void emitTreeAttrs(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const fetchers::Tree & tree,
|
const SourcePath & path,
|
||||||
const fetchers::Input & input,
|
const fetchers::Input & input,
|
||||||
Value & v,
|
Value & v,
|
||||||
bool emptyRevFallback = false,
|
bool emptyRevFallback = false,
|
||||||
|
|
|
@ -16,15 +16,14 @@ Path EvalState::packPath(const SourcePath & path)
|
||||||
|
|
||||||
SourcePath EvalState::unpackPath(const Path & path)
|
SourcePath EvalState::unpackPath(const Path & path)
|
||||||
{
|
{
|
||||||
|
printError("UNPACK %s", path);
|
||||||
if (hasPrefix(path, marker)) {
|
if (hasPrefix(path, marker)) {
|
||||||
auto s = path.substr(marker.size());
|
auto s = path.substr(marker.size());
|
||||||
auto slash = s.find('/');
|
auto slash = s.find('/');
|
||||||
assert(slash != s.npos);
|
|
||||||
auto n = std::stoi(s.substr(0, slash));
|
auto n = std::stoi(s.substr(0, slash));
|
||||||
printError("GOT %d", n);
|
|
||||||
auto i = inputAccessors.find(n);
|
auto i = inputAccessors.find(n);
|
||||||
assert(i != inputAccessors.end());
|
assert(i != inputAccessors.end());
|
||||||
return {i->second, s.substr(slash)};
|
return {i->second, slash != std::string::npos ? s.substr(slash) : "/"};
|
||||||
} else {
|
} else {
|
||||||
printError("FIXME: %s", path);
|
printError("FIXME: %s", path);
|
||||||
return rootPath(path);
|
return rootPath(path);
|
||||||
|
|
|
@ -13,25 +13,32 @@ namespace nix {
|
||||||
|
|
||||||
void emitTreeAttrs(
|
void emitTreeAttrs(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const fetchers::Tree & tree,
|
const SourcePath & path,
|
||||||
const fetchers::Input & input,
|
const fetchers::Input & input,
|
||||||
Value & v,
|
Value & v,
|
||||||
bool emptyRevFallback,
|
bool emptyRevFallback,
|
||||||
bool forceDirty)
|
bool forceDirty)
|
||||||
{
|
{
|
||||||
assert(input.isLocked());
|
// FIXME?
|
||||||
|
//assert(input.isLocked());
|
||||||
|
|
||||||
auto attrs = state.buildBindings(8);
|
auto attrs = state.buildBindings(8);
|
||||||
|
|
||||||
|
#if 0
|
||||||
auto storePath = state.store->printStorePath(tree.storePath);
|
auto storePath = state.store->printStorePath(tree.storePath);
|
||||||
|
|
||||||
attrs.alloc(state.sOutPath).mkString(storePath, {storePath});
|
attrs.alloc(state.sOutPath).mkString(storePath, {storePath});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
attrs.alloc(state.sOutPath).mkPath(state.packPath(path));
|
||||||
|
|
||||||
// FIXME: support arbitrary input attributes.
|
// FIXME: support arbitrary input attributes.
|
||||||
|
|
||||||
|
#if 0
|
||||||
auto narHash = input.getNarHash();
|
auto narHash = input.getNarHash();
|
||||||
assert(narHash);
|
assert(narHash);
|
||||||
attrs.alloc("narHash").mkString(narHash->to_string(SRI, true));
|
attrs.alloc("narHash").mkString(narHash->to_string(SRI, true));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (input.getType() == "git")
|
if (input.getType() == "git")
|
||||||
attrs.alloc("submodules").mkBool(
|
attrs.alloc("submodules").mkBool(
|
||||||
|
@ -169,11 +176,17 @@ static void fetchTree(
|
||||||
if (evalSettings.pureEval && !input.isLocked())
|
if (evalSettings.pureEval && !input.isLocked())
|
||||||
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
|
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
|
||||||
|
|
||||||
auto [tree, input2] = input.fetch(state.store);
|
auto [accessor, input2] = input.lazyFetch(state.store);
|
||||||
|
|
||||||
state.allowPath(tree.storePath);
|
//state.allowPath(tree.storePath);
|
||||||
|
|
||||||
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
emitTreeAttrs(
|
||||||
|
state,
|
||||||
|
{accessor, "/"},
|
||||||
|
input2,
|
||||||
|
v,
|
||||||
|
params.emptyRevFallback,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
|
|
@ -172,6 +172,19 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
|
||||||
return {std::move(tree), input};
|
return {std::move(tree), input};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<ref<InputAccessor>, Input> Input::lazyFetch(ref<Store> store) const
|
||||||
|
{
|
||||||
|
if (!scheme)
|
||||||
|
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
return scheme->lazyFetch(store, *this);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace({}, "while fetching the input '%s'", to_string());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Input Input::applyOverrides(
|
Input Input::applyOverrides(
|
||||||
std::optional<std::string> ref,
|
std::optional<std::string> ref,
|
||||||
std::optional<Hash> rev) const
|
std::optional<Hash> rev) const
|
||||||
|
@ -289,4 +302,11 @@ void InputScheme::clone(const Input & input, const Path & destDir)
|
||||||
throw Error("do not know how to clone input '%s'", input.to_string());
|
throw Error("do not know how to clone input '%s'", input.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<ref<InputAccessor>, Input> InputScheme::lazyFetch(ref<Store> store, const Input & input)
|
||||||
|
{
|
||||||
|
auto [storePath, input2] = fetch(store, input);
|
||||||
|
|
||||||
|
return {makeFSInputAccessor(store->toRealPath(storePath)), input2};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,11 @@ public:
|
||||||
the Nix store and the locked input. */
|
the Nix store and the locked input. */
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store) const;
|
std::pair<Tree, Input> fetch(ref<Store> store) const;
|
||||||
|
|
||||||
|
/* Return an InputAccessor that allows access to files in the
|
||||||
|
input without copying it to the store. Also return a possibly
|
||||||
|
unlocked input. */
|
||||||
|
std::pair<ref<InputAccessor>, Input> lazyFetch(ref<Store> store) const;
|
||||||
|
|
||||||
Input applyOverrides(
|
Input applyOverrides(
|
||||||
std::optional<std::string> ref,
|
std::optional<std::string> ref,
|
||||||
std::optional<Hash> rev) const;
|
std::optional<Hash> rev) const;
|
||||||
|
@ -132,6 +137,8 @@ struct InputScheme
|
||||||
|
|
||||||
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
|
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
|
||||||
|
|
||||||
|
virtual std::pair<ref<InputAccessor>, Input> lazyFetch(ref<Store> store, const Input & input);
|
||||||
|
|
||||||
virtual ref<InputAccessor> getAccessor()
|
virtual ref<InputAccessor> getAccessor()
|
||||||
{
|
{
|
||||||
throw UnimplementedError("getAccessor");
|
throw UnimplementedError("getAccessor");
|
||||||
|
|
|
@ -14,7 +14,7 @@ InputAccessor::InputAccessor()
|
||||||
// FIXME: merge with archive.cc.
|
// FIXME: merge with archive.cc.
|
||||||
const std::string narVersionMagic1 = "nix-archive-1";
|
const std::string narVersionMagic1 = "nix-archive-1";
|
||||||
|
|
||||||
static string caseHackSuffix = "~nix~case~hack~";
|
static std::string caseHackSuffix = "~nix~case~hack~";
|
||||||
|
|
||||||
void InputAccessor::dumpPath(
|
void InputAccessor::dumpPath(
|
||||||
const Path & path,
|
const Path & path,
|
||||||
|
@ -51,12 +51,12 @@ void InputAccessor::dumpPath(
|
||||||
|
|
||||||
/* If we're on a case-insensitive system like macOS, undo
|
/* If we're on a case-insensitive system like macOS, undo
|
||||||
the case hack applied by restorePath(). */
|
the case hack applied by restorePath(). */
|
||||||
std::map<string, string> unhacked;
|
std::map<std::string, std::string> unhacked;
|
||||||
for (auto & i : readDirectory(path))
|
for (auto & i : readDirectory(path))
|
||||||
if (/* archiveSettings.useCaseHack */ false) { // FIXME
|
if (/* archiveSettings.useCaseHack */ false) { // FIXME
|
||||||
string name(i.first);
|
std::string name(i.first);
|
||||||
size_t pos = i.first.find(caseHackSuffix);
|
size_t pos = i.first.find(caseHackSuffix);
|
||||||
if (pos != string::npos) {
|
if (pos != std::string::npos) {
|
||||||
debug(format("removing case hack suffix from '%s'") % (path + "/" + i.first));
|
debug(format("removing case hack suffix from '%s'") % (path + "/" + i.first));
|
||||||
name.erase(pos);
|
name.erase(pos);
|
||||||
}
|
}
|
||||||
|
@ -227,4 +227,10 @@ ref<MemoryInputAccessor> makeMemoryInputAccessor()
|
||||||
return make_ref<MemoryInputAccessorImpl>();
|
return make_ref<MemoryInputAccessorImpl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string_view SourcePath::baseName() const
|
||||||
|
{
|
||||||
|
// FIXME
|
||||||
|
return path == "" || path == "/" ? "source" : baseNameOf(path);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,8 @@ struct SourcePath
|
||||||
{
|
{
|
||||||
ref<InputAccessor> accessor;
|
ref<InputAccessor> accessor;
|
||||||
Path path;
|
Path path;
|
||||||
|
|
||||||
|
std::string_view baseName() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const SourcePath & path);
|
std::ostream & operator << (std::ostream & str, const SourcePath & path);
|
||||||
|
|
|
@ -81,29 +81,36 @@ struct PathInputScheme : InputScheme
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path getAbsPath(ref<Store> store, const Input & input)
|
||||||
|
{
|
||||||
|
auto path = getStrAttr(input.attrs, "path");
|
||||||
|
|
||||||
|
if (path[0] == '/')
|
||||||
|
return path;
|
||||||
|
|
||||||
|
if (!input.parent)
|
||||||
|
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||||
|
|
||||||
|
auto parent = canonPath(*input.parent);
|
||||||
|
|
||||||
|
// the path isn't relative, prefix it
|
||||||
|
auto absPath = nix::absPath(path, parent);
|
||||||
|
|
||||||
|
// for security, ensure that if the parent is a store path, it's inside it
|
||||||
|
if (store->isInStore(parent)) {
|
||||||
|
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
||||||
|
if (!isDirOrInDir(absPath, storePath))
|
||||||
|
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return absPath;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
||||||
{
|
{
|
||||||
Input input(_input);
|
Input input(_input);
|
||||||
std::string absPath;
|
|
||||||
auto path = getStrAttr(input.attrs, "path");
|
|
||||||
|
|
||||||
if (path[0] != '/') {
|
auto absPath = getAbsPath(store, input);
|
||||||
if (!input.parent)
|
|
||||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
|
||||||
|
|
||||||
auto parent = canonPath(*input.parent);
|
|
||||||
|
|
||||||
// the path isn't relative, prefix it
|
|
||||||
absPath = nix::absPath(path, parent);
|
|
||||||
|
|
||||||
// for security, ensure that if the parent is a store path, it's inside it
|
|
||||||
if (store->isInStore(parent)) {
|
|
||||||
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
|
||||||
if (!isDirOrInDir(absPath, storePath))
|
|
||||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
absPath = path;
|
|
||||||
|
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
||||||
|
|
||||||
|
@ -125,6 +132,14 @@ struct PathInputScheme : InputScheme
|
||||||
|
|
||||||
return {std::move(*storePath), input};
|
return {std::move(*storePath), input};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<ref<InputAccessor>, Input> lazyFetch(ref<Store> store, const Input & input) override
|
||||||
|
{
|
||||||
|
auto absPath = getAbsPath(store, input);
|
||||||
|
auto input2(input);
|
||||||
|
input2.attrs.emplace("path", absPath);
|
||||||
|
return {makeFSInputAccessor(absPath), std::move(input2)};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static auto rPathInputScheme = OnStartup([] { registerInputScheme(std::make_unique<PathInputScheme>()); });
|
static auto rPathInputScheme = OnStartup([] { registerInputScheme(std::make_unique<PathInputScheme>()); });
|
||||||
|
|
|
@ -182,7 +182,9 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
|
||||||
j["revCount"] = *revCount;
|
j["revCount"] = *revCount;
|
||||||
if (auto lastModified = flake.lockedRef.input.getLastModified())
|
if (auto lastModified = flake.lockedRef.input.getLastModified())
|
||||||
j["lastModified"] = *lastModified;
|
j["lastModified"] = *lastModified;
|
||||||
|
#if 0
|
||||||
j["path"] = store->printStorePath(flake.sourceInfo->storePath);
|
j["path"] = store->printStorePath(flake.sourceInfo->storePath);
|
||||||
|
#endif
|
||||||
j["locks"] = lockedFlake.lockFile.toJSON();
|
j["locks"] = lockedFlake.lockFile.toJSON();
|
||||||
logger->cout("%s", j.dump());
|
logger->cout("%s", j.dump());
|
||||||
} else {
|
} else {
|
||||||
|
@ -196,9 +198,11 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
|
||||||
logger->cout(
|
logger->cout(
|
||||||
ANSI_BOLD "Description:" ANSI_NORMAL " %s",
|
ANSI_BOLD "Description:" ANSI_NORMAL " %s",
|
||||||
*flake.description);
|
*flake.description);
|
||||||
|
#if 0
|
||||||
logger->cout(
|
logger->cout(
|
||||||
ANSI_BOLD "Path:" ANSI_NORMAL " %s",
|
ANSI_BOLD "Path:" ANSI_NORMAL " %s",
|
||||||
store->printStorePath(flake.sourceInfo->storePath));
|
store->printStorePath(flake.sourceInfo->storePath));
|
||||||
|
#endif
|
||||||
if (auto rev = flake.lockedRef.input.getRev())
|
if (auto rev = flake.lockedRef.input.getRev())
|
||||||
logger->cout(
|
logger->cout(
|
||||||
ANSI_BOLD "Revision:" ANSI_NORMAL " %s",
|
ANSI_BOLD "Revision:" ANSI_NORMAL " %s",
|
||||||
|
@ -881,6 +885,8 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
|
||||||
|
|
||||||
StorePathSet sources;
|
StorePathSet sources;
|
||||||
|
|
||||||
|
throw UnimplementedError("flake archive");
|
||||||
|
#if 0
|
||||||
sources.insert(flake.flake.sourceInfo->storePath);
|
sources.insert(flake.flake.sourceInfo->storePath);
|
||||||
if (jsonRoot)
|
if (jsonRoot)
|
||||||
jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath));
|
jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath));
|
||||||
|
@ -911,6 +917,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
|
||||||
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
|
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
|
||||||
copyPaths(*store, *dstStore, sources);
|
copyPaths(*store, *dstStore, sources);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue