forked from lix-project/lix
nix-profile-install: show helpful error upon package conflict
Whenever a file conflict happens during "nix profile install" an error is shown that was previously thrown inside builtins.buildEnv. We catch BuildProfileConflictError here so that we can provide the user with more useful instructions on what to do next. Most notably, we give the user concrete commands to use with all parameters already filled in. This avoids the need for the user to look up these commands in manual pages.
This commit is contained in:
parent
3113b13df9
commit
872cdb4346
|
@ -329,7 +329,61 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
||||||
manifest.elements.push_back(std::move(element));
|
manifest.elements.push_back(std::move(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
updateProfile(manifest.build(store));
|
updateProfile(manifest.build(store));
|
||||||
|
} catch (BuildEnvFileConflictError & conflictError) {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue