diff --git a/src/libcmd/include/nix/cmd/command.hh b/src/libcmd/include/nix/cmd/command.hh index 3d00b2c0091..4019b06773f 100644 --- a/src/libcmd/include/nix/cmd/command.hh +++ b/src/libcmd/include/nix/cmd/command.hh @@ -407,7 +407,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu struct MixOutLinkBase : virtual Args { /** Prefix for any output symlinks. Empty means do not write an output symlink. */ - Path outLink; + std::filesystem::path outLink; MixOutLinkBase(const std::string & defaultOutLink) : outLink(defaultOutLink) @@ -435,7 +435,7 @@ struct MixOutLinkByDefault : MixOutLinkBase, virtual Args addFlag({ .longName = "no-link", .description = "Do not create symlinks to the build results.", - .handler = {&outLink, Path("")}, + .handler = {&outLink, std::filesystem::path{}}, }); } }; diff --git a/src/libcmd/include/nix/cmd/repl.hh b/src/libcmd/include/nix/cmd/repl.hh index 7d78c04c049..0eabe8fa2ba 100644 --- a/src/libcmd/include/nix/cmd/repl.hh +++ b/src/libcmd/include/nix/cmd/repl.hh @@ -2,6 +2,7 @@ ///@file #include "nix/expr/eval.hh" +#include "nix/util/os-string.hh" namespace nix { @@ -27,8 +28,7 @@ struct AbstractNixRepl * @param programName Name of the command, e.g. `nix` or `nix-env`. * @param args arguments to the command. */ - using RunNix = - void(const std::string & programName, const Strings & args, const std::optional & input); + using RunNix = void(const std::string & programName, OsStrings args, const std::optional & input); /** * @param runNix Function to run the nix CLI to support various diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 0324d98f040..4320d5b787a 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -28,6 +28,8 @@ #include "nix/util/ref.hh" #include "nix/expr/value.hh" +#include "nix/util/os-string.hh" +#include "nix/util/processes.hh" #include "nix/util/strings.hh" namespace nix { @@ -71,7 +73,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc RunNix * runNixPtr; - void runNix(const std::string & program, const Strings & args, const std::optional & input = {}); + void runNix(const std::string & program, OsStrings args, const std::optional & input = {}); std::unique_ptr interacter; @@ -508,7 +510,12 @@ ProcessLineResult NixRepl::processLine(std::string line) // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI - runProgram2(RunOptions{.program = editor, .lookupPath = true, .args = args, .isInteractive = true}); + runProgram2({ + .program = editor, + .lookupPath = true, + .args = toOsStrings(std::move(args)), + .isInteractive = true, + }); // Reload right after exiting the editor state->resetFileCache(); @@ -528,7 +535,7 @@ ProcessLineResult NixRepl::processLine(std::string line) state->callFunction(f, v, result, PosIdx()); StorePath drvPath = getDerivationPath(result); - runNix("nix-shell", {state->store->printStorePath(drvPath)}); + runNix("nix-shell", toOsStrings({state->store->printStorePath(drvPath)})); } else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") { @@ -559,7 +566,7 @@ ProcessLineResult NixRepl::processLine(std::string line) } } } else if (command == ":i") { - runNix("nix-env", {"-i", drvPathRaw}); + runNix("nix-env", toOsStrings({"-i", drvPathRaw})); } else if (command == ":log") { settings.readOnlyMode = true; Finally roModeReset([&]() { settings.readOnlyMode = false; }); @@ -567,7 +574,7 @@ ProcessLineResult NixRepl::processLine(std::string line) auto log = fetchBuildLog(state->store, drvPath, drvPathRaw); logger->writeToStdout(log); } else { - runNix("nix-shell", {drvPathRaw}); + runNix("nix-shell", toOsStrings({drvPathRaw})); } } @@ -872,10 +879,10 @@ void NixRepl::evalString(std::string s, Value & v) state->forceValue(v, v.determinePos(noPos)); } -void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional & input) +void NixRepl::runNix(const std::string & program, OsStrings args, const std::optional & input) { if (runNixPtr) - (*runNixPtr)(program, args, input); + (*runNixPtr)(program, std::move(args), input); else throw Error( "Cannot run '%s' because no method of calling the Nix CLI was provided. This is a configuration problem pertaining to how this program was built. See Nix 2.25 release notes", diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0cdf0707559..84ab4f62db5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -11,6 +11,7 @@ #include "nix/store/path-references.hh" #include "nix/store/store-api.hh" #include "nix/util/util.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/expr/value-to-json.hh" #include "nix/expr/value-to-xml.hh" @@ -508,7 +509,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v) .debugThrow(); } - auto output = runProgram(program, true, commandArgs); + auto output = runProgram(program, true, toOsStrings(std::move(commandArgs))); Expr * parsed; try { parsed = state.parseExprFromString(std::move(output), state.rootPath(CanonPath::root)); diff --git a/src/libfetchers/git-lfs-fetch.cc b/src/libfetchers/git-lfs-fetch.cc index 5e2e4d91e1c..ac42493da03 100644 --- a/src/libfetchers/git-lfs-fetch.cc +++ b/src/libfetchers/git-lfs-fetch.cc @@ -1,9 +1,11 @@ #include "nix/fetchers/git-lfs-fetch.hh" #include "nix/fetchers/git-utils.hh" #include "nix/store/filetransfer.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/util/url.hh" #include "nix/util/users.hh" +#include "nix/util/util.hh" #include "nix/util/hash.hh" #include "nix/store/ssh.hh" @@ -73,10 +75,12 @@ static LfsApiInfo getLfsApi(const ParsedURL & url) args.push_back(url.renderPath(/*encode=*/false)); args.push_back("download"); - auto [status, output] = runProgram({.program = "ssh", .args = args}); + auto [status, output] = runProgram({.program = "ssh", .args = toOsStrings(args)}); if (output.empty()) - throw Error("git-lfs-authenticate: no output (cmd: 'ssh %s')", concatStringsSep(" ", args)); + throw Error( + "git-lfs-authenticate: no output (cmd: 'ssh %s')", + concatMapStringsSep(" ", args, escapeShellArgAlways)); auto queryResp = nlohmann::json::parse(output); auto headerIt = queryResp.find("header"); diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index d440c3c7076..02dfc9f85c0 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -4,6 +4,7 @@ #include "nix/fetchers/fetch-settings.hh" #include "nix/util/base-n.hh" #include "nix/util/finally.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/util/signals.hh" #include "nix/util/users.hh" @@ -637,12 +638,24 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this // then use code that was removed in this commit (see blame) auto dir = this->path; - Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--progress", "--force"}; - if (shallow) - append(gitArgs, {"--depth", "1"}); - append(gitArgs, {std::string("--"), url, refspec}); + OsStrings gitArgs = { + OS_STR("-C"), + dir.native(), + OS_STR("--git-dir"), + OS_STR("."), + OS_STR("fetch"), + OS_STR("--progress"), + OS_STR("--force"), + }; + if (shallow) { + gitArgs.push_back(OS_STR("--depth")); + gitArgs.push_back(OS_STR("1")); + } + gitArgs.push_back(OS_STR("--")); + gitArgs.push_back(string_to_os_string(url)); + gitArgs.push_back(string_to_os_string(refspec)); - auto status = runProgram(RunOptions{.program = "git", .args = gitArgs, .isInteractive = true}).first; + auto status = runProgram({.program = "git", .args = gitArgs, .isInteractive = true}).first; if (status > 0) throw Error("Failed to fetch git repository '%s'", url); @@ -682,18 +695,18 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this writeFile(allowedSignersFile, allowedSigners); // Run verification command - auto [status, output] = runProgram( - RunOptions{ - .program = "git", - .args = - {"-c", - "gpg.ssh.allowedSignersFile=" + allowedSignersFile, - "-C", - path.string(), - "verify-commit", - rev.gitRev()}, - .mergeStderrToStdout = true, - }); + auto [status, output] = runProgram({ + .program = "git", + .args{ + OS_STR("-c"), + string_to_os_string("gpg.ssh.allowedSignersFile=" + allowedSignersFile), + OS_STR("-C"), + path.native(), + OS_STR("verify-commit"), + string_to_os_string(rev.gitRev()), + }, + .mergeStderrToStdout = true, + }); /* Evaluate result through status code and checking if public key fingerprints appear on stderr. This is necessary diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index a9b24557d6b..2a70ce53aff 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -7,6 +7,7 @@ #include "nix/store/store-api.hh" #include "nix/util/url-parts.hh" #include "nix/store/pathlocks.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/util/git.hh" #include "nix/fetchers/git-utils.hh" @@ -62,7 +63,7 @@ std::optional readHead(const std::filesystem::path & path) RunOptions{ .program = "git", // FIXME: use 'HEAD' to avoid returning all refs - .args = {"ls-remote", "--symref", path.string()}, + .args = {OS_STR("ls-remote"), OS_STR("--symref"), path.native()}, .isInteractive = true, }); if (status != 0) @@ -89,7 +90,19 @@ bool storeCachedHead(const std::string & actualUrl, bool shallow, const std::str { std::filesystem::path cacheDir = getCachePath(actualUrl, shallow); try { - runProgram("git", true, {"-C", cacheDir.string(), "--git-dir", ".", "symbolic-ref", "--", "HEAD", headRef}); + runProgram( + "git", + true, + { + OS_STR("-C"), + cacheDir.native(), + OS_STR("--git-dir"), + OS_STR("."), + OS_STR("symbolic-ref"), + OS_STR("--"), + OS_STR("HEAD"), + string_to_os_string(headRef), + }); } catch (ExecError & e) { if ( #ifndef WIN32 // TODO abstract over exit status handling on Windows @@ -439,19 +452,19 @@ struct GitInputScheme : InputScheme { auto repoInfo = getRepoInfo(input); - Strings args = {"clone"}; + OsStrings args = {OS_STR("clone")}; - args.push_back(repoInfo.locationToArg()); + args.push_back(string_to_os_string(repoInfo.locationToArg())); if (auto ref = input.getRef()) { - args.push_back("--branch"); - args.push_back(*ref); + args.push_back(OS_STR("--branch")); + args.push_back(string_to_os_string(*ref)); } if (input.getRev()) throw UnimplementedError("cloning a specific revision is not implemented"); - args.push_back(destDir.string()); + args.push_back(destDir.native()); runProgram("git", true, args, {}, true); } @@ -478,14 +491,15 @@ struct GitInputScheme : InputScheme auto result = runProgram( RunOptions{ .program = "git", - .args = - {"-C", - repoPath->string(), - "--git-dir", - repoInfo.gitDir, - "check-ignore", - "--quiet", - std::string(path.rel())}, + .args{ + OS_STR("-C"), + repoPath->native(), + OS_STR("--git-dir"), + string_to_os_string(repoInfo.gitDir), + OS_STR("check-ignore"), + OS_STR("--quiet"), + string_to_os_string(std::string(path.rel())), + }, }); auto exitCode = #ifndef WIN32 // TODO abstract over exit status handling on Windows @@ -500,14 +514,16 @@ struct GitInputScheme : InputScheme runProgram( "git", true, - {"-C", - repoPath->string(), - "--git-dir", - repoInfo.gitDir, - "add", - "--intent-to-add", - "--", - std::string(path.rel())}); + { + OS_STR("-C"), + repoPath->native(), + OS_STR("--git-dir"), + string_to_os_string(repoInfo.gitDir), + OS_STR("add"), + OS_STR("--intent-to-add"), + OS_STR("--"), + string_to_os_string(std::string(path.rel())), + }); if (commitMsg) { // Pause the logger to allow for user input (such as a gpg passphrase) in `git commit` @@ -515,14 +531,16 @@ struct GitInputScheme : InputScheme runProgram( "git", true, - {"-C", - repoPath->string(), - "--git-dir", - repoInfo.gitDir, - "commit", - std::string(path.rel()), - "-F", - "-"}, + { + OS_STR("-C"), + repoPath->native(), + OS_STR("--git-dir"), + string_to_os_string(repoInfo.gitDir), + OS_STR("commit"), + string_to_os_string(std::string(path.rel())), + OS_STR("-F"), + OS_STR("-"), + }, *commitMsg); } } @@ -627,10 +645,12 @@ struct GitInputScheme : InputScheme // // See: https://discourse.nixos.org/t/57783 and #9708 // - if (url.scheme == "file" && !forceHttp && !isBareRepository(renderUrlPathEnsureLegal(url.path))) { - auto path = renderUrlPathEnsureLegal(url.path); + auto maybeUrlFsPathForFileUrl = + url.scheme == "file" ? std::make_optional(urlPathToPath(url.path)) : std::nullopt; + if (maybeUrlFsPathForFileUrl && !forceHttp && !isBareRepository(maybeUrlFsPathForFileUrl->string())) { + auto & path = *maybeUrlFsPathForFileUrl; - if (!isAbsolute(path)) { + if (!path.is_absolute()) { warn( "Fetching Git repository '%s', which uses a path relative to the current directory. " "This is not supported and will stop working in a future release. " @@ -640,7 +660,7 @@ struct GitInputScheme : InputScheme repoInfo.location = std::filesystem::absolute(path); } else { - if (url.scheme == "file") + if (maybeUrlFsPathForFileUrl) /* Query parameters are meaningless for file://, but Git interprets them as part of the file name. So get rid of them. */ diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 005409176cd..187d25f340d 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -1,5 +1,9 @@ #include "nix/fetchers/fetchers.hh" +#include "nix/util/file-system.hh" +#include "nix/util/fmt.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" +#include "nix/util/util.hh" #include "nix/util/environment-variables.hh" #include "nix/util/users.hh" #include "nix/fetchers/cache.hh" @@ -10,24 +14,25 @@ #include "nix/fetchers/fetch-settings.hh" #include +#include using namespace std::string_literals; namespace nix::fetchers { -static RunOptions hgOptions(const Strings & args) +static RunOptions hgOptions(OsStrings args) { auto env = getEnvOs(); // Set HGPLAIN: this means we get consistent output from hg and avoids leakage from a user or system .hgrc. env[OS_STR("HGPLAIN")] = OS_STR(""); - return {.program = "hg", .lookupPath = true, .args = args, .environment = env}; + return {.program = "hg", .lookupPath = true, .args = std::move(args), .environment = env}; } // runProgram wrapper that uses hgOptions instead of stock RunOptions. -static std::string runHg(const Strings & args, const std::optional & input = {}) +static std::string runHg(OsStrings args, const std::optional & input = {}) { - RunOptions opts = hgOptions(args); + RunOptions opts = hgOptions(std::move(args)); opts.input = input; auto res = runProgram(std::move(opts)); @@ -145,7 +150,7 @@ struct MercurialInputScheme : InputScheme { auto url = parseURL(getStrAttr(input.attrs, "url")); if (url.scheme == "file" && !input.getRef() && !input.getRev()) - return renderUrlPathEnsureLegal(url.path); + return urlPathToPath(url.path); return {}; } @@ -155,29 +160,36 @@ struct MercurialInputScheme : InputScheme std::string_view contents, std::optional commitMsg) const override { - auto [isLocal, repoPath] = getActualUrl(input); - if (!isLocal) - throw Error( - "cannot commit '%s' to Mercurial repository '%s' because it's not a working tree", - path, - input.to_string()); - - auto absPath = CanonPath(repoPath) / path; - - writeFile(absPath.abs(), contents); - - // FIXME: shut up if file is already tracked. - runHg({"add", absPath.abs()}); - - if (commitMsg) - runHg({"commit", absPath.abs(), "-m", *commitMsg}); + std::visit( + overloaded{ + [&](const std::filesystem::path & repoPath) { + auto absPath = repoPath / path.rel(); + + writeFile(absPath, contents); + + // FIXME: shut up if file is already tracked. + runHg({OS_STR("add"), absPath.native()}); + + if (commitMsg) + runHg({OS_STR("commit"), absPath.native(), OS_STR("-m"), string_to_os_string(*commitMsg)}); + }, + [&](const std::string &) { + throw Error( + "cannot commit '%s' to Mercurial repository '%s' because it's not a working tree", + path, + input.to_string()); + }, + }, + getActualUrl(input)); } - std::pair getActualUrl(const Input & input) const + std::variant getActualUrl(const Input & input) const { auto url = parseURL(getStrAttr(input.attrs, "url")); - bool isLocal = url.scheme == "file"; - return {isLocal, isLocal ? renderUrlPathEnsureLegal(url.path) : url.to_string()}; + if (url.scheme == "file") + return urlPathToPath(url.path); + else + return url.to_string(); } StorePath fetchToStore(const Settings & settings, Store & store, Input & input) const @@ -186,35 +198,52 @@ struct MercurialInputScheme : InputScheme auto name = input.getName(); - auto [isLocal, actualUrl_] = getActualUrl(input); - auto actualUrl = actualUrl_; // work around clang bug + auto actualUrl_ = getActualUrl(input); // FIXME: return lastModified. // FIXME: don't clone local repositories. - if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) { - - bool clean = runHg({"status", "-R", actualUrl, "--modified", "--added", "--removed"}) == ""; - - if (!clean) { - + if (auto * localPathP = std::get_if(&actualUrl_)) { + auto & localPath = *localPathP; + auto unlocked = !input.getRef() && !input.getRev(); + auto isValidLocalRepo = pathExists(localPath / ".hg"); + // short circuiting to not bother checking if locked / no repo is important. + bool dirty = unlocked && isValidLocalRepo + && runHg({ + OS_STR("status"), + OS_STR("-R"), + localPath.native(), + OS_STR("--modified"), + OS_STR("--added"), + OS_STR("--removed"), + }) != ""; + if (dirty) { /* This is an unclean working tree. So copy all tracked files. */ if (!settings.allowDirty) - throw Error("Mercurial tree '%s' is unclean", actualUrl); + throw Error("Mercurial tree '%s' is unclean", PathFmt{localPath}); if (settings.warnDirty) - warn("Mercurial tree '%s' is unclean", actualUrl); + warn("Mercurial tree '%s' is unclean", PathFmt{localPath}); - input.attrs.insert_or_assign("ref", chomp(runHg({"branch", "-R", actualUrl}))); + input.attrs.insert_or_assign("ref", chomp(runHg({OS_STR("branch"), OS_STR("-R"), localPath.native()}))); auto files = tokenizeString( - runHg({"status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0"}), + runHg({ + OS_STR("status"), + OS_STR("-R"), + localPath.native(), + OS_STR("--clean"), + OS_STR("--modified"), + OS_STR("--added"), + OS_STR("--no-status"), + OS_STR("--print0"), + }), "\0"s); - std::filesystem::path actualPath(absPath(actualUrl)); + auto actualPath = absPath(localPath); PathFilter filter = [&](const Path & p) -> bool { assert(hasPrefix(p, actualPath.string())); @@ -231,18 +260,23 @@ struct MercurialInputScheme : InputScheme return files.count(file); }; - auto storePath = store.addToStore( + return store.addToStore( input.getName(), {getFSSourceAccessor(), CanonPath(actualPath.string())}, ContentAddressMethod::Raw::NixArchive, HashAlgorithm::SHA256, {}, filter); - - return storePath; } } + auto [actualUrl, actualUrlOs] = std::visit( + overloaded{ + [&](const std::filesystem::path & p) { return std::make_pair(p.string(), p.native()); }, + [&](const std::string & s) { return std::make_pair(s, string_to_os_string(s)); }, + }, + actualUrl_); + if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); @@ -282,40 +316,48 @@ struct MercurialInputScheme : InputScheme /* If this is a commit hash that we already have, we don't have to pull again. */ if (!(input.getRev() && pathExists(cacheDir) - && runProgram( - hgOptions({"log", "-R", cacheDir.string(), "-r", input.getRev()->gitRev(), "--template", "1"})) + && runProgram(hgOptions({ + OS_STR("log"), + OS_STR("-R"), + cacheDir.native(), + OS_STR("-r"), + string_to_os_string(input.getRev()->gitRev()), + OS_STR("--template"), + OS_STR("1"), + })) .second == "1")) { Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl)); if (pathExists(cacheDir)) { try { - runHg({"pull", "-R", cacheDir.string(), "--", actualUrl}); + runHg({OS_STR("pull"), OS_STR("-R"), cacheDir.native(), OS_STR("--"), actualUrlOs}); } catch (ExecError & e) { auto transJournal = cacheDir / ".hg" / "store" / "journal"; /* hg throws "abandoned transaction" error only if this file exists */ if (pathExists(transJournal)) { - runHg({"recover", "-R", cacheDir.string()}); - runHg({"pull", "-R", cacheDir.string(), "--", actualUrl}); + runHg({OS_STR("recover"), OS_STR("-R"), cacheDir.native()}); + runHg({OS_STR("pull"), OS_STR("-R"), cacheDir.native(), OS_STR("--"), actualUrlOs}); } else { throw ExecError(e.status, "'hg pull' %s", statusToString(e.status)); } } } else { - createDirs(dirOf(cacheDir.string())); - runHg({"clone", "--noupdate", "--", actualUrl, cacheDir.string()}); + createDirs(cacheDir.parent_path()); + runHg({OS_STR("clone"), OS_STR("--noupdate"), OS_STR("--"), actualUrlOs, cacheDir.native()}); } } /* Fetch the remote rev or ref. */ - auto tokens = tokenizeString>(runHg( - {"log", - "-R", - cacheDir.string(), - "-r", - input.getRev() ? input.getRev()->gitRev() : *input.getRef(), - "--template", - "{node} {rev} {branch}"})); + auto tokens = tokenizeString>(runHg({ + OS_STR("log"), + OS_STR("-R"), + cacheDir.native(), + OS_STR("-r"), + string_to_os_string(input.getRev() ? input.getRev()->gitRev() : *input.getRef()), + OS_STR("--template"), + OS_STR("{node} {rev} {branch}"), + })); assert(tokens.size() == 3); auto rev = Hash::parseAny(tokens[0], HashAlgorithm::SHA1); @@ -331,7 +373,14 @@ struct MercurialInputScheme : InputScheme std::filesystem::path tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); - runHg({"archive", "-R", cacheDir.string(), "-r", rev.gitRev(), tmpDir.string()}); + runHg({ + OS_STR("archive"), + OS_STR("-R"), + cacheDir.native(), + OS_STR("-r"), + string_to_os_string(rev.gitRev()), + tmpDir.native(), + }); deletePath(tmpDir / ".hg_archival.txt"); diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index c8fb0391aab..d6b4b44bc33 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -19,7 +19,7 @@ struct PathInputScheme : InputScheme Input input{}; input.attrs.insert_or_assign("type", "path"); - input.attrs.insert_or_assign("path", renderUrlPathEnsureLegal(url.path)); + input.attrs.insert_or_assign("path", urlPathToPath(url.path).string()); for (auto & [name, value] : url.query) if (name == "rev" || name == "narHash") diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index d9927914658..e7297bfca00 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -113,7 +113,7 @@ static DownloadTarballResult downloadTarball_( // Namely lets catch when the url is a local file path, but // it is not in fact a tarball. if (url.scheme == "file") { - std::filesystem::path localPath = renderUrlPathEnsureLegal(url.path); + std::filesystem::path localPath = urlPathToPath(url.path); if (!localPath.is_absolute()) { throw Error( "tarball '%s' must use an absolute path. " diff --git a/src/libflake/flakeref.cc b/src/libflake/flakeref.cc index f3bfb952738..f33a6e48644 100644 --- a/src/libflake/flakeref.cc +++ b/src/libflake/flakeref.cc @@ -250,9 +250,9 @@ std::optional> parseURLFlakeRef( auto parsed = parseURL(url, /*lenient=*/true); if (baseDir && (parsed.scheme == "path" || parsed.scheme == "git+file")) { /* Here we know that the path must not contain encoded '/' or NUL bytes. */ - auto path = renderUrlPathEnsureLegal(parsed.path); - if (!isAbsolute(path)) - parsed.path = splitString>(absPath(path, baseDir->string()), "/"); + auto path = urlPathToPath(parsed.path); + if (!path.is_absolute()) + parsed.path = pathToUrlPath(absPath(path, get(baseDir))); } return fromParsedURL(fetchSettings, std::move(parsed), isFlake); } catch (BadURL &) { diff --git a/src/libstore-test-support/https-store.cc b/src/libstore-test-support/https-store.cc index 3cbe0c789aa..aeba8f0e0f4 100644 --- a/src/libstore-test-support/https-store.cc +++ b/src/libstore-test-support/https-store.cc @@ -1,4 +1,5 @@ #include "nix/store/tests/https-store.hh" +#include "nix/util/os-string.hh" #include @@ -21,7 +22,7 @@ ref TestHttpBinaryCacheStoreConfig::openTestStore(ref< void HttpsBinaryCacheStoreTest::openssl(Strings args) { - runProgram("openssl", /*lookupPath=*/true, args); + runProgram("openssl", /*lookupPath=*/true, toOsStrings(std::move(args))); } void HttpsBinaryCacheStoreTest::SetUp() diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc index da1b6bfd589..a765458c984 100644 --- a/src/libstore/build/derivation-building-goal.cc +++ b/src/libstore/build/derivation-building-goal.cc @@ -1061,7 +1061,7 @@ static void runPostBuildHook( LogSink sink(act); runProgram2({ - .program = workerSettings.postBuildHook, + .program = workerSettings.postBuildHook.get(), .environment = hookEnvironment, .standardOut = &sink, .mergeStderrToStdout = true, diff --git a/src/libstore/include/nix/store/local-overlay-store.hh b/src/libstore/include/nix/store/local-overlay-store.hh index 181c8cc90c6..0f07696521b 100644 --- a/src/libstore/include/nix/store/local-overlay-store.hh +++ b/src/libstore/include/nix/store/local-overlay-store.hh @@ -53,7 +53,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig default, but can be disabled if needed. )"}; - PathSetting remountHook{ + Setting remountHook{ (StoreConfig *) this, "", "remount-hook", diff --git a/src/libstore/local-gc.cc b/src/libstore/local-gc.cc index 681ac28c514..e3c74f35752 100644 --- a/src/libstore/local-gc.cc +++ b/src/libstore/local-gc.cc @@ -128,8 +128,8 @@ Roots findRuntimeRootsUnchecked(const StoreDirConfig & config) if (getEnv("_NIX_TEST_NO_LSOF") != "1") { try { boost::regex lsofRegex(R"(^n(/.*)$)"); - auto lsofLines = - tokenizeString>(runProgram(LSOF, true, {"-n", "-w", "-F", "n"}), "\n"); + auto lsofLines = tokenizeString>( + runProgram(LSOF, true, {OS_STR("-n"), OS_STR("-w"), OS_STR("-F"), OS_STR("n")}), "\n"); for (const auto & line : lsofLines) { boost::smatch match; if (boost::regex_match(line, match, lsofRegex)) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 6459ac2e19a..9c0f70237cb 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -2,6 +2,7 @@ #include "nix/store/local-overlay-store.hh" #include "nix/util/callback.hh" +#include "nix/util/os-string.hh" #include "nix/store/realisation.hh" #include "nix/util/processes.hh" #include "nix/util/url.hh" @@ -283,7 +284,7 @@ void LocalOverlayStore::remountIfNecessary() if (config->remountHook.get().empty()) { warn("'%s' needs remounting, set remount-hook to do this automatically", config->realStoreDir.get()); } else { - runProgram(config->remountHook, false, {config->realStoreDir}); + runProgram(config->remountHook, false, {string_to_os_string(config->realStoreDir.get())}); } _remountRequired = false; diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index d284aa8457b..2b567af1317 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -2,6 +2,7 @@ #include "nix/util/finally.hh" #include "nix/util/current-process.hh" #include "nix/util/environment-variables.hh" +#include "nix/util/os-string.hh" #include "nix/util/util.hh" #include "nix/util/exec.hh" #include "nix/util/base-n.hh" @@ -120,7 +121,8 @@ bool SSHMaster::isMasterRunning() Strings args = {"-O", "check", hostnameAndUser}; addCommonSSHOpts(args); - auto res = runProgram(RunOptions{.program = "ssh", .args = args, .mergeStderrToStdout = true}); + auto res = + runProgram(RunOptions{.program = "ssh", .args = toOsStrings(std::move(args)), .mergeStderrToStdout = true}); return res.first == 0; } diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc index 59ef52b5868..9995b015f00 100644 --- a/src/libstore/unix/build/derivation-builder.cc +++ b/src/libstore/unix/build/derivation-builder.cc @@ -925,7 +925,7 @@ PathsInChroot DerivationBuilderImpl::getPathsInSandbox() enum BuildHookState { stBegin, stExtraChrootDirs }; auto state = stBegin; - auto lines = runProgram(localSettings.preBuildHook, false, getPreBuildHookArgs()); + auto lines = runProgram(localSettings.preBuildHook.get(), false, getPreBuildHookArgs()); auto lastPos = std::string::size_type{0}; for (auto nlPos = lines.find('\n'); nlPos != std::string::npos; nlPos = lines.find('\n', lastPos)) { auto line = lines.substr(lastPos, nlPos - lastPos); diff --git a/src/libutil-tests/spawn.cc b/src/libutil-tests/spawn.cc index cf3645260e1..715c0d17fb2 100644 --- a/src/libutil-tests/spawn.cc +++ b/src/libutil-tests/spawn.cc @@ -1,5 +1,6 @@ #include +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" namespace nix { @@ -7,30 +8,31 @@ namespace nix { #ifdef _WIN32 TEST(SpawnTest, spawnEcho) { - auto output = runProgram(RunOptions{.program = "cmd.exe", .args = {"/C", "echo", "hello world"}}); + auto output = + runProgram(RunOptions{.program = "cmd.exe", .args = {OS_STR("/C"), OS_STR("echo"), OS_STR("hello world")}}); ASSERT_EQ(output.first, 0); ASSERT_EQ(output.second, "\"hello world\"\r\n"); } -std::string windowsEscape(const std::string & str, bool cmd); +OsString windowsEscape(const OsString & str, bool cmd); TEST(SpawnTest, windowsEscape) { - auto empty = windowsEscape("", false); - ASSERT_EQ(empty, R"("")"); + auto empty = windowsEscape(L"", false); + ASSERT_EQ(empty, LR"("")"); // There's no quotes in this argument so the input should equal the output - auto backslashStr = R"(\\\\)"; + auto backslashStr = LR"(\\\\)"; auto backslashes = windowsEscape(backslashStr, false); ASSERT_EQ(backslashes, backslashStr); - auto nestedQuotes = windowsEscape(R"(he said: "hello there")", false); - ASSERT_EQ(nestedQuotes, R"("he said: \"hello there\"")"); + auto nestedQuotes = windowsEscape(LR"(he said: "hello there")", false); + ASSERT_EQ(nestedQuotes, LR"("he said: \"hello there\"")"); - auto middleQuote = windowsEscape(R"( \\\" )", false); - ASSERT_EQ(middleQuote, R"(" \\\\\\\" ")"); + auto middleQuote = windowsEscape(LR"( \\\" )", false); + ASSERT_EQ(middleQuote, LR"(" \\\\\\\" ")"); - auto space = windowsEscape("hello world", false); - ASSERT_EQ(space, R"("hello world")"); + auto space = windowsEscape(L"hello world", false); + ASSERT_EQ(space, LR"("hello world")"); } #endif } // namespace nix diff --git a/src/libutil/include/nix/util/os-string.hh b/src/libutil/include/nix/util/os-string.hh index cc96a1fb8d8..7e3bf47d38e 100644 --- a/src/libutil/include/nix/util/os-string.hh +++ b/src/libutil/include/nix/util/os-string.hh @@ -1,6 +1,7 @@ #pragma once ///@file +#include #include #include #include @@ -42,9 +43,57 @@ using OsStringView = std::basic_string_view; */ using OsStringMap = std::map>; -std::string os_string_to_string(OsStringView path); +/** + * `nix::Strings` counterpart for `OsString` + */ +using OsStrings = std::list; + +std::string os_string_to_string(OsStringView s); +std::string os_string_to_string(OsString s); OsString string_to_os_string(std::string_view s); +OsString string_to_os_string(std::string s); + +#ifndef _WIN32 + +inline std::string os_string_to_string(OsStringView s) +{ + return std::string(s); +} + +inline std::string os_string_to_string(OsString s) +{ + return s; +} + +inline OsString string_to_os_string(std::string_view s) +{ + return std::string(s); +} + +inline OsString string_to_os_string(std::string s) +{ + return s; +} + +#endif + +/** + * Convert a list of `std::string` to `OsStrings`. + * Takes ownership to enable moves on Unix. + */ +inline OsStrings toOsStrings(std::list ss) +{ +#ifndef _WIN32 + // On Unix, OsStrings is std::list, so just move + return ss; +#else + OsStrings result; + for (auto & s : ss) + result.push_back(string_to_os_string(std::move(s))); + return result; +#endif +} /** * Create string literals with the native character width of paths diff --git a/src/libutil/include/nix/util/processes.hh b/src/libutil/include/nix/util/processes.hh index 3b9cff22ceb..15fc9fea0dc 100644 --- a/src/libutil/include/nix/util/processes.hh +++ b/src/libutil/include/nix/util/processes.hh @@ -7,6 +7,9 @@ #include "nix/util/file-path.hh" #include "nix/util/logging.hh" #include "nix/util/ansicolor.hh" +#include "nix/util/os-string.hh" + +#include #include #include @@ -105,17 +108,17 @@ pid_t startProcess(std::function fun, const ProcessOptions & options = P * shell backtick operator). */ std::string runProgram( - Path program, + std::filesystem::path program, bool lookupPath = false, - const Strings & args = Strings(), + const OsStrings & args = OsStrings(), const std::optional & input = {}, bool isInteractive = false); struct RunOptions { - Path program; + std::filesystem::path program; bool lookupPath = true; - Strings args; + OsStrings args; #ifndef _WIN32 std::optional uid; std::optional gid; diff --git a/src/libutil/include/nix/util/url.hh b/src/libutil/include/nix/util/url.hh index 31144c0ab53..32c7ee7ecb8 100644 --- a/src/libutil/include/nix/util/url.hh +++ b/src/libutil/include/nix/util/url.hh @@ -258,16 +258,6 @@ MakeError(BadURL, Error); std::string percentDecode(std::string_view in); std::string percentEncode(std::string_view s, std::string_view keep = ""); -/** - * Get the path part of the URL as an absolute or relative Path. - * - * @throws if any path component contains an slash (which would have - * been escaped `%2F` in the rendered URL). This is because OS file - * paths have no escape sequences --- file names cannot contain a - * `/`. - */ -Path renderUrlPathEnsureLegal(std::span urlPath); - /** * Render URL path segments to a string by joining with `/`. * Does not percent-encode the segments. diff --git a/src/libutil/unix/meson.build b/src/libutil/unix/meson.build index 7859707fe88..dbe142d3a88 100644 --- a/src/libutil/unix/meson.build +++ b/src/libutil/unix/meson.build @@ -62,7 +62,6 @@ sources += files( 'file-system-at.cc', 'file-system.cc', 'muxable-pipe.cc', - 'os-string.cc', 'processes.cc', 'signals.cc', 'users.cc', diff --git a/src/libutil/unix/os-string.cc b/src/libutil/unix/os-string.cc deleted file mode 100644 index 08d275bc671..00000000000 --- a/src/libutil/unix/os-string.cc +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include - -#include "nix/util/file-path.hh" -#include "nix/util/util.hh" - -namespace nix { - -std::string os_string_to_string(PathViewNG::string_view path) -{ - return std::string{path}; -} - -std::filesystem::path::string_type string_to_os_string(std::string_view s) -{ - return std::string{s}; -} - -} // namespace nix diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index 2393da55d4a..d9c4f24fae1 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -1,12 +1,14 @@ #include "nix/util/current-process.hh" #include "nix/util/environment-variables.hh" #include "nix/util/executable-path.hh" +#include "nix/util/fmt.hh" #include "nix/util/signals.hh" #include "nix/util/processes.hh" #include "nix/util/finally.hh" #include "nix/util/serialise.hh" #include +#include #include #include #include @@ -267,7 +269,11 @@ pid_t startProcess(std::function fun, const ProcessOptions & options) } std::string runProgram( - Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) + std::filesystem::path program, + bool lookupPath, + const OsStrings & args, + const std::optional & input, + bool isInteractive) { auto res = runProgram( RunOptions{ @@ -278,7 +284,7 @@ std::string runProgram( .isInteractive = isInteractive}); if (!statusOk(res.first)) - throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); + throw ExecError(res.first, "program %s %s", PathFmt(program), statusToString(res.first)); return res.second; } @@ -353,7 +359,7 @@ void runProgram2(const RunOptions & options) throw SysError("setuid failed"); Strings args_(options.args); - args_.push_front(options.program); + args_.push_front(options.program.native()); restoreProcessContext(); @@ -364,7 +370,7 @@ void runProgram2(const RunOptions & options) else execv(options.program.c_str(), stringsToCharPtrs(args_).data()); - throw SysError("executing '%1%'", options.program); + throw SysError("executing %s", PathFmt(options.program)); }, processOptions); @@ -412,7 +418,7 @@ void runProgram2(const RunOptions & options) promise.get_future().get(); if (status) - throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status)); + throw ExecError(status, "program %1% %2%", PathFmt(options.program), statusToString(status)); } ////////////////////////////////////////////////////////////////////// diff --git a/src/libutil/url.cc b/src/libutil/url.cc index afa427a9a52..184eba263f0 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -321,22 +321,6 @@ std::string encodeQuery(const StringMap & ss) return res; } -Path renderUrlPathEnsureLegal(std::span urlPath) -{ - for (const auto & comp : urlPath) { - /* This is only really valid for UNIX. Windows has more restrictions. */ - if (comp.contains('/')) - throw BadURL("URL path component '%s' contains '/', which is not allowed in file names", comp); - if (comp.contains(char(0))) { - using namespace std::string_view_literals; - auto str = replaceStrings(comp, "\0"sv, "␀"sv); - throw BadURL("URL path component '%s' contains NUL byte which is not allowed", str); - } - } - - return renderUrlPathNoPctEncoding(urlPath); -} - std::string renderUrlPathNoPctEncoding(std::span urlPath) { return concatStringsSep("/", urlPath); @@ -482,28 +466,41 @@ std::vector pathToUrlPath(const std::filesystem::path & path) std::filesystem::path urlPathToPath(std::span urlPath) { + for (const auto & comp : urlPath) { + /* This is only really valid for UNIX. Windows has more restrictions. */ + if (comp.contains('/')) + throw BadURL("URL path component '%s' contains '/', which is not allowed in file names", comp); + if (comp.contains(char(0))) { + using namespace std::string_view_literals; + auto str = replaceStrings(comp, "\0"sv, "␀"sv); + throw BadURL("URL path component '%s' contains NUL byte which is not allowed", str); + } + } + std::filesystem::path result; auto it = urlPath.begin(); - // URL path must start with empty segment (representing absolute path "/") - if (it == urlPath.end() || !it->empty()) - throw Error("only absolute URL paths can be converted to filesystem paths"); + if (it == urlPath.end()) + return result; - ++it; - result = "/"; + // Empty first segment means absolute path (leading "/") + if (it->empty()) { + ++it; + result = "/"; #ifdef _WIN32 - // On Windows, check if next segment is a drive letter (e.g., "C:"). - // If it isn't then this is something like a UNC path rather than a - // DOS path. - if (it != urlPath.end()) { - std::filesystem::path segment{*it}; - if (segment.has_root_name()) { - segment /= "/"; - result = std::move(segment); - ++it; + // On Windows, check if next segment is a drive letter (e.g., "C:"). + // If it isn't then this is something like a UNC path rather than a + // DOS path. + if (it != urlPath.end()) { + std::filesystem::path segment{*it}; + if (segment.has_root_name()) { + segment /= "/"; + result = std::move(segment); + ++it; + } } - } #endif + } // Append remaining segments for (; it != urlPath.end(); ++it) diff --git a/src/libutil/windows/os-string.cc b/src/libutil/windows/os-string.cc index d6f8e36705c..2fface41955 100644 --- a/src/libutil/windows/os-string.cc +++ b/src/libutil/windows/os-string.cc @@ -3,24 +3,32 @@ #include #include -#include "nix/util/file-path.hh" -#include "nix/util/file-path-impl.hh" -#include "nix/util/util.hh" +#include "nix/util/os-string.hh" #ifdef _WIN32 namespace nix { -std::string os_string_to_string(PathViewNG::string_view path) +std::string os_string_to_string(OsStringView s) { std::wstring_convert> converter; - return converter.to_bytes(std::filesystem::path::string_type{path}); + return converter.to_bytes(s.data(), s.data() + s.size()); } -std::filesystem::path::string_type string_to_os_string(std::string_view s) +std::string os_string_to_string(OsString s) +{ + return os_string_to_string(OsStringView{s}); +} + +OsString string_to_os_string(std::string_view s) { std::wstring_convert> converter; - return converter.from_bytes(std::string{s}); + return converter.from_bytes(s.data(), s.data() + s.size()); +} + +OsString string_to_os_string(std::string s) +{ + return string_to_os_string(std::string_view{s}); } } // namespace nix diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 577f94fd050..9c7fa5d35ff 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -4,6 +4,7 @@ #include "nix/util/executable-path.hh" #include "nix/util/file-descriptor.hh" #include "nix/util/file-path.hh" +#include "nix/util/fmt.hh" #include "nix/util/os-string.hh" #include "nix/util/signals.hh" #include "nix/util/processes.hh" @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -88,7 +90,11 @@ int Pid::wait(bool allowInterrupts) // TODO: Merge this with Unix's runProgram since it's identical logic. std::string runProgram( - Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) + std::filesystem::path program, + bool lookupPath, + const OsStrings & args, + const std::optional & input, + bool isInteractive) { auto res = runProgram( RunOptions{ @@ -99,17 +105,17 @@ std::string runProgram( .isInteractive = isInteractive}); if (!statusOk(res.first)) - throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); + throw ExecError(res.first, "program %s %s", PathFmt(program), statusToString(res.first)); return res.second; } -std::optional getProgramInterpreter(const Path & program) +std::optional getProgramInterpreter(const std::filesystem::path & program) { // These extensions are automatically handled by Windows and don't require an interpreter. static constexpr const char * exts[] = {".exe", ".cmd", ".bat"}; for (const auto ext : exts) { - if (hasSuffix(program, ext)) { + if (hasSuffix(program.string(), ext)) { return {}; } } @@ -151,23 +157,23 @@ AutoCloseFD nullFD() // Adapted from // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ -std::string windowsEscape(const std::string & str, bool cmd) +OsString windowsEscape(const OsString & str, bool cmd) { // TODO: This doesn't handle cmd.exe escaping. if (cmd) { throw UnimplementedError("cmd.exe escaping is not implemented"); } - if (str.find_first_of(" \t\n\v\"") == str.npos && !str.empty()) { + if (str.find_first_of(L" \t\n\v\"") == str.npos && !str.empty()) { // No need to escape this one, the nonempty contents don't have a special character return str; } - std::string buffer; + OsString buffer; // Add the opening quote - buffer += '"'; + buffer += L'"'; for (auto iter = str.begin();; ++iter) { size_t backslashes = 0; - while (iter != str.end() && *iter == '\\') { + while (iter != str.end() && *iter == L'\\') { ++iter; ++backslashes; } @@ -178,24 +184,24 @@ std::string windowsEscape(const std::string & str, bool cmd) // Both of these cases break the escaping if not handled. Otherwise backslashes are fine as-is if (iter == str.end()) { // Need to escape each backslash - buffer.append(backslashes * 2, '\\'); + buffer.append(backslashes * 2, L'\\'); // Exit since we've reached the end of the string break; - } else if (*iter == '"') { + } else if (*iter == L'"') { // Need to escape each backslash and the intermediate quote character - buffer.append(backslashes * 2, '\\'); - buffer += "\\\""; + buffer.append(backslashes * 2, L'\\'); + buffer += L"\\\""; } else { // Don't escape the backslashes since they won't break the delimiter - buffer.append(backslashes, '\\'); + buffer.append(backslashes, L'\\'); buffer += *iter; } } // Add the closing quote - return buffer + '"'; + return buffer + L'"'; } -Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & out, Pipe & in) +Pid spawnProcess(const std::filesystem::path & realProgram, const RunOptions & options, Pipe & out, Pipe & in) { // Setup pipes. if (options.standardOut) { @@ -232,18 +238,19 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou envline += (envVar.first + L'=' + envVar.second + L'\0'); } - std::string cmdline = windowsEscape(realProgram, false); + OsString cmdline = windowsEscape(realProgram.native(), false); for (const auto & arg : options.args) { // TODO: This isn't the right way to escape windows command // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw - cmdline += ' ' + windowsEscape(arg, false); + cmdline += L' '; + cmdline += windowsEscape(arg, false); } PROCESS_INFORMATION procInfo = {0}; if (CreateProcessW( // EXE path is provided in the cmdline NULL, - string_to_os_string(cmdline).data(), + cmdline.data(), NULL, NULL, TRUE, @@ -253,7 +260,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou &startInfo, &procInfo) == 0) { - throw WinError("CreateProcessW failed (%1%)", cmdline); + throw WinError("CreateProcessW failed (%1%)", os_string_to_string(cmdline)); } // Convert these to use RAII @@ -321,7 +328,7 @@ void runProgram2(const RunOptions & options) if (source) in.create(); - Path realProgram = options.program; + std::filesystem::path realProgram = options.program; // TODO: Implement shebang / program interpreter lookup on Windows auto interpreter = getProgramInterpreter(realProgram); @@ -374,7 +381,7 @@ void runProgram2(const RunOptions & options) promise.get_future().get(); if (status) - throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status)); + throw ExecError(status, "program %1% %2%", PathFmt(options.program), statusToString(status)); } std::string statusToString(int status) diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index e87f4954607..134d31fac35 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -10,7 +10,7 @@ using namespace nix; struct CmdAddToStore : MixDryRun, StoreCommand { - Path path; + std::filesystem::path path; std::optional namePart; ContentAddressMethod caMethod = ContentAddressMethod::Raw::NixArchive; HashAlgorithm hashAlgo = HashAlgorithm::SHA256; @@ -36,7 +36,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand void run(ref store) override { if (!namePart) - namePart = baseNameOf(path); + namePart = path.filename().string(); auto sourcePath = PosixSourceAccessor::createAtRoot(makeParentCanonical(path)); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index cbd9581dae4..5bca8e0cb31 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -12,7 +12,7 @@ using namespace nix; struct CmdBundle : InstallableValueCommand { std::string bundler = "github:NixOS/bundlers"; - std::optional outLink; + std::optional outLink; CmdBundle() { @@ -126,7 +126,7 @@ struct CmdBundle : InstallableValueCommand } // TODO: will crash if not a localFSStore? - store.dynamic_pointer_cast()->addPermRoot(outPath, absPath(*outLink)); + store.dynamic_pointer_cast()->addPermRoot(outPath, absPath(*outLink).string()); } }; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 826b17800d9..c0fb8325448 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -331,7 +331,7 @@ struct Common : InstallableCommand, MixProfile "UID", }; - std::vector> redirects; + std::vector> redirects; Common() { @@ -339,7 +339,7 @@ struct Common : InstallableCommand, MixProfile .longName = "redirect", .description = "Redirect a store path to a mutable location.", .labels = {"installable", "outputs-dir"}, - .handler = {[&](std::string installable, std::string outputsDir) { + .handler = {[&](std::string installable, std::filesystem::path outputsDir) { redirects.push_back({installable, outputsDir}); }}, }); @@ -412,8 +412,8 @@ struct Common : InstallableCommand, MixProfile if (script.find(from) == std::string::npos) warn("'%s' (path '%s') is not used by this build environment", installable->what(), from); else { - printInfo("redirecting '%s' to '%s'", from, dir); - rewrites.insert({from, dir}); + printInfo("redirecting '%s' to '%s'", from, PathFmt(dir)); + rewrites.insert({from, dir.string()}); } } } @@ -633,7 +633,7 @@ struct CmdDevelop : Common, MixEnvironment // prevent garbage collection until shell exits setEnv("NIX_GCROOT", store->printStorePath(gcroot).c_str()); - Path shell = "bash"; + std::filesystem::path shell = "bash"; bool foundInteractive = false; try { @@ -676,9 +676,9 @@ struct CmdDevelop : Common, MixEnvironment // Override SHELL with the one chosen for this environment. // This is to make sure the system shell doesn't leak into the build environment. - setEnv("SHELL", shell.c_str()); + setEnv("SHELL", shell.string().c_str()); // https://github.com/NixOS/nix/issues/5873 - script += fmt("SHELL=\"%s\"\n", shell); + script += fmt("SHELL=%s\n", PathFmt(shell)); if (foundInteractive) script += fmt("PATH=\"%s${PATH:+:$PATH}\"\n", std::filesystem::path(shell).parent_path().string()); writeFull(rcFileFd.get(), script); @@ -688,8 +688,8 @@ struct CmdDevelop : Common, MixEnvironment #else // If running a phase or single command, don't want an interactive shell running after // Ctrl-C, so don't pass --rcfile - auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath} - : Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; + auto args = phase || !command.empty() ? Strings{shell.filename().string(), rcFilePath} + : Strings{shell.filename().string(), "--rcfile", rcFilePath}; // Need to chdir since phases assume in flake directory if (phase) { diff --git a/src/nix/dump-path.cc b/src/nix/dump-path.cc index 62fd8987761..2a6253b026f 100644 --- a/src/nix/dump-path.cc +++ b/src/nix/dump-path.cc @@ -39,7 +39,7 @@ static auto rDumpPath = registerCommand2({"store", "dump-path"}); struct CmdDumpPath2 : Command { - Path path; + std::filesystem::path path; CmdDumpPath2() { @@ -61,7 +61,7 @@ struct CmdDumpPath2 : Command void run() override { auto sink = getNarSink(); - dumpPath(path, sink); + dumpPath(path.string(), sink); sink.flush(); } }; diff --git a/src/nix/eval.cc b/src/nix/eval.cc index ba3e4142a78..f41d98f1a8d 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -85,7 +85,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption state->forceValue(v, pos); if (v.type() == nString) // FIXME: disallow strings with contexts? - writeFile(path.string(), v.string_view()); + writeFile(path, v.string_view()); else if (v.type() == nAttrs) { [[maybe_unused]] bool directoryCreated = std::filesystem::create_directory(path); // Directory should not already exist diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e1ce69b204c..e7e58d68b4d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -5,6 +5,7 @@ #include "nix/expr/eval-inline.hh" #include "nix/expr/eval-settings.hh" #include "nix/expr/get-drvs.hh" +#include "nix/util/os-string.hh" #include "nix/util/signals.hh" #include "nix/store/store-open.hh" #include "nix/store/derivations.hh" @@ -853,7 +854,7 @@ static Strings defaultTemplateAttrPaths = {"templates.default", "defaultTemplate struct CmdFlakeInitCommon : virtual Args, EvalCommand { std::string templateUrl = "templates"; - Path destDir; + std::filesystem::path destDir; const LockFlags lockFlags{.writeLockFile = false}; @@ -958,9 +959,16 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand }(templateDir, flakeDir); if (!changedFiles.empty() && std::filesystem::exists(std::filesystem::path{flakeDir} / ".git")) { - Strings args = {"-C", flakeDir, "add", "--intent-to-add", "--force", "--"}; + OsStrings args = { + OS_STR("-C"), + flakeDir.native(), + OS_STR("add"), + OS_STR("--intent-to-add"), + OS_STR("--force"), + OS_STR("--"), + }; for (auto & s : changedFiles) - args.emplace_back(s.string()); + args.emplace_back(s.native()); runProgram("git", true, args); } diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index 441fed025f2..b7dec706765 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -140,7 +140,7 @@ static void main_nix_build(int argc, char ** argv) auto myName = isNixShell ? "nix-shell" : "nix-build"; auto inShebang = false; - std::string script; + std::filesystem::path script; std::vector savedArgs; AutoDelete tmpDir(createTempDir("", myName)); @@ -200,9 +200,9 @@ static void main_nix_build(int argc, char ** argv) { using LegacyArgs::LegacyArgs; - void setBaseDir(Path baseDir) + void setBaseDir(std::filesystem::path baseDir) { - commandBaseDir = baseDir; + commandBaseDir = baseDir.string(); } }; @@ -284,11 +284,15 @@ static void main_nix_build(int argc, char ** argv) fmt("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%", execArgs, interpreter, - escapeShellArgAlways(script), + escapeShellArgAlways(script.string()), joined.view()); } else { envCommand = - fmt("exec %1% %2% %3% %4%", execArgs, interpreter, escapeShellArgAlways(script), joined.view()); + fmt("exec %1% %2% %3% %4%", + execArgs, + interpreter, + escapeShellArgAlways(script.string()), + joined.view()); } } @@ -321,7 +325,7 @@ static void main_nix_build(int argc, char ** argv) buildMode = bmRepair; if (inShebang && compatibilitySettings.nixShellShebangArgumentsRelativeToScript) { - myArgs.setBaseDir(absPath(dirOf(script))); + myArgs.setBaseDir(absPath(script.parent_path())); } auto autoArgs = myArgs.getAutoArgs(*state); @@ -373,17 +377,17 @@ static void main_nix_build(int argc, char ** argv) exprs = {state->parseStdin()}; else for (auto i : remainingArgs) { + auto shebangBaseDir = absPath(script.parent_path()); if (fromArgs) { - auto shebangBaseDir = absPath(dirOf(script)); exprs.push_back(state->parseExprFromString( std::move(i), (inShebang && compatibilitySettings.nixShellShebangArgumentsRelativeToScript) - ? lookupFileArg(*state, shebangBaseDir) + ? lookupFileArg(*state, shebangBaseDir.string()) : state->rootPath("."))); } else { auto absolute = i; try { - absolute = canonPath(absPath(i), true); + absolute = canonPath(absPath(std::filesystem::path{i}), true).string(); } catch (Error & e) { }; auto [path, outputNames] = parsePathWithOutputs(absolute); @@ -392,9 +396,10 @@ static void main_nix_build(int argc, char ** argv) else { /* If we're in a #! script, interpret filenames relative to the script. */ - auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; + std::filesystem::path iPath{i}; + auto baseDir = inShebang && !packages ? absPath(iPath, &shebangBaseDir) : iPath; - auto sourcePath = lookupFileArg(*state, baseDir); + auto sourcePath = lookupFileArg(*state, baseDir.string()); auto resolvedPath = isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); exprs.push_back(state->parseExprFromFile(resolvedPath)); @@ -570,7 +575,7 @@ static void main_nix_build(int argc, char ** argv) for (auto & var : drv.env) if (drvOptions.passAsFile.count(var.first)) { auto fn = ".attr-" + std::to_string(fileNr++); - Path p = (tmpDir.path() / fn).string(); + auto p = (tmpDir.path() / fn).string(); writeFile(p, var.second); env[var.first + "Path"] = p; } else diff --git a/src/nix/nix-channel/nix-channel.cc b/src/nix/nix-channel/nix-channel.cc index 063be334271..fac4616a0bb 100644 --- a/src/nix/nix-channel/nix-channel.cc +++ b/src/nix/nix-channel/nix-channel.cc @@ -6,6 +6,7 @@ #include "nix/cmd/legacy.hh" #include "nix/cmd/common-eval-args.hh" #include "nix/expr/eval-settings.hh" // for defexpr +#include "nix/util/os-string.hh" #include "nix/util/users.hh" #include "nix/fetchers/tarball.hh" #include "nix/fetchers/fetch-settings.hh" @@ -69,7 +70,7 @@ static void addChannel(const std::string & url, const std::string & name) writeChannels(); } -static Path profile; +static std::filesystem::path profile; // Remove a channel. static void removeChannel(const std::string & name) @@ -78,10 +79,18 @@ static void removeChannel(const std::string & name) channels.erase(name); writeChannels(); - runProgram(getNixBin("nix-env").string(), true, {"--profile", profile, "--uninstall", name}); + runProgram( + getNixBin("nix-env"), + true, + { + OS_STR("--profile"), + profile.native(), + OS_STR("--uninstall"), + string_to_os_string(name), + }); } -static Path nixDefExpr; +static std::filesystem::path nixDefExpr; // Fetch Nix expressions and binary cache URLs from the subscribed channels. static void update(const StringSet & channelNames) @@ -117,12 +126,12 @@ static void update(const StringSet & channelNames) if (!(channelNames.empty() || channelNames.count(name))) { // no need to update this channel, reuse the existing store path - Path symlink = profile + "/" + name; - Path storepath = dirOf(readLink(symlink)); + std::filesystem::path symlink = profile / name; + std::filesystem::path storepath = readLink(symlink).parent_path(); exprs.push_back( "f: rec { name = \"" + cname + "\"; type = \"derivation\"; outputs = [\"out\"]; system = \"builtin\"; outPath = builtins.storePath \"" - + storepath + "\"; out = { inherit outPath; };}"); + + storepath.string() + "\"; out = { inherit outPath; };}"); } else { // We want to download the url to a file to see if it's a tarball while also checking if we // got redirected in the process, so that we can grab the various parts of a nix channel @@ -133,12 +142,15 @@ static void update(const StringSet & channelNames) bool unpacked = false; if (std::regex_search(std::string{result.storePath.to_string()}, std::regex("\\.tar\\.(gz|bz2|xz)$"))) { runProgram( - getNixBin("nix-build").string(), + getNixBin("nix-build"), false, - {"--no-out-link", - "--expr", - "import " + unpackChannelPath + "{ name = \"" + cname + "\"; channelName = \"" + name - + "\"; src = builtins.storePath \"" + store->printStorePath(result.storePath) + "\"; }"}); + { + OS_STR("--no-out-link"), + OS_STR("--expr"), + string_to_os_string( + "import " + unpackChannelPath + "{ name = \"" + cname + "\"; channelName = \"" + name + + "\"; src = builtins.storePath \"" + store->printStorePath(result.storePath) + "\"; }"), + }); unpacked = true; } @@ -161,12 +173,18 @@ static void update(const StringSet & channelNames) // Unpack the channel tarballs into the Nix store and install them // into the channels profile. std::cerr << "unpacking " << exprs.size() << " channels...\n"; - Strings envArgs{ - "--profile", profile, "--file", unpackChannelPath, "--install", "--remove-all", "--from-expression"}; + OsStrings envArgs{ + OS_STR("--profile"), + profile.native(), + OS_STR("--file"), + string_to_os_string(unpackChannelPath), + OS_STR("--install"), + OS_STR("--remove-all"), + OS_STR("--from-expression")}; for (auto & expr : exprs) - envArgs.push_back(std::move(expr)); - envArgs.push_back("--quiet"); - runProgram(getNixBin("nix-env").string(), false, envArgs); + envArgs.push_back(string_to_os_string(std::move(expr))); + envArgs.push_back(OS_STR("--quiet")); + runProgram(getNixBin("nix-env"), false, envArgs); // Make the channels appear in nix-env. PosixStat st; @@ -174,12 +192,12 @@ static void update(const StringSet & channelNames) if (S_ISLNK(st.st_mode)) // old-skool ~/.nix-defexpr if (unlink(nixDefExpr.c_str()) == -1) - throw SysError("unlinking %1%", nixDefExpr); + throw SysError("unlinking %1%", PathFmt(nixDefExpr)); } else if (errno != ENOENT) { - throw SysError("getting status of %1%", nixDefExpr); + throw SysError("getting status of %1%", PathFmt(nixDefExpr)); } createDirs(nixDefExpr); - auto channelLink = nixDefExpr + "/channels"; + auto channelLink = nixDefExpr / "channels"; replaceSymlink(profile, channelLink); } @@ -193,8 +211,8 @@ static int main_nix_channel(int argc, char ** argv) nixDefExpr = getNixDefExpr(); // Figure out the name of the channels profile. - profile = (profilesDir(settings.getProfileDirsOptions()) / "channels").string(); - createDirs(dirOf(profile)); + profile = profilesDir(settings.getProfileDirsOptions()) / "channels"; + createDirs(profile.parent_path()); enum { cNone, cAdd, cRemove, cList, cUpdate, cListGenerations, cRollback } cmd = cNone; @@ -261,20 +279,26 @@ static int main_nix_channel(int argc, char ** argv) case cListGenerations: if (!args.empty()) throw UsageError("'--list-generations' expects no arguments"); - std::cout << runProgram(getNixBin("nix-env").string(), false, {"--profile", profile, "--list-generations"}) - << std::flush; + std::cout << runProgram( + getNixBin("nix-env"), + false, + { + OS_STR("--profile"), + profile.native(), + OS_STR("--list-generations"), + }) << std::flush; break; case cRollback: if (args.size() > 1) throw UsageError("'--rollback' has at most one argument"); - Strings envArgs{"--profile", profile}; + OsStrings envArgs{OS_STR("--profile"), profile.native()}; if (args.size() == 1) { - envArgs.push_back("--switch-generation"); - envArgs.push_back(args[0]); + envArgs.push_back(OS_STR("--switch-generation")); + envArgs.push_back(string_to_os_string(args[0])); } else { - envArgs.push_back("--rollback"); + envArgs.push_back(OS_STR("--rollback")); } - runProgram(getNixBin("nix-env").string(), false, envArgs); + runProgram(getNixBin("nix-env"), false, envArgs); break; } diff --git a/src/nix/nix-env/nix-env.cc b/src/nix/nix-env/nix-env.cc index 946b6ece480..a04ea7f41f0 100644 --- a/src/nix/nix-env/nix-env.cc +++ b/src/nix/nix-env/nix-env.cc @@ -74,7 +74,7 @@ struct InstallSourceInfo { InstallSourceType type; std::shared_ptr nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ - Path profile; /* for srcProfile */ + std::filesystem::path profile; /* for srcProfile */ std::string systemFilter; /* for srcNixExprDrvs */ Bindings * autoArgs; }; @@ -82,7 +82,7 @@ struct InstallSourceInfo struct Globals { InstallSourceInfo instSource; - Path profile; + std::filesystem::path profile; std::shared_ptr state; bool dryRun; bool preserveInstalled; @@ -522,8 +522,8 @@ static void setMetaFlag(EvalState & state, PackageInfo & drv, const std::string drv.setMeta(name, v); } -static void -installDerivations(Globals & globals, const Strings & args, const Path & profile, std::optional priority) +static void installDerivations( + Globals & globals, const Strings & args, const std::filesystem::path & profile, std::optional priority) { debug("installing derivations"); @@ -800,7 +800,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) switchLink(globals.profile, generation); } -static void uninstallDerivations(Globals & globals, Strings & selectors, Path & profile) +static void uninstallDerivations(Globals & globals, Strings & selectors, const std::filesystem::path & profile) { while (true) { auto lockToken = optimisticLockProfile(profile); diff --git a/src/nix/nix-env/user-env.cc b/src/nix/nix-env/user-env.cc index 551dfd2a88e..f87e7f6103a 100644 --- a/src/nix/nix-env/user-env.cc +++ b/src/nix/nix-env/user-env.cc @@ -16,15 +16,15 @@ namespace nix { -PackageInfos queryInstalled(EvalState & state, const Path & userEnv) +PackageInfos queryInstalled(EvalState & state, const std::filesystem::path & userEnv) { PackageInfos elems; - if (pathExists(userEnv + "/manifest.json")) - throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv); - auto manifestFile = userEnv + "/manifest.nix"; + if (pathExists(userEnv / "manifest.json")) + throw Error("profile %s is incompatible with 'nix-env'; please use 'nix profile' instead", PathFmt(userEnv)); + auto manifestFile = userEnv / "manifest.nix"; if (pathExists(manifestFile)) { Value v; - state.evalFile(state.rootPath(CanonPath(manifestFile)).resolveSymlinks(), v); + state.evalFile(state.rootPath(CanonPath(manifestFile.string())).resolveSymlinks(), v); Bindings & bindings = Bindings::emptyBindings; getDerivations(state, v, "", bindings, elems, false); } @@ -32,7 +32,11 @@ PackageInfos queryInstalled(EvalState & state, const Path & userEnv) } bool createUserEnv( - EvalState & state, PackageInfos & elems, const Path & profile, bool keepDerivations, const std::string & lockToken) + EvalState & state, + PackageInfos & elems, + const std::filesystem::path & profile, + bool keepDerivations, + const std::string & lockToken) { /* Build the components in the user environment, if they don't exist already. */ @@ -163,7 +167,7 @@ bool createUserEnv( std::filesystem::path lockTokenCur = optimisticLockProfile(profile); if (lockToken != lockTokenCur) { - printInfo("profile '%1%' changed while we were busy; restarting", profile); + printInfo("profile %s changed while we were busy; restarting", PathFmt(profile)); return false; } diff --git a/src/nix/nix-env/user-env.hh b/src/nix/nix-env/user-env.hh index abe25af65fe..2c0ce9da0da 100644 --- a/src/nix/nix-env/user-env.hh +++ b/src/nix/nix-env/user-env.hh @@ -5,9 +5,13 @@ namespace nix { -PackageInfos queryInstalled(EvalState & state, const Path & userEnv); +PackageInfos queryInstalled(EvalState & state, const std::filesystem::path & userEnv); bool createUserEnv( - EvalState & state, PackageInfos & elems, const Path & profile, bool keepDerivations, const std::string & lockToken); + EvalState & state, + PackageInfos & elems, + const std::filesystem::path & profile, + bool keepDerivations, + const std::string & lockToken); } // namespace nix diff --git a/src/nix/nix-instantiate/nix-instantiate.cc b/src/nix/nix-instantiate/nix-instantiate.cc index 31ee5feedfe..6651b2c4530 100644 --- a/src/nix/nix-instantiate/nix-instantiate.cc +++ b/src/nix/nix-instantiate/nix-instantiate.cc @@ -19,7 +19,7 @@ using namespace nix; -static Path gcRoot; +std::filesystem::path gcRoot; static int rootNr = 0; enum OutputKind { okPlain, okRaw, okXML, okJSON }; @@ -83,15 +83,15 @@ void processExpr( if (outputName == "") throw Error("derivation '%1%' lacks an 'outputName' attribute", drvPathS); - if (gcRoot == "") + if (gcRoot.empty()) printGCWarning(); else { - Path rootName = absPath(gcRoot); + auto rootName = absPath(gcRoot); if (++rootNr > 1) rootName += "-" + std::to_string(rootNr); auto store2 = state.store.dynamic_pointer_cast(); if (store2) - drvPathS = store2->addPermRoot(drvPath, rootName); + drvPathS = store2->addPermRoot(drvPath, rootName.string()); } std::cout << fmt("%s%s\n", drvPathS, (outputName != "out" ? "!" + outputName : "")); } diff --git a/src/nix/nix-store/nix-store.cc b/src/nix/nix-store/nix-store.cc index 45fb8e58177..1aba51aa68f 100644 --- a/src/nix/nix-store/nix-store.cc +++ b/src/nix/nix-store/nix-store.cc @@ -46,7 +46,7 @@ using std::cout; typedef void (*Operation)(Strings opFlags, Strings opArgs); -static Path gcRoot; +static std::filesystem::path gcRoot; static int rootNr = 0; static bool noOutput = false; static std::shared_ptr store; @@ -99,12 +99,12 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) if (gcRoot == "") printGCWarning(); else { - Path rootName = gcRoot; + std::filesystem::path rootName = gcRoot; if (rootNr > 1) rootName += "-" + std::to_string(rootNr); if (i->first != "out") rootName += "-" + i->first; - retPath = store2->addPermRoot(outPath, rootName); + retPath = store2->addPermRoot(outPath, rootName.string()); } } outputs.insert(retPath); @@ -121,11 +121,11 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) if (gcRoot == "") printGCWarning(); else { - Path rootName = gcRoot; + std::filesystem::path rootName = gcRoot; rootNr++; if (rootNr > 1) rootName += "-" + std::to_string(rootNr); - return {store2->addPermRoot(path.path, rootName)}; + return {store2->addPermRoot(path.path, rootName.string())}; } } return {store->printStorePath(path.path)}; @@ -528,8 +528,8 @@ static void opPrintEnv(Strings opFlags, Strings opArgs) if (opArgs.size() != 1) throw UsageError("'--print-env' requires one derivation store path"); - Path drvPath = opArgs.front(); - Derivation drv = store->derivationFromPath(store->parseStorePath(drvPath)); + StorePath drvPath = store->parseStorePath(opArgs.front()); + Derivation drv = store->derivationFromPath(drvPath); /* Print each environment variable in the derivation in a format * that can be sourced by the shell. */ @@ -689,7 +689,7 @@ static void opGC(Strings opFlags, Strings opArgs) if (printRoots) { Roots roots = gcStore.findRoots(false); - std::set> roots2; + std::set> roots2; // Transpose and sort the roots. for (auto & [target, links] : roots) for (auto & link : links) diff --git a/src/nix/registry.cc b/src/nix/registry.cc index 6d913adcd6c..c943e80f9e1 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -40,7 +40,7 @@ class RegistryCommand : virtual Args return registry; } - Path getRegistryPath() + std::filesystem::path getRegistryPath() { if (registry_path.empty()) { return fetchers::getUserRegistryPath().string(); @@ -118,7 +118,7 @@ struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand extraAttrs["dir"] = toRef.subdir; registry->remove(fromRef.input); registry->add(fromRef.input, toRef.input, extraAttrs); - registry->write(getRegistryPath()); + registry->write(getRegistryPath().string()); } }; @@ -147,7 +147,7 @@ struct CmdRegistryRemove : RegistryCommand, Command { auto registry = getRegistry(); registry->remove(parseFlakeRef(fetchSettings, url).input); - registry->write(getRegistryPath()); + registry->write(getRegistryPath().string()); } }; @@ -198,7 +198,7 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand extraAttrs["dir"] = ref.subdir; registry->remove(ref.input); registry->add(ref.input, resolved, extraAttrs); - registry->write(getRegistryPath()); + registry->write(getRegistryPath().string()); } }; diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 1fc30954b72..ea1ecb305aa 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -6,13 +6,14 @@ #include "nix/cmd/command.hh" #include "nix/cmd/installable-value.hh" #include "nix/cmd/repl.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/util/environment-variables.hh" #include "self-exe.hh" namespace nix { -void runNix(const std::string & program, const Strings & args, const std::optional & input = {}) +void runNix(const std::string & program, OsStrings args, const std::optional & input = {}) { auto subprocessEnv = getEnvOs(); subprocessEnv[OS_STR("NIX_CONFIG")] = string_to_os_string(globalConfig.toKeyValue()); @@ -20,7 +21,7 @@ void runNix(const std::string & program, const Strings & args, const std::option runProgram2( RunOptions{ .program = getNixBin(program).string(), - .args = args, + .args = std::move(args), .environment = subprocessEnv, .input = input, .isInteractive = true, diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index 3258aca99f8..c72204cea3d 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -52,13 +52,11 @@ struct CmdCopySigs : StorePathsCommand // logger->setExpected(doneLabel, storePaths.size()); - auto doPath = [&](const Path & storePathS) { + auto doPath = [&](const StorePath & storePath) { // Activity act(*logger, lvlInfo, "getting signatures for '%s'", storePath); checkInterrupt(); - auto storePath = store->parseStorePath(storePathS); - auto info = store->queryPathInfo(storePath); std::set newSigs; @@ -89,7 +87,7 @@ struct CmdCopySigs : StorePathsCommand }; for (auto & storePath : storePaths) - pool.enqueue(std::bind(doPath, store->printStorePath(storePath))); + pool.enqueue(std::bind(doPath, storePath)); pool.process(); @@ -101,7 +99,7 @@ static auto rCmdCopySigs = registerCommand2({"store", "copy-sigs"}) struct CmdSign : StorePathsCommand { - Path secretKeyFile; + std::filesystem::path secretKeyFile; CmdSign() { diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index bb7066444be..10599408e67 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -1,3 +1,4 @@ +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/cmd/command.hh" #include "nix/main/common-args.hh" @@ -111,7 +112,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Activity act( *logger, lvlInfo, actUnknown, fmt("verifying that '%s' works...", store->printStorePath(storePath))); auto program = store->printStorePath(storePath) + "/bin/nix-env"; - auto s = runProgram(program, false, {"--version"}); + auto s = runProgram(program, false, {OS_STR("--version")}); if (s.find("Nix") == std::string::npos) throw Error("could not verify that '%s' works", program); } @@ -127,9 +128,15 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand // FIXME: don't call an external process. runProgram( - getNixBin("nix-env").string(), + getNixBin("nix-env"), false, - {"--profile", profileDir.string(), "-i", store->printStorePath(storePath), "--no-sandbox"}); + { + OS_STR("--profile"), + profileDir.native(), + OS_STR("-i"), + string_to_os_string(store->printStorePath(storePath)), + OS_STR("--no-sandbox"), + }); } printInfo(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version); @@ -151,13 +158,13 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto profileDir = where.parent_path(); // Resolve profile to /nix/var/nix/profiles/ link. - while (canonPath(profileDir.string()).find("/profiles/") == std::string::npos + while (canonPath(profileDir).string().find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) - profileDir = readLink(profileDir.string()); + profileDir = readLink(profileDir); printInfo("found profile %s", PathFmt(profileDir)); - Path userEnv = canonPath(profileDir.string(), true); + auto userEnv = canonPath(profileDir); if (std::filesystem::exists(profileDir / "manifest.json")) throw Error( @@ -167,8 +174,8 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand if (!std::filesystem::exists(profileDir / "manifest.nix")) throw Error("directory %s does not appear to be part of a Nix profile", PathFmt(profileDir)); - if (!store->isValidPath(store->parseStorePath(userEnv))) - throw Error("directory '%s' is not in the Nix store", userEnv); + if (!store->isValidPath(store->parseStorePath(userEnv.string()))) + throw Error("directory %s is not in the Nix store", PathFmt(userEnv)); return profileDir; } diff --git a/src/nswrapper/nswrapper.cc b/src/nswrapper/nswrapper.cc index 8e2fef69aa7..c7a659aa0e3 100644 --- a/src/nswrapper/nswrapper.cc +++ b/src/nswrapper/nswrapper.cc @@ -1,4 +1,5 @@ #include "nix/util/file-descriptor.hh" +#include "nix/util/os-string.hh" #include "nix/util/processes.hh" #include "nix/util/util.hh" #include @@ -61,7 +62,7 @@ void mainWrapped(int argc, char ** argv) runProgram( "newuidmap", true, - { + toOsStrings({ std::to_string(parentPid), // UID 0 in namespace is euid of parent "0", @@ -71,12 +72,12 @@ void mainWrapped(int argc, char ** argv) std::to_string(baseExtra), std::to_string(baseExtra), std::to_string(numExtra), - }); + })); runProgram( "newgidmap", true, - { + toOsStrings({ std::to_string(parentPid), // GID 0 in namespace is egid of parent "0", @@ -86,7 +87,7 @@ void mainWrapped(int argc, char ** argv) std::to_string(baseExtra), std::to_string(baseExtra), std::to_string(numExtra), - }); + })); exit(0); });