1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-07 10:11:47 +02:00

Merge branch 'master' into paths-from-stdin

This commit is contained in:
Théophane Hufschmitt 2023-03-02 19:20:51 +01:00 committed by GitHub
commit 1f394d2107
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 322 additions and 119 deletions

View file

@ -31,27 +31,24 @@ InstallableDerivedPath InstallableDerivedPath::parse(
ExtendedOutputsSpec extendedOutputsSpec)
{
auto derivedPath = std::visit(overloaded {
// If the user did not use ^, we treat the output more liberally.
// If the user did not use ^, we treat the output more
// liberally: we accept a symlink chain or an actual
// store path.
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
// First, we accept a symlink chain or an actual store path.
auto storePath = store->followLinksToStorePath(prefix);
// Second, we see if the store path ends in `.drv` to decide what sort
// of derived path they want.
//
// This handling predates the `^` syntax. The `^*` in
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
// also unambiguously mean "do the DerivedPath::Opaque` case".
//
// Issue #7261 tracks reconsidering this `.drv` dispatching.
return storePath.isDerivation()
? (DerivedPath) DerivedPath::Built {
.drvPath = std::move(storePath),
.outputs = OutputsSpec::All {},
}
: (DerivedPath) DerivedPath::Opaque {
.path = std::move(storePath),
// Remove this prior to stabilizing the new CLI.
if (storePath.isDerivation()) {
auto oldDerivedPath = DerivedPath::Built {
.drvPath = storePath,
.outputs = OutputsSpec::All { },
};
warn(
"The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '%s'",
oldDerivedPath.to_string(*store));
};
return DerivedPath::Opaque {
.path = std::move(storePath),
};
},
// If the user did use ^, we just do exactly what is written.
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {

View file

@ -677,9 +677,12 @@ StorePathSet Installable::toDerivations(
for (const auto & b : i->toDerivedPaths())
std::visit(overloaded {
[&](const DerivedPath::Opaque & bo) {
if (!useDeriver)
throw Error("argument '%s' did not evaluate to a derivation", i->what());
drvPaths.insert(getDeriver(store, *i, bo.path));
drvPaths.insert(
bo.path.isDerivation()
? bo.path
: useDeriver
? getDeriver(store, *i, bo.path)
: throw Error("argument '%s' did not evaluate to a derivation", i->what()));
},
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);

View file

@ -63,6 +63,11 @@ public:
one that contains a commit hash or content hash. */
bool isLocked() const { return locked; }
/* Check whether the input carries all necessary info required
for cache insertion and substitution.
These fields are used to uniquely identify cached trees
within the "tarball TTL" window without necessarily
indicating that the input's origin is unchanged. */
bool hasAllInfo() const;
bool operator ==(const Input & other) const;

View file

@ -92,13 +92,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
if (S_ISLNK(dstSt.st_mode)) {
auto prevPriority = state.priorities[dstFile];
if (prevPriority == priority)
throw Error(
"files '%1%' and '%2%' have the same priority %3%; "
"use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' "
"or type 'nix profile install --help' if using 'nix profile' to find out how "
"to change the priority of one of the conflicting packages"
" (0 being the highest priority)",
srcFile, readLink(dstFile), priority);
throw BuildEnvFileConflictError(
readLink(dstFile),
srcFile,
priority
);
if (prevPriority < priority)
continue;
if (unlink(dstFile.c_str()) == -1)

View file

@ -12,6 +12,32 @@ struct Package {
Package(const Path & path, bool active, int priority) : path{path}, active{active}, priority{priority} {}
};
class BuildEnvFileConflictError : public Error
{
public:
const Path fileA;
const Path fileB;
int priority;
BuildEnvFileConflictError(
const Path fileA,
const Path fileB,
int priority
)
: Error(
"Unable to build profile. There is a conflict for the following files:\n"
"\n"
" %1%\n"
" %2%",
fileA,
fileB
)
, fileA(fileA)
, fileB(fileB)
, priority(priority)
{}
};
typedef std::vector<Package> Packages;
void buildProfile(const Path & out, Packages && pkgs);

View file

@ -88,6 +88,10 @@ struct curlFileTransfer : public FileTransfer
{request.uri}, request.parentAct)
, callback(std::move(callback))
, finalSink([this](std::string_view data) {
if (errorSink) {
(*errorSink)(data);
}
if (this->request.dataCallback) {
auto httpStatus = getHTTPStatus();
@ -163,8 +167,6 @@ struct curlFileTransfer : public FileTransfer
}
}
if (errorSink)
(*errorSink)({(char *) contents, realSize});
(*decompressionSink)({(char *) contents, realSize});
return realSize;

View file

@ -38,8 +38,6 @@ class RemoteStore : public virtual RemoteStoreConfig,
{
public:
virtual bool sameMachine() = 0;
RemoteStore(const Params & params);
/* Implementations of abstract store API methods. */

View file

@ -49,9 +49,6 @@ public:
return *uriSchemes().begin() + "://" + host;
}
bool sameMachine() override
{ return false; }
// FIXME extend daemon protocol, move implementation to RemoteStore
std::optional<std::string> getBuildLogExact(const StorePath & path) override
{ unsupported("getBuildLogExact"); }

View file

@ -855,6 +855,7 @@ json Store::pathInfoToJSON(const StorePathSet & storePaths,
auto info = queryPathInfo(storePath);
jsonPath["path"] = printStorePath(info->path);
jsonPath["valid"] = true;
jsonPath["narHash"] = info->narHash.to_string(hashBase, true);
jsonPath["narSize"] = info->narSize;

View file

@ -29,9 +29,6 @@ public:
static std::set<std::string> uriSchemes()
{ return {"unix"}; }
bool sameMachine() override
{ return true; }
ref<FSAccessor> getFSAccessor() override
{ return LocalFSStore::getFSAccessor(); }

View file

@ -32,7 +32,8 @@ void Logger::warn(const std::string & msg)
void Logger::writeToStdout(std::string_view s)
{
std::cout << s << "\n";
writeFull(STDOUT_FILENO, s);
writeFull(STDOUT_FILENO, "\n");
}
class SimpleLogger : public Logger
@ -84,7 +85,7 @@ public:
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
const std::string & s, const Fields & fields, ActivityId parent)
override
override
{
if (lvl <= verbosity && !s.empty())
log(lvl, s + "...");

View file

@ -102,11 +102,9 @@ public:
virtual void writeToStdout(std::string_view s);
template<typename... Args>
inline void cout(const std::string & fs, const Args & ... args)
inline void cout(const Args & ... args)
{
boost::format f(fs);
formatHelper(f, args...);
writeToStdout(f.str());
writeToStdout(fmt(args...));
}
virtual std::optional<char> ask(std::string_view s)

View file

@ -139,11 +139,11 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
for (auto & buildable : buildables) {
std::visit(overloaded {
[&](const BuiltPath::Opaque & bo) {
std::cout << store->printStorePath(bo.path) << std::endl;
logger->cout(store->printStorePath(bo.path));
},
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
std::cout << store->printStorePath(output.second) << std::endl;
logger->cout(store->printStorePath(output.second));
}
},
}, buildable.path.raw());

View file

@ -17,7 +17,7 @@ struct MixCat : virtual Args
if (st.type != FSAccessor::Type::tRegular)
throw Error("path '%1%' is not a regular file", path);
std::cout << accessor->readFile(path);
writeFull(STDOUT_FILENO, accessor->readFile(path));
}
};

View file

@ -25,7 +25,7 @@ struct CmdDescribeStores : Command, MixJSON
res[storeName] = storeConfig->toJSON();
}
if (json) {
std::cout << res;
logger->cout("%s", res);
} else {
for (auto & [storeName, storeConfig] : res.items()) {
std::cout << "## " << storeName << std::endl << std::endl;

View file

@ -97,7 +97,7 @@ void printClosureDiff(
items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added)));
if (showDelta)
items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0));
std::cout << fmt("%s%s: %s\n", indent, name, concatStringsSep(", ", items));
logger->cout("%s%s: %s", indent, name, concatStringsSep(", ", items));
}
}
}

View file

@ -112,11 +112,11 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption
else if (raw) {
stopProgressBar();
std::cout << *state->coerceToString(noPos, *v, context, "while generating the eval command output");
writeFull(STDOUT_FILENO, *state->coerceToString(noPos, *v, context, "while generating the eval command output"));
}
else if (json) {
std::cout << printValueAsJSON(*state, true, *v, pos, context, false).dump() << std::endl;
logger->cout("%s", printValueAsJSON(*state, true, *v, pos, context, false));
}
else {

View file

@ -952,7 +952,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
{"path", store->printStorePath(flake.flake.sourceInfo->storePath)},
{"inputs", traverse(*flake.lockFile.root)},
};
std::cout << jsonRoot.dump() << std::endl;
logger->cout("%s", jsonRoot);
} else {
traverse(*flake.lockFile.root);
}

View file

@ -53,7 +53,7 @@ struct CmdLog : InstallableCommand
if (!log) continue;
stopProgressBar();
printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri());
std::cout << *log;
writeFull(STDOUT_FILENO, *log);
return;
}

View file

@ -93,7 +93,7 @@ struct MixLs : virtual Args, MixJSON
if (json) {
if (showDirectory)
throw UsageError("'--directory' is useless with '--json'");
std::cout << listNar(accessor, path, recursive);
logger->cout("%s", listNar(accessor, path, recursive));
} else
listText(accessor);
}

View file

@ -292,7 +292,7 @@ void mainWrapped(int argc, char * * argv)
NixArgs args;
if (argc == 2 && std::string(argv[1]) == "__dump-args") {
std::cout << args.toJSON().dump() << "\n";
logger->cout("%s", args.toJSON());
return;
}
@ -312,7 +312,7 @@ void mainWrapped(int argc, char * * argv)
b["doc"] = trim(stripIndentation(primOp->doc));
res[state.symbols[builtin.name]] = std::move(b);
}
std::cout << res.dump() << "\n";
logger->cout("%s", res);
return;
}
@ -321,14 +321,14 @@ void mainWrapped(int argc, char * * argv)
if (completions) {
switch (completionType) {
case ctNormal:
std::cout << "normal\n"; break;
logger->cout("normal"); break;
case ctFilenames:
std::cout << "filenames\n"; break;
logger->cout("filenames"); break;
case ctAttrs:
std::cout << "attrs\n"; break;
logger->cout("attrs"); break;
}
for (auto & s : *completions)
std::cout << s.completion << "\t" << trim(s.description) << "\n";
logger->cout(s.completion + "\t" + trim(s.description));
}
});

View file

@ -45,7 +45,7 @@ struct CmdMakeContentAddressed : virtual CopyCommand, virtual StorePathsCommand,
}
auto json = json::object();
json["rewrites"] = jsonRewrites;
std::cout << json.dump();
logger->cout("%s", json);
} else {
for (auto & path : storePaths) {
auto i = remappings.find(path);

View file

@ -234,9 +234,9 @@ static int main_nix_prefetch_url(int argc, char * * argv)
if (!printPath)
printInfo("path is '%s'", store->printStorePath(storePath));
std::cout << printHash16or32(hash) << std::endl;
logger->cout(printHash16or32(hash));
if (printPath)
std::cout << store->printStorePath(storePath) << std::endl;
logger->cout(store->printStorePath(storePath));
return 0;
}

View file

@ -228,12 +228,12 @@ struct ProfileManifest
while (i != prevElems.end() || j != curElems.end()) {
if (j != curElems.end() && (i == prevElems.end() || i->describe() > j->describe())) {
std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions());
logger->cout("%s%s: ∅ -> %s", indent, j->describe(), j->versions());
changes = true;
++j;
}
else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) {
std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions());
logger->cout("%s%s: %s -> ∅", indent, i->describe(), i->versions());
changes = true;
++i;
}
@ -241,7 +241,7 @@ struct ProfileManifest
auto v1 = i->versions();
auto v2 = j->versions();
if (v1 != v2) {
std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2);
logger->cout("%s%s: %s -> %s", indent, i->describe(), v1, v2);
changes = true;
}
++i;
@ -250,7 +250,7 @@ struct ProfileManifest
}
if (!changes)
std::cout << fmt("%sNo changes.\n", indent);
logger->cout("%sNo changes.", indent);
}
};
@ -330,7 +330,63 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
manifest.elements.push_back(std::move(element));
}
updateProfile(manifest.build(store));
try {
updateProfile(manifest.build(store));
} catch (BuildEnvFileConflictError & conflictError) {
// FIXME use C++20 std::ranges once macOS has it
// See https://github.com/NixOS/nix/compare/3efa476c5439f8f6c1968a6ba20a31d1239c2f04..1fe5d172ece51a619e879c4b86f603d9495cc102
auto findRefByFilePath = [&]<typename Iterator>(Iterator begin, Iterator end) {
for (auto it = begin; it != end; it++) {
auto profileElement = *it;
for (auto & storePath : profileElement.storePaths) {
if (conflictError.fileA.starts_with(store->printStorePath(storePath))) {
return std::pair(conflictError.fileA, profileElement.source->originalRef);
}
if (conflictError.fileB.starts_with(store->printStorePath(storePath))) {
return std::pair(conflictError.fileB, profileElement.source->originalRef);
}
}
}
throw conflictError;
};
// There are 2 conflicting files. We need to find out which one is from the already installed package and
// which one is the package that is the new package that is being installed.
// The first matching package is the one that was already installed (original).
auto [originalConflictingFilePath, originalConflictingRef] = findRefByFilePath(manifest.elements.begin(), manifest.elements.end());
// The last matching package is the one that was going to be installed (new).
auto [newConflictingFilePath, newConflictingRef] = findRefByFilePath(manifest.elements.rbegin(), manifest.elements.rend());
throw Error(
"An existing package already provides the following file:\n"
"\n"
" %1%\n"
"\n"
"This is the conflicting file from the new package:\n"
"\n"
" %2%\n"
"\n"
"To remove the existing package:\n"
"\n"
" nix profile remove %3%\n"
"\n"
"The new package can also be installed next to the existing one by assigning a different priority.\n"
"The conflicting packages have a priority of %5%.\n"
"To prioritise the new package:\n"
"\n"
" nix profile install %4% --priority %6%\n"
"\n"
"To prioritise the existing package:\n"
"\n"
" nix profile install %4% --priority %7%\n",
originalConflictingFilePath,
newConflictingFilePath,
originalConflictingRef.to_string(),
newConflictingRef.to_string(),
conflictError.priority,
conflictError.priority - 1,
conflictError.priority + 1
);
}
}
};
@ -584,9 +640,9 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile
for (auto & gen : gens) {
if (prevGen) {
if (!first) std::cout << "\n";
if (!first) logger->cout("");
first = false;
std::cout << fmt("Version %d -> %d:\n", prevGen->number, gen.number);
logger->cout("Version %d -> %d:", prevGen->number, gen.number);
printClosureDiff(store,
store->followLinksToStorePath(prevGen->path),
store->followLinksToStorePath(gen.path),
@ -622,10 +678,10 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile
for (auto & gen : gens) {
ProfileManifest manifest(*getEvalState(), gen.path);
if (!first) std::cout << "\n";
if (!first) logger->cout("");
first = false;
std::cout << fmt("Version %s%d" ANSI_NORMAL " (%s)%s:\n",
logger->cout("Version %s%d" ANSI_NORMAL " (%s)%s:",
gen.number == curGen ? ANSI_GREEN : ANSI_BOLD,
gen.number,
std::put_time(std::gmtime(&gen.creationTime), "%Y-%m-%d"),

View file

@ -65,18 +65,16 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
res.push_back(currentPath);
}
std::cout << res.dump();
logger->cout("%s", res);
}
else {
for (auto & path : realisations) {
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
std::cout <<
realisation->id.to_string() << " " <<
store->printStorePath(realisation->outPath);
logger->cout("%s %s",
realisation->id.to_string(),
store->printStorePath(realisation->outPath));
} else
std::cout << store->printStorePath(path.path());
std::cout << std::endl;
logger->cout("%s", store->printStorePath(path.path()));
}
}
}

View file

@ -196,9 +196,8 @@ struct CmdSearch : InstallableCommand, MixJSON
for (auto & cursor : installable->getCursors(*state))
visit(*cursor, cursor->getAttrPath(), true);
if (json) {
std::cout << jsonOut->dump() << std::endl;
}
if (json)
logger->cout("%s", *jsonOut);
if (!json && !results)
throw Error("no results for the given search term(s)!");

View file

@ -57,7 +57,7 @@ struct CmdShowDerivation : InstallablesCommand
jsonRoot[store->printStorePath(drvPath)] =
store->readDerivation(drvPath).toJSON(*store);
}
std::cout << jsonRoot.dump(2) << std::endl;
logger->cout(jsonRoot.dump(2));
}
};

View file

@ -173,7 +173,7 @@ struct CmdKeyGenerateSecret : Command
if (!keyName)
throw UsageError("required argument '--key-name' is missing");
std::cout << SecretKey::generate(*keyName).to_string();
writeFull(STDOUT_FILENO, SecretKey::generate(*keyName).to_string());
}
};
@ -194,7 +194,7 @@ struct CmdKeyConvertSecretToPublic : Command
void run() override
{
SecretKey secretKey(drainFD(STDIN_FILENO));
std::cout << secretKey.toPublicKey().to_string();
writeFull(STDOUT_FILENO, secretKey.toPublicKey().to_string());
}
};