Skip to content

Commit

Permalink
nix-profile-install: show helpful error upon package conflict
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bobvanderlinden committed Feb 25, 2023
1 parent db31c1e commit 9af477e
Showing 1 changed file with 60 additions and 1 deletion.
61 changes: 60 additions & 1 deletion src/nix/profile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,66 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
manifest.elements.push_back(std::move(element));
}

updateProfile(manifest.build(store));
try {
updateProfile(manifest.build(store));
} catch (BuildProfileConflictError & conflictError) {
auto findInstallableOfPkgPath = [&](const Path & pkgPath) {
std::string fallbackInstallable = "INSTALLABLE";
auto maybeStorePath = store->maybeParseStorePath(pkgPath);
if (!maybeStorePath) {
printError("warning: unable to determine installable from package path (%1%)", pkgPath);
return fallbackInstallable;
}
auto storePath = *maybeStorePath;
auto manifestElement = find_if(manifest.elements.begin(), manifest.elements.end(), [&](const ProfileElement & currentProfileElement) {
return currentProfileElement.storePaths.contains(storePath);
});
if (manifestElement == manifest.elements.end()) {
printError("warning: unable to find profile element for store path (%1%)", storePath.to_string());
return fallbackInstallable;
}
if (!manifestElement->source) {
printError("warning: store path does not have a source (%1%)", storePath.to_string());
return fallbackInstallable;
}
return manifestElement->source->originalRef.to_string();
};
std::string originalInstallable = findInstallableOfPkgPath(conflictError.originalPkgPath);
std::string conflictingInstallable = findInstallableOfPkgPath(conflictError.conflictingPkgPath);

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"
" %3%\n"
"\n"
"To remove the existing package:\n"
"\n"
" nix profile remove %5%\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 %7%.\n"
"To prioritise the new package:\n"
"\n"
" nix profile install %6% --priority %8%\n"
"\n"
"To prioritise the existing package:\n"
"\n"
" nix profile install %6% --priority %9%\n",
conflictError.originalFile,
conflictError.originalPkgPath,
conflictError.conflictingFile,
conflictError.conflictingPkgPath,
originalInstallable,
conflictingInstallable,
conflictError.priority,
conflictError.priority - 1,
conflictError.priority + 1
);
}
}
};

Expand Down

0 comments on commit 9af477e

Please sign in to comment.