From 319d77c1c60b1fbe2cbdc11c6d9773eb5369faba Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 2 Sep 2023 17:35:16 -0400 Subject: [PATCH] Build a minimized Nix with MinGW At this point many features are stripped out, but this works: - Can run libnix{util,store,expr} unit tests - Can run some Nix commands Co-Authored-By volth Co-Authored-By Brian McKenna --- Makefile | 12 +- m4/gcc_bug_80431.m4 | 8 +- precompiled-headers.h | 21 +-- src/libcmd/common-eval-args.cc | 2 +- src/libcmd/markdown.cc | 4 +- src/libcmd/repl-interacter.cc | 4 + src/libcmd/repl.cc | 4 + src/libexpr/eval.cc | 10 +- src/libexpr/eval.hh | 2 + src/libexpr/primops.cc | 13 +- src/libexpr/search-path.hh | 3 + src/libfetchers/git-utils.cc | 26 ++- src/libmain/shared.cc | 13 ++ src/libmain/shared.hh | 8 +- src/libmain/{ => unix}/stack.cc | 0 src/libstore/builtins/buildenv.cc | 6 +- src/libstore/daemon.cc | 7 +- src/libstore/filetransfer.cc | 12 ++ src/libstore/globals.cc | 17 +- src/libstore/globals.hh | 2 + src/libstore/local.mk | 17 +- src/libstore/misc.cc | 1 - src/libstore/profiles.cc | 12 ++ src/libstore/profiles.hh | 8 +- src/libstore/remote-fs-accessor.cc | 13 +- src/libstore/ssh.cc | 20 ++- src/libstore/ssh.hh | 15 +- src/libstore/store-api.cc | 20 ++- src/libstore/{ => unix}/build/child.cc | 0 src/libstore/{ => unix}/build/child.hh | 0 .../{ => unix}/build/derivation-goal.cc | 0 .../{ => unix}/build/derivation-goal.hh | 0 .../build/drv-output-substitution-goal.cc | 0 .../build/drv-output-substitution-goal.hh | 0 src/libstore/{ => unix}/build/entry-points.cc | 0 src/libstore/{ => unix}/build/goal.cc | 0 src/libstore/{ => unix}/build/goal.hh | 0 .../{ => unix}/build/hook-instance.cc | 0 .../{ => unix}/build/hook-instance.hh | 0 .../{ => unix}/build/local-derivation-goal.cc | 0 .../{ => unix}/build/local-derivation-goal.hh | 0 src/libstore/{ => unix}/build/personality.cc | 0 src/libstore/{ => unix}/build/personality.hh | 0 .../{ => unix}/build/sandbox-defaults.sb | 0 .../{ => unix}/build/sandbox-minimal.sb | 0 .../{ => unix}/build/sandbox-network.sb | 0 .../{ => unix}/build/substitution-goal.cc | 0 .../{ => unix}/build/substitution-goal.hh | 0 src/libstore/{ => unix}/build/worker.cc | 0 src/libstore/{ => unix}/build/worker.hh | 0 src/libstore/{ => unix}/builtins/fetchurl.cc | 0 .../{ => unix}/builtins/unpack-channel.cc | 0 .../{ => unix}/ca-specific-schema.sql | 0 src/libstore/{ => unix}/gc.cc | 0 src/libstore/{ => unix}/local-store.cc | 0 src/libstore/{ => unix}/local-store.hh | 0 src/libstore/{ => unix}/local-store.md | 0 src/libstore/{ => unix}/lock.cc | 0 src/libstore/{ => unix}/lock.hh | 0 src/libstore/{ => unix}/optimise-store.cc | 0 src/libstore/{ => unix}/pathlocks.cc | 0 src/libstore/{ => unix}/pathlocks.hh | 0 .../{ => unix}/posix-fs-canonicalise.cc | 0 .../{ => unix}/posix-fs-canonicalise.hh | 0 src/libstore/{ => unix}/schema.sql | 0 src/libstore/{ => unix}/uds-remote-store.cc | 0 src/libstore/{ => unix}/uds-remote-store.hh | 0 src/libstore/{ => unix}/uds-remote-store.md | 0 src/libstore/windows/build.cc | 37 +++++ src/libutil/args.cc | 6 +- src/libutil/current-process.cc | 15 +- src/libutil/current-process.hh | 9 +- src/libutil/environment-variables.hh | 7 + src/libutil/error.hh | 17 ++ src/libutil/file-descriptor.cc | 27 +++- src/libutil/file-descriptor.hh | 54 ++++++- src/libutil/file-path.hh | 52 ++++++ src/libutil/file-system.cc | 109 ++++++++++--- src/libutil/file-system.hh | 14 ++ src/libutil/fs-sink.cc | 31 +++- src/libutil/local.mk | 6 + src/libutil/logging.cc | 19 ++- src/libutil/posix-source-accessor.cc | 33 ++-- src/libutil/serialise.cc | 14 ++ src/libutil/terminal.cc | 9 +- src/libutil/terminal.hh | 4 + src/libutil/thread-pool.cc | 2 + src/libutil/unix/environment-variables.cc | 2 +- src/libutil/unix/file-path.cc | 31 ++++ src/libutil/users.hh | 8 +- src/libutil/windows/environment-variables.cc | 17 ++ src/libutil/windows/file-descriptor.cc | 148 ++++++++++++++++++ src/libutil/windows/file-path.cc | 52 ++++++ src/libutil/windows/signals-impl.hh | 41 +++++ src/libutil/windows/users.cc | 50 ++++++ src/libutil/windows/windows-error.cc | 31 ++++ src/libutil/windows/windows-error.hh | 51 ++++++ src/nix-env/nix-env.cc | 12 +- src/nix-env/user-env.cc | 2 + src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix-store/nix-store.cc | 29 +++- src/nix/cat.cc | 3 +- src/nix/develop.cc | 9 +- src/nix/dump-path.cc | 4 +- src/nix/eval.cc | 8 +- src/nix/flake.cc | 2 + src/nix/local.mk | 12 +- src/nix/log.cc | 2 +- src/nix/main.cc | 27 +++- src/nix/prefetch.cc | 2 +- src/nix/sigs.cc | 4 +- tests/unit/libutil/tests.cc | 7 +- 112 files changed, 1158 insertions(+), 143 deletions(-) rename src/libmain/{ => unix}/stack.cc (100%) rename src/libstore/{ => unix}/build/child.cc (100%) rename src/libstore/{ => unix}/build/child.hh (100%) rename src/libstore/{ => unix}/build/derivation-goal.cc (100%) rename src/libstore/{ => unix}/build/derivation-goal.hh (100%) rename src/libstore/{ => unix}/build/drv-output-substitution-goal.cc (100%) rename src/libstore/{ => unix}/build/drv-output-substitution-goal.hh (100%) rename src/libstore/{ => unix}/build/entry-points.cc (100%) rename src/libstore/{ => unix}/build/goal.cc (100%) rename src/libstore/{ => unix}/build/goal.hh (100%) rename src/libstore/{ => unix}/build/hook-instance.cc (100%) rename src/libstore/{ => unix}/build/hook-instance.hh (100%) rename src/libstore/{ => unix}/build/local-derivation-goal.cc (100%) rename src/libstore/{ => unix}/build/local-derivation-goal.hh (100%) rename src/libstore/{ => unix}/build/personality.cc (100%) rename src/libstore/{ => unix}/build/personality.hh (100%) rename src/libstore/{ => unix}/build/sandbox-defaults.sb (100%) rename src/libstore/{ => unix}/build/sandbox-minimal.sb (100%) rename src/libstore/{ => unix}/build/sandbox-network.sb (100%) rename src/libstore/{ => unix}/build/substitution-goal.cc (100%) rename src/libstore/{ => unix}/build/substitution-goal.hh (100%) rename src/libstore/{ => unix}/build/worker.cc (100%) rename src/libstore/{ => unix}/build/worker.hh (100%) rename src/libstore/{ => unix}/builtins/fetchurl.cc (100%) rename src/libstore/{ => unix}/builtins/unpack-channel.cc (100%) rename src/libstore/{ => unix}/ca-specific-schema.sql (100%) rename src/libstore/{ => unix}/gc.cc (100%) rename src/libstore/{ => unix}/local-store.cc (100%) rename src/libstore/{ => unix}/local-store.hh (100%) rename src/libstore/{ => unix}/local-store.md (100%) rename src/libstore/{ => unix}/lock.cc (100%) rename src/libstore/{ => unix}/lock.hh (100%) rename src/libstore/{ => unix}/optimise-store.cc (100%) rename src/libstore/{ => unix}/pathlocks.cc (100%) rename src/libstore/{ => unix}/pathlocks.hh (100%) rename src/libstore/{ => unix}/posix-fs-canonicalise.cc (100%) rename src/libstore/{ => unix}/posix-fs-canonicalise.hh (100%) rename src/libstore/{ => unix}/schema.sql (100%) rename src/libstore/{ => unix}/uds-remote-store.cc (100%) rename src/libstore/{ => unix}/uds-remote-store.hh (100%) rename src/libstore/{ => unix}/uds-remote-store.md (100%) create mode 100644 src/libstore/windows/build.cc create mode 100644 src/libutil/file-path.hh create mode 100644 src/libutil/unix/file-path.cc create mode 100644 src/libutil/windows/environment-variables.cc create mode 100644 src/libutil/windows/file-descriptor.cc create mode 100644 src/libutil/windows/file-path.cc create mode 100644 src/libutil/windows/signals-impl.hh create mode 100644 src/libutil/windows/users.cc create mode 100644 src/libutil/windows/windows-error.cc create mode 100644 src/libutil/windows/windows-error.hh diff --git a/Makefile b/Makefile index 62194278d0fe..2ed7b40dc279 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ clean-files += $(buildprefix)Makefile.config # List makefiles +include mk/platform.mk + ifeq ($(ENABLE_BUILD), yes) makefiles = \ mk/precompiled-headers.mk \ @@ -20,7 +22,10 @@ makefiles = \ src/nix/local.mk \ src/libutil-c/local.mk \ src/libstore-c/local.mk \ - src/libexpr-c/local.mk \ + src/libexpr-c/local.mk + +ifdef HOST_UNIX +makefiles += \ src/resolve-system-dependencies/local.mk \ scripts/local.mk \ misc/bash/local.mk \ @@ -30,6 +35,7 @@ makefiles = \ misc/launchd/local.mk \ misc/upstart/local.mk endif +endif ifeq ($(ENABLE_UNIT_TESTS), yes) makefiles += \ @@ -43,6 +49,7 @@ makefiles += \ endif ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) +ifdef HOST_UNIX makefiles += \ tests/functional/local.mk \ tests/functional/ca/local.mk \ @@ -51,6 +58,7 @@ makefiles += \ tests/functional/test-libstoreconsumer/local.mk \ tests/functional/plugins/local.mk endif +endif # Some makefiles require access to built programs and must be included late. makefiles-late = @@ -79,8 +87,6 @@ else unexport NIX_HARDENING_ENABLE endif -include mk/platform.mk - ifdef HOST_WINDOWS # Windows DLLs are stricter about symbol visibility than Unix shared # objects --- see https://gcc.gnu.org/wiki/Visibility for details. diff --git a/m4/gcc_bug_80431.m4 b/m4/gcc_bug_80431.m4 index e42f0195614f..cdc4ddb401a7 100644 --- a/m4/gcc_bug_80431.m4 +++ b/m4/gcc_bug_80431.m4 @@ -46,11 +46,13 @@ AC_DEFUN([ENSURE_NO_GCC_BUG_80431], ]])], [status_80431=0], [status_80431=$?], - [ - # Assume we're bug-free when cross-compiling - ]) + [status_80431='']) AC_LANG_POP(C++) AS_CASE([$status_80431], + [''],[ + AC_MSG_RESULT(cannot check because cross compiling) + AC_MSG_NOTICE(assume we are bug free) + ], [0],[ AC_MSG_RESULT(yes) ], diff --git a/precompiled-headers.h b/precompiled-headers.h index f52f1cab8134..e1a3f8cc031d 100644 --- a/precompiled-headers.h +++ b/precompiled-headers.h @@ -42,19 +42,22 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include #include #include #include -#include -#include -#include #include +#ifndef _WIN32 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + #include diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index b87bbbc27c5c..c6ee0d0b2593 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -181,7 +181,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) v->mkString(arg.s); }, [&](const AutoArgFile & arg) { - v->mkString(readFile(arg.path)); + v->mkString(readFile(arg.path.string())); }, [&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index d62ff0d96006..88c3f640bad9 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -3,9 +3,9 @@ #include "finally.hh" #include "terminal.hh" -#include #if HAVE_LOWDOWN -#include +# include +# include #endif namespace nix { diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 3e34ecdb6ad1..eb4361e25626 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -137,6 +137,7 @@ static constexpr const char * promptForType(ReplPromptType promptType) bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType) { +#ifndef _WIN32 // TODO use more signals.hh for this struct sigaction act, old; sigset_t savedSignalMask, set; @@ -161,9 +162,12 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT }; setupSignals(); +#endif char * s = readline(promptForType(promptType)); Finally doFree([&]() { free(s); }); +#ifndef _WIN32 // TODO use more signals.hh for this restoreSignals(); +#endif if (g_signal_received) { g_signal_received = 0; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 4f1bf4516391..7d86077b2da8 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -138,12 +138,14 @@ void runNix(Path program, const Strings & args, auto subprocessEnv = getEnv(); subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(); +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. runProgram2(RunOptions { .program = settings.nixBinDir+ "/" + program, .args = args, .environment = subprocessEnv, .input = input, }); +#endif return; } @@ -480,6 +482,7 @@ ProcessLineResult NixRepl::processLine(std::string line) reloadFiles(); } +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. else if (command == ":e" || command == ":edit") { Value v; evalString(arg, v); @@ -514,6 +517,7 @@ ProcessLineResult NixRepl::processLine(std::string line) state->resetFileCache(); reloadFiles(); } +#endif else if (command == ":t") { Value v; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 648032ea3315..c1c8880df758 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -33,15 +33,17 @@ #include #include #include -#include #include #include #include -#include #include #include +#ifndef _WIN32 // TODO use portable implementation +# include +#endif + #if HAVE_BOEHMGC #define GC_INCLUDE_NEW @@ -2632,9 +2634,11 @@ void EvalState::maybePrintStats() void EvalState::printStatistics() { +#ifndef _WIN32 // TODO use portable implementation struct rusage buf; getrusage(RUSAGE_SELF, &buf); float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000); +#endif uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *); uint64_t bLists = nrListElems * sizeof(Value *); @@ -2651,7 +2655,9 @@ void EvalState::printStatistics() if (outPath != "-") fs.open(outPath, std::fstream::out); json topObj = json::object(); +#ifndef _WIN32 // TODO implement topObj["cpuTime"] = cpuTime; +#endif topObj["envs"] = { {"number", nrEnvs}, {"elements", nrValuesInEnvs}, diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 4d53bcde6db1..fd0f078031a6 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -161,6 +161,8 @@ struct DebugTrace { bool isError; }; +// Don't want Windows function +#undef SearchPath class EvalState : public std::enable_shared_from_this { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8a2b3512b74d..f3603e737233 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -11,7 +11,6 @@ #include "path-references.hh" #include "store-api.hh" #include "util.hh" -#include "processes.hh" #include "value-to-json.hh" #include "value-to-xml.hh" #include "primops.hh" @@ -28,7 +27,11 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +# include "processes.hh" +#endif #include @@ -324,6 +327,8 @@ static RegisterPrimOp primop_import({ } }); +#ifndef _WIN32 // TODO implement via DLL loading on Windows + /* Want reasonable symbol names, so extern C */ /* !!! Should we pass the Pos or the file name too? */ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); @@ -396,6 +401,8 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v) } } +#endif + /* Return a string representing the type of the expression. */ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -4581,6 +4588,7 @@ void EvalState::createBaseEnv() )", }); +#ifndef _WIN32 // TODO implement on Windows // Miscellaneous if (evalSettings.enableNativeCode) { addPrimOp({ @@ -4594,6 +4602,7 @@ void EvalState::createBaseEnv() .fun = prim_exec, }); } +#endif addPrimOp({ .name = "__traceVerbose", diff --git a/src/libexpr/search-path.hh b/src/libexpr/search-path.hh index ce78135b51f1..231752ea66c5 100644 --- a/src/libexpr/search-path.hh +++ b/src/libexpr/search-path.hh @@ -8,6 +8,9 @@ namespace nix { +// Do not want the windows macro (alias to `SearchPathA`) +#undef SearchPath + /** * A "search path" is a list of ways look for something, used with * `builtins.findFile` and `< >` lookup expressions. diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5e560f5f3ce9..6fcd128b9c00 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -5,7 +5,9 @@ #include "memory-input-accessor.hh" #include "cache.hh" #include "finally.hh" -#include "processes.hh" +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. +# include "processes.hh" +#endif #include "signals.hh" #include "users.hh" #include "fs-sink.hh" @@ -151,11 +153,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { initLibGit2(); - if (pathExists(path.native())) { - if (git_repository_open(Setter(repo), path.c_str())) + if (pathExists(path.string())) { + if (git_repository_open(Setter(repo), path.string().c_str())) throw Error("opening Git repository '%s': %s", path, git_error_last()->message); } else { - if (git_repository_init(Setter(repo), path.c_str(), bare)) + if (git_repository_init(Setter(repo), path.string().c_str(), bare)) throw Error("creating Git repository '%s': %s", path, git_error_last()->message); } } @@ -210,7 +212,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this std::vector parseSubmodules(const std::filesystem::path & configFile) { GitConfig config; - if (git_config_open_ondisk(Setter(config), configFile.c_str())) + if (git_config_open_ondisk(Setter(config), configFile.string().c_str())) throw Error("parsing .gitmodules file: %s", git_error_last()->message); ConfigIterator it; @@ -282,7 +284,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this /* Get submodule info. */ auto modulesFile = path / ".gitmodules"; - if (pathExists(modulesFile)) + if (pathExists(modulesFile.string())) info.submodules = parseSubmodules(modulesFile); return info; @@ -377,12 +379,13 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this auto dir = this->path; Strings gitArgs; if (shallow) { - gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec }; + gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec }; } else { - gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec }; + gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--", url, refspec }; } + #ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. runProgram(RunOptions { .program = "git", .searchPath = true, @@ -392,6 +395,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this .input = {}, .isInteractive = true }); + #else + throw UnimplementedError("Cannot shell out to git on Windows yet"); + #endif } void verifyCommit( @@ -420,6 +426,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this } writeFile(allowedSignersFile, allowedSigners); +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. // Run verification command auto [status, output] = runProgram(RunOptions { .program = "git", @@ -450,6 +457,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this printTalkative("Signature verification on commit %s succeeded.", rev.gitRev()); else throw Error("Commit signature verification on commit %s failed: %s", rev.gitRev(), output); +#else + throw Error("Commit signature verification not implemented on Windows yet"); +#endif } Hash treeHashToNarHash(const Hash & treeHash) override diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 4c9051d3bc13..cd3b1ba5c887 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -108,7 +108,9 @@ std::string getArg(const std::string & opt, return *i; } +#ifndef _WIN32 static void sigHandler(int signo) { } +#endif void initNix() @@ -121,6 +123,7 @@ void initNix() initLibStore(); +#ifndef _WIN32 unix::startSignalHandlerThread(); /* Reset SIGCHLD to its default. */ @@ -135,6 +138,7 @@ void initNix() /* Install a dummy SIGUSR1 handler for use with pthread_kill(). */ act.sa_handler = sigHandler; if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1"); +#endif #if __APPLE__ /* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH. @@ -156,21 +160,26 @@ void initNix() if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP"); #endif +#ifndef _WIN32 /* Register a SIGSEGV handler to detect stack overflows. Why not initLibExpr()? initGC() is essentially that, but detectStackOverflow is not an instance of the init function concept, as it may have to be invoked more than once per process. */ detectStackOverflow(); +#endif /* There is no privacy in the Nix system ;-) At least not for now. In particular, store objects should be readable by everybody. */ umask(0022); +#ifndef _WIN32 /* Initialise the PRNG. */ struct timeval tv; gettimeofday(&tv, 0); srandom(tv.tv_usec); +#endif + } @@ -362,6 +371,7 @@ RunPager::RunPager() stopProgressBar(); +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pipe toPager; toPager.create(); @@ -383,11 +393,13 @@ RunPager::RunPager() std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) throw SysError("dupping standard output"); +#endif } RunPager::~RunPager() { +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. try { if (pid != -1) { std::cout.flush(); @@ -397,6 +409,7 @@ RunPager::~RunPager() } catch (...) { ignoreException(); } +#endif } diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 99c3dffab300..04a9e9c00fb5 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -1,7 +1,9 @@ #pragma once ///@file -#include "processes.hh" +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. +# include "processes.hh" +#endif #include "args.hh" #include "args/root.hh" #include "common-args.hh" @@ -89,8 +91,10 @@ public: ~RunPager(); private: +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pid pid; int std_out; +#endif }; extern volatile ::sig_atomic_t blockInt; @@ -112,6 +116,7 @@ struct PrintFreed }; +#ifndef _WIN32 /** * Install a SIGSEGV handler to detect stack overflows. */ @@ -141,5 +146,6 @@ extern std::function stackOverflowHandler; * logger. Exits the process immediately after. */ void defaultStackOverflowHandler(siginfo_t * info, void * ctx); +#endif } diff --git a/src/libmain/stack.cc b/src/libmain/unix/stack.cc similarity index 100% rename from src/libmain/stack.cc rename to src/libmain/unix/stack.cc diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 31a6b32f19c7..e009f5b9dbfa 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -76,7 +76,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target); if (unlink(dstFile.c_str()) == -1) throw SysError("unlinking '%1%'", dstFile); - if (mkdir(dstFile.c_str(), 0755) == -1) + if (mkdir(dstFile.c_str() + #ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0755 + #endif + ) == -1) throw SysError("creating directory '%1%'", dstFile); createLinks(state, target, dstFile, state.priorities[dstFile]); createLinks(state, srcFile, dstFile, priority); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index def2c80b2180..47d6d5541be2 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,5 +1,4 @@ #include "daemon.hh" -#include "monitor-fd.hh" #include "signals.hh" #include "worker-protocol.hh" #include "worker-protocol-impl.hh" @@ -16,6 +15,10 @@ #include "args.hh" #include "git.hh" +#ifndef _WIN32 // TODO need graceful async exit support on Windows? +# include "monitor-fd.hh" +#endif + namespace nix::daemon { Sink & operator << (Sink & sink, const Logger::Fields & fields) @@ -1018,7 +1021,9 @@ void processConnection( TrustedFlag trusted, RecursiveFlag recursive) { +#ifndef _WIN32 // TODO need graceful async exit support on Windows? auto monitor = !recursive ? std::make_unique(from.fd) : nullptr; +#endif /* Exchange the greeting. */ unsigned int magic = readInt(from); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index df89b5bd10cd..219b60c44a8c 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer Sync state_; + #ifndef _WIN32 // TODO need graceful async exit support on Windows? /* We can't use a std::condition_variable to wake up the curl thread, because it only monitors file descriptors. So use a pipe instead. */ Pipe wakeupPipe; + #endif std::thread workerThread; @@ -539,8 +541,10 @@ struct curlFileTransfer : public FileTransfer fileTransferSettings.httpConnections.get()); #endif + #ifndef _WIN32 // TODO need graceful async exit support on Windows? wakeupPipe.create(); fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK); + #endif workerThread = std::thread([&]() { workerThreadEntry(); }); } @@ -561,15 +565,19 @@ struct curlFileTransfer : public FileTransfer auto state(state_.lock()); state->quit = true; } + #ifndef _WIN32 // TODO need graceful async exit support on Windows? writeFull(wakeupPipe.writeSide.get(), " ", false); + #endif } void workerThreadMain() { /* Cause this thread to be notified on SIGINT. */ + #ifndef _WIN32 // TODO need graceful async exit support on Windows? auto callback = createInterruptCallback([&]() { stopWorkerThread(); }); + #endif #if __linux__ unshareFilesystem(); @@ -607,9 +615,11 @@ struct curlFileTransfer : public FileTransfer /* Wait for activity, including wakeup events. */ int numfds = 0; struct curl_waitfd extraFDs[1]; + #ifndef _WIN32 // TODO need graceful async exit support on Windows? extraFDs[0].fd = wakeupPipe.readSide.get(); extraFDs[0].events = CURL_WAIT_POLLIN; extraFDs[0].revents = 0; + #endif long maxSleepTimeMs = items.empty() ? 10000 : 100; auto sleepTimeMs = nextWakeup != std::chrono::steady_clock::time_point() @@ -693,7 +703,9 @@ struct curlFileTransfer : public FileTransfer throw nix::Error("cannot enqueue download request because the download thread is shutting down"); state->incoming.push(item); } + #ifndef _WIN32 // TODO need graceful async exit support on Windows? writeFull(wakeupPipe.writeSide.get(), " "); + #endif } #if ENABLE_S3 diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 46a860c4800b..83e54e008f12 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -9,11 +9,14 @@ #include #include #include -#include -#include #include +#ifndef _WIN32 +# include +# include +#endif + #ifdef __GLIBC__ # include # include @@ -62,7 +65,9 @@ Settings::Settings() , nixManDir(canonPath(NIX_MAN_DIR)) , nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) { +#ifndef _WIN32 buildUsersGroup = isRootUser() ? "nixbld" : ""; +#endif allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1"; auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or("")); @@ -245,11 +250,15 @@ StringSet Settings::getDefaultExtraPlatforms() bool Settings::isWSL1() { +#if __linux__ struct utsname utsbuf; uname(&utsbuf); // WSL1 uses -Microsoft suffix // WSL2 uses -microsoft-standard suffix return hasSuffix(utsbuf.release, "-Microsoft"); +#else + return false; +#endif } Path Settings::getDefaultSSLCertFile() @@ -347,6 +356,7 @@ void initPlugins() for (const auto & file : pluginFiles) { /* handle is purposefully leaked as there may be state in the DSO needed by the action of the plugin. */ +#ifndef _WIN32 // TODO implement via DLL loading on Windows void *handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) @@ -357,6 +367,9 @@ void initPlugins() void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry"); if (nix_plugin_entry) nix_plugin_entry(); +#else + throw Error("could not dynamically open plugin file '%s'", file); +#endif } } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 4bdbe3333f91..852dba76413a 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -666,6 +666,7 @@ public: Setting sandboxFallback{this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; +#ifndef _WIN32 Setting requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups", R"( Following the principle of least privilege, @@ -683,6 +684,7 @@ public: (since `root` usually has permissions to call setgroups) and `false` otherwise. )"}; +#endif #if __linux__ Setting sandboxShmSize{ diff --git a/src/libstore/local.mk b/src/libstore/local.mk index a924e15ab485..d9001bbbf595 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -4,9 +4,12 @@ libstore_NAME = libnixstore libstore_DIR := $(d) -libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) +libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc) ifdef HOST_UNIX - libstore_SOURCES += $(wildcard $(d)/unix/*.cc) + libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) +endif +ifdef HOST_WINDOWS + libstore_SOURCES += $(wildcard $(d)/windows/*.cc) endif libstore_LIBS = libutil @@ -63,9 +66,9 @@ libstore_CXXFLAGS += \ ifeq ($(embedded_sandbox_shell),yes) libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\" -$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh +$(d)/unix/build/local-derivation-goal.cc: $(d)/unix/embedded-sandbox-shell.gen.hh -$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell) +$(d)/unix/embedded-sandbox-shell.gen.hh: $(sandbox_shell) $(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp @mv $@.tmp $@ else @@ -74,11 +77,11 @@ else endif endif -$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh +$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh -$(d)/build.cc: +$(d)/unix/build.cc: -clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh +clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh $(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index cc8ad3d02739..cc3f4884f51b 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,7 +1,6 @@ #include "derivations.hh" #include "parsed-derivations.hh" #include "globals.hh" -#include "local-store.hh" #include "store-api.hh" #include "thread-pool.hh" #include "realisation.hh" diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 73d3976f43ae..8e5935c525eb 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -144,8 +144,10 @@ static void deleteGeneration2(const Path & profile, GenerationNumber gen, bool d void deleteGenerations(const Path & profile, const std::set & gensToDelete, bool dryRun) { +#ifndef _WIN32 // TODO implement path locks on Windows. PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, curGen] = findGenerations(profile); @@ -171,8 +173,10 @@ void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bo if (max == 0) throw Error("Must keep at least one generation, otherwise the current one would be deleted"); +#ifndef _WIN32 // TODO implement path locks on Windows. PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, _curGen] = findGenerations(profile); auto curGen = _curGen; @@ -192,8 +196,10 @@ void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bo void deleteOldGenerations(const Path & profile, bool dryRun) { +#ifndef _WIN32 // TODO implement path locks on Windows. PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, curGen] = findGenerations(profile); @@ -205,8 +211,10 @@ void deleteOldGenerations(const Path & profile, bool dryRun) void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun) { +#ifndef _WIN32 // TODO implement path locks on Windows. PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, curGen] = findGenerations(profile); @@ -266,8 +274,10 @@ void switchGeneration( std::optional dstGen, bool dryRun) { +#ifndef _WIN32 // TODO implement path locks on Windows. PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, curGen] = findGenerations(profile); @@ -292,11 +302,13 @@ void switchGeneration( } +#ifndef _WIN32 // TODO implement path locks on Windows. void lockProfile(PathLocks & lock, const Path & profile) { lock.lockPaths({profile}, fmt("waiting for lock on profile '%1%'", profile)); lock.setDeletion(true); } +#endif std::string optimisticLockProfile(const Path & profile) diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index 193c0bf21ccf..19a92bfe8628 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -6,8 +6,12 @@ */ #include "types.hh" -#include "pathlocks.hh" +#ifndef _WIN32 // TODO implement path locks on Windows. +# include "pathlocks.hh" +#endif + +#include #include @@ -187,11 +191,13 @@ void switchGeneration( std::optional dstGen, bool dryRun); +#ifndef _WIN32 // TODO implement path locks on Windows. /** * Ensure exclusive access to a profile. Any command that modifies * the profile first acquires this lock. */ void lockProfile(PathLocks & lock, const Path & profile); +#endif /** * Optimistic locking is used by long-running operations like `nix-env diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc index b44edfe890df..125de3d77a36 100644 --- a/src/libstore/remote-fs-accessor.cc +++ b/src/libstore/remote-fs-accessor.cc @@ -71,11 +71,20 @@ std::pair, CanonPath> RemoteFSAccessor::fetch(const CanonPat auto narAccessor = makeLazyNarAccessor(listing, [cacheFile](uint64_t offset, uint64_t length) { - AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC); + AutoCloseFD fd = toDescriptor(open(cacheFile.c_str(), O_RDONLY + #ifndef _WIN32 + | O_CLOEXEC + #endif + )); if (!fd) throw SysError("opening NAR cache file '%s'", cacheFile); - if (lseek(fd.get(), offset, SEEK_SET) != (off_t) offset) + #ifndef _WIN32 + // make dead code identifer in scope + constexpr int _O_RDONLY = 0; + #endif + + if (lseek(fromDescriptor(fd.get(), _O_RDONLY), offset, SEEK_SET) != (off_t) offset) throw SysError("seeking in '%s'", cacheFile); std::string buf(length, 0); diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 30fe73adb09f..7f223ddf2667 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -45,16 +45,23 @@ void SSHMaster::addCommonSSHOpts(Strings & args) } bool SSHMaster::isMasterRunning() { +#if _WIN32 // TODO re-enable on Windows, once we can start processes. + return false; +#else Strings args = {"-O", "check", host}; addCommonSSHOpts(args); auto res = runProgram(RunOptions {.program = "ssh", .args = args, .mergeStderrToStdout = true}); return res.first == 0; +#endif } std::unique_ptr SSHMaster::startCommand( Strings && command, Strings && extraSshArgs) { +#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes. + throw UnimplementedError("cannot yet SSH on windows because spawning processes is not yet implemented"); +#else Path socketPath = startMaster(); Pipe in, out; @@ -105,8 +112,8 @@ std::unique_ptr SSHMaster::startCommand( }, options); - in.readSide = -1; - out.writeSide = -1; + in.readSide = INVALID_DESCRIPTOR; + out.writeSide = INVALID_DESCRIPTOR; // Wait for the SSH connection to be established, // So that we don't overwrite the password prompt with our progress bar. @@ -126,15 +133,18 @@ std::unique_ptr SSHMaster::startCommand( conn->in = std::move(in.writeSide); return conn; +#endif } +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. + Path SSHMaster::startMaster() { if (!useMaster) return ""; auto state(state_.lock()); - if (state->sshMaster != -1) return state->socketPath; + if (state->sshMaster != INVALID_DESCRIPTOR) return state->socketPath; state->socketPath = (Path) *state->tmpDir + "/ssh.sock"; @@ -167,7 +177,7 @@ Path SSHMaster::startMaster() throw SysError("unable to execute '%s'", args.front()); }, options); - out.writeSide = -1; + out.writeSide = INVALID_DESCRIPTOR; std::string reply; try { @@ -182,4 +192,6 @@ Path SSHMaster::startMaster() return state->socketPath; } +#endif + } diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index 08bb43dfabd8..763b53a0d856 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -2,9 +2,12 @@ ///@file #include "sync.hh" -#include "processes.hh" #include "file-system.hh" +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. +# include "processes.hh" +#endif + namespace nix { class SSHMaster @@ -21,7 +24,9 @@ private: struct State { +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pid sshMaster; +#endif std::unique_ptr tmpDir; Path socketPath; }; @@ -31,13 +36,19 @@ private: void addCommonSSHOpts(Strings & args); bool isMasterRunning(); +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. + Path startMaster(); +#endif + public: SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1); struct Connection { +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pid sshPid; +#endif AutoCloseFD out, in; }; @@ -51,8 +62,6 @@ public: std::unique_ptr startCommand( Strings && command, Strings && extraSshArgs = {}); - - Path startMaster(); }; } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 62403e633ca6..510c31f314c3 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -13,7 +13,6 @@ #include "archive.hh" #include "callback.hh" #include "git.hh" -#include "remote-store.hh" #include "posix-source-accessor.hh" // FIXME this should not be here, see TODO below on // `addMultipleToStore`. @@ -21,6 +20,10 @@ #include "signals.hh" #include "users.hh" +#ifndef _WIN32 +# include "remote-store.hh" +#endif + #include #include @@ -1266,9 +1269,10 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath) } - -#include "local-store.hh" -#include "uds-remote-store.hh" +#ifndef _WIN32 +# include "local-store.hh" +# include "uds-remote-store.hh" +#endif namespace nix { @@ -1286,6 +1290,7 @@ std::pair splitUriAndParams(const std::string & uri_ return {uri, params}; } +#ifndef _WIN32 // Unused on Windows because the next `#ifndef` static bool isNonUriPath(const std::string & spec) { return @@ -1295,9 +1300,13 @@ static bool isNonUriPath(const std::string & spec) // might be special like "auto" && spec.find("/") != std::string::npos; } +#endif std::shared_ptr openFromNonUri(const std::string & uri, const Store::Params & params) { + // TODO reenable on Windows once we have `LocalStore` and + // `UDSRemoteStore`. + #ifndef _WIN32 if (uri == "" || uri == "auto") { auto stateDir = getOr(params, "state", settings.nixStateDir); if (access(stateDir.c_str(), R_OK | W_OK) == 0) @@ -1342,6 +1351,9 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else { return nullptr; } + #else + return nullptr; + #endif } // The `parseURL` function supports both IPv6 URIs as defined in diff --git a/src/libstore/build/child.cc b/src/libstore/unix/build/child.cc similarity index 100% rename from src/libstore/build/child.cc rename to src/libstore/unix/build/child.cc diff --git a/src/libstore/build/child.hh b/src/libstore/unix/build/child.hh similarity index 100% rename from src/libstore/build/child.hh rename to src/libstore/unix/build/child.hh diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc similarity index 100% rename from src/libstore/build/derivation-goal.cc rename to src/libstore/unix/build/derivation-goal.cc diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/unix/build/derivation-goal.hh similarity index 100% rename from src/libstore/build/derivation-goal.hh rename to src/libstore/unix/build/derivation-goal.hh diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/unix/build/drv-output-substitution-goal.cc similarity index 100% rename from src/libstore/build/drv-output-substitution-goal.cc rename to src/libstore/unix/build/drv-output-substitution-goal.cc diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/unix/build/drv-output-substitution-goal.hh similarity index 100% rename from src/libstore/build/drv-output-substitution-goal.hh rename to src/libstore/unix/build/drv-output-substitution-goal.hh diff --git a/src/libstore/build/entry-points.cc b/src/libstore/unix/build/entry-points.cc similarity index 100% rename from src/libstore/build/entry-points.cc rename to src/libstore/unix/build/entry-points.cc diff --git a/src/libstore/build/goal.cc b/src/libstore/unix/build/goal.cc similarity index 100% rename from src/libstore/build/goal.cc rename to src/libstore/unix/build/goal.cc diff --git a/src/libstore/build/goal.hh b/src/libstore/unix/build/goal.hh similarity index 100% rename from src/libstore/build/goal.hh rename to src/libstore/unix/build/goal.hh diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc similarity index 100% rename from src/libstore/build/hook-instance.cc rename to src/libstore/unix/build/hook-instance.cc diff --git a/src/libstore/build/hook-instance.hh b/src/libstore/unix/build/hook-instance.hh similarity index 100% rename from src/libstore/build/hook-instance.hh rename to src/libstore/unix/build/hook-instance.hh diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc similarity index 100% rename from src/libstore/build/local-derivation-goal.cc rename to src/libstore/unix/build/local-derivation-goal.cc diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh similarity index 100% rename from src/libstore/build/local-derivation-goal.hh rename to src/libstore/unix/build/local-derivation-goal.hh diff --git a/src/libstore/build/personality.cc b/src/libstore/unix/build/personality.cc similarity index 100% rename from src/libstore/build/personality.cc rename to src/libstore/unix/build/personality.cc diff --git a/src/libstore/build/personality.hh b/src/libstore/unix/build/personality.hh similarity index 100% rename from src/libstore/build/personality.hh rename to src/libstore/unix/build/personality.hh diff --git a/src/libstore/build/sandbox-defaults.sb b/src/libstore/unix/build/sandbox-defaults.sb similarity index 100% rename from src/libstore/build/sandbox-defaults.sb rename to src/libstore/unix/build/sandbox-defaults.sb diff --git a/src/libstore/build/sandbox-minimal.sb b/src/libstore/unix/build/sandbox-minimal.sb similarity index 100% rename from src/libstore/build/sandbox-minimal.sb rename to src/libstore/unix/build/sandbox-minimal.sb diff --git a/src/libstore/build/sandbox-network.sb b/src/libstore/unix/build/sandbox-network.sb similarity index 100% rename from src/libstore/build/sandbox-network.sb rename to src/libstore/unix/build/sandbox-network.sb diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/unix/build/substitution-goal.cc similarity index 100% rename from src/libstore/build/substitution-goal.cc rename to src/libstore/unix/build/substitution-goal.cc diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/unix/build/substitution-goal.hh similarity index 100% rename from src/libstore/build/substitution-goal.hh rename to src/libstore/unix/build/substitution-goal.hh diff --git a/src/libstore/build/worker.cc b/src/libstore/unix/build/worker.cc similarity index 100% rename from src/libstore/build/worker.cc rename to src/libstore/unix/build/worker.cc diff --git a/src/libstore/build/worker.hh b/src/libstore/unix/build/worker.hh similarity index 100% rename from src/libstore/build/worker.hh rename to src/libstore/unix/build/worker.hh diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/unix/builtins/fetchurl.cc similarity index 100% rename from src/libstore/builtins/fetchurl.cc rename to src/libstore/unix/builtins/fetchurl.cc diff --git a/src/libstore/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc similarity index 100% rename from src/libstore/builtins/unpack-channel.cc rename to src/libstore/unix/builtins/unpack-channel.cc diff --git a/src/libstore/ca-specific-schema.sql b/src/libstore/unix/ca-specific-schema.sql similarity index 100% rename from src/libstore/ca-specific-schema.sql rename to src/libstore/unix/ca-specific-schema.sql diff --git a/src/libstore/gc.cc b/src/libstore/unix/gc.cc similarity index 100% rename from src/libstore/gc.cc rename to src/libstore/unix/gc.cc diff --git a/src/libstore/local-store.cc b/src/libstore/unix/local-store.cc similarity index 100% rename from src/libstore/local-store.cc rename to src/libstore/unix/local-store.cc diff --git a/src/libstore/local-store.hh b/src/libstore/unix/local-store.hh similarity index 100% rename from src/libstore/local-store.hh rename to src/libstore/unix/local-store.hh diff --git a/src/libstore/local-store.md b/src/libstore/unix/local-store.md similarity index 100% rename from src/libstore/local-store.md rename to src/libstore/unix/local-store.md diff --git a/src/libstore/lock.cc b/src/libstore/unix/lock.cc similarity index 100% rename from src/libstore/lock.cc rename to src/libstore/unix/lock.cc diff --git a/src/libstore/lock.hh b/src/libstore/unix/lock.hh similarity index 100% rename from src/libstore/lock.hh rename to src/libstore/unix/lock.hh diff --git a/src/libstore/optimise-store.cc b/src/libstore/unix/optimise-store.cc similarity index 100% rename from src/libstore/optimise-store.cc rename to src/libstore/unix/optimise-store.cc diff --git a/src/libstore/pathlocks.cc b/src/libstore/unix/pathlocks.cc similarity index 100% rename from src/libstore/pathlocks.cc rename to src/libstore/unix/pathlocks.cc diff --git a/src/libstore/pathlocks.hh b/src/libstore/unix/pathlocks.hh similarity index 100% rename from src/libstore/pathlocks.hh rename to src/libstore/unix/pathlocks.hh diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/unix/posix-fs-canonicalise.cc similarity index 100% rename from src/libstore/posix-fs-canonicalise.cc rename to src/libstore/unix/posix-fs-canonicalise.cc diff --git a/src/libstore/posix-fs-canonicalise.hh b/src/libstore/unix/posix-fs-canonicalise.hh similarity index 100% rename from src/libstore/posix-fs-canonicalise.hh rename to src/libstore/unix/posix-fs-canonicalise.hh diff --git a/src/libstore/schema.sql b/src/libstore/unix/schema.sql similarity index 100% rename from src/libstore/schema.sql rename to src/libstore/unix/schema.sql diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/unix/uds-remote-store.cc similarity index 100% rename from src/libstore/uds-remote-store.cc rename to src/libstore/unix/uds-remote-store.cc diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/unix/uds-remote-store.hh similarity index 100% rename from src/libstore/uds-remote-store.hh rename to src/libstore/unix/uds-remote-store.hh diff --git a/src/libstore/uds-remote-store.md b/src/libstore/unix/uds-remote-store.md similarity index 100% rename from src/libstore/uds-remote-store.md rename to src/libstore/unix/uds-remote-store.md diff --git a/src/libstore/windows/build.cc b/src/libstore/windows/build.cc new file mode 100644 index 000000000000..3eadc5bdaf3c --- /dev/null +++ b/src/libstore/windows/build.cc @@ -0,0 +1,37 @@ +#include "store-api.hh" +#include "build-result.hh" + +namespace nix { + +void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) +{ + unsupported("buildPaths"); +} + +std::vector Store::buildPathsWithResults( + const std::vector & reqs, + BuildMode buildMode, + std::shared_ptr evalStore) +{ + unsupported("buildPathsWithResults"); +} + +BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, + BuildMode buildMode) +{ + unsupported("buildDerivation"); +} + + +void Store::ensurePath(const StorePath & path) +{ + unsupported("ensurePath"); +} + + +void Store::repairPath(const StorePath & path) +{ + unsupported("repairPath"); +} + +} diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 834fc7314ca0..243e3a5a6206 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -9,7 +9,9 @@ #include #include #include -#include +#ifndef _WIN32 +# include +#endif namespace nix { @@ -547,6 +549,7 @@ nlohmann::json Args::toJSON() static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs) { completions.setType(Completions::Type::Filenames); + #ifndef _WIN32 // TODO implement globbing completions on Windows glob_t globbuf; int flags = GLOB_NOESCAPE; #ifdef GLOB_ONLYDIR @@ -564,6 +567,7 @@ static void _completePath(AddCompletions & completions, std::string_view prefix, } } globfree(&globbuf); + #endif } void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index d33f7163af2f..b69a22902932 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -5,9 +5,12 @@ #include "util.hh" #include "finally.hh" #include "file-system.hh" -#include "processes.hh" #include "signals.hh" +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. +# include "processes.hh" +#endif + #ifdef __APPLE__ # include #endif @@ -19,7 +22,9 @@ # include "namespaces.hh" #endif -#include +#ifndef _WIN32 +# include +#endif namespace nix { @@ -57,6 +62,7 @@ unsigned int getMaxCPU() ////////////////////////////////////////////////////////////////////// +#ifndef _WIN32 rlim_t savedStackSize = 0; void setStackSize(rlim_t stackSize) @@ -79,16 +85,20 @@ void setStackSize(rlim_t stackSize) } } } +#endif void restoreProcessContext(bool restoreMounts) { + #ifndef _WIN32 unix::restoreSignals(); + #endif if (restoreMounts) { #if __linux__ restoreMountNamespace(); #endif } + #ifndef _WIN32 if (savedStackSize) { struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0) { @@ -96,6 +106,7 @@ void restoreProcessContext(bool restoreMounts) setrlimit(RLIMIT_STACK, &limit); } } + #endif } diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index 444c717d1fcc..a5adb70cfd5b 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -2,7 +2,10 @@ ///@file #include -#include + +#ifndef _WIN32 +# include +#endif #include "types.hh" @@ -14,16 +17,18 @@ namespace nix { */ unsigned int getMaxCPU(); +#ifndef _WIN32 // TODO implement on Windows, if needed. /** * Change the stack size. */ void setStackSize(rlim_t stackSize); +#endif /** * Restore the original inherited Unix process context (such as signal * masks, stack size). - * See startSignalHandlerThread(), saveSignalMask(). + * See unix::startSignalHandlerThread(), unix::saveSignalMask(). */ void restoreProcessContext(bool restoreMounts = true); diff --git a/src/libutil/environment-variables.hh b/src/libutil/environment-variables.hh index 21c2356a4749..e0649adac347 100644 --- a/src/libutil/environment-variables.hh +++ b/src/libutil/environment-variables.hh @@ -28,6 +28,13 @@ std::optional getEnvNonEmpty(const std::string & key); */ std::map getEnv(); +#ifdef _WIN32 +/** + * Implementation of missing POSIX function. + */ +int unsetenv(const char * name); +#endif + /** * Like POSIX `setenv`, but always overrides. * diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 445b1e19cd15..0419f36d6340 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -247,6 +247,23 @@ public: } }; +#ifdef _WIN32 +class WinError; +#endif + +/** + * Convenience alias for when we use a `errno`-based error handling + * function on Unix, and `GetLastError()`-based error handling on on + * Windows. + */ +using NativeSysError = +#ifdef _WIN32 + WinError +#else + SysError +#endif + ; + /** * Throw an exception for the purpose of checking that exception * handling works; see 'initLibUtil()'. diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index 95cbb8537bc2..3bbfc50ee10f 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -5,6 +5,11 @@ #include #include +#ifdef _WIN32 +# include +# include +# include "windows-error.hh" +#endif namespace nix { @@ -20,7 +25,13 @@ std::string drainFD(Descriptor fd, bool block, const size_t reserveSize) // the parser needs two extra bytes to append terminating characters, other users will // not care very much about the extra memory. StringSink sink(reserveSize + 2); +#ifdef _WIN32 + // non-blocking is not supported this way on Windows + assert(block); + drainFD(fd, sink); +#else drainFD(fd, sink, block); +#endif return std::move(sink.s); } @@ -68,9 +79,15 @@ Descriptor AutoCloseFD::get() const void AutoCloseFD::close() { if (fd != INVALID_DESCRIPTOR) { - if(::close(fd) == -1) + if( +#ifdef _WIN32 + ::CloseHandle(fd) +#else + ::close(fd) +#endif + == -1) /* This should never happen. */ - throw SysError("closing file descriptor %1%", fd); + throw NativeSysError("closing file descriptor %1%", fd); fd = INVALID_DESCRIPTOR; } } @@ -80,14 +97,16 @@ void AutoCloseFD::fsync() if (fd != INVALID_DESCRIPTOR) { int result; result = -#if __APPLE__ +#ifdef _WIN32 + ::FlushFileBuffers(fd) +#elif __APPLE__ ::fcntl(fd, F_FULLFSYNC) #else ::fsync(fd) #endif ; if (result == -1) - throw SysError("fsync file descriptor %1%", fd); + throw NativeSysError("fsync file descriptor %1%", fd); } } diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 719e1e444926..fe5e78c1f5ac 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -4,6 +4,11 @@ #include "types.hh" #include "error.hh" +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#endif + namespace nix { struct Sink; @@ -12,9 +17,21 @@ struct Source; /** * Operating System capability */ -typedef int Descriptor; - -const Descriptor INVALID_DESCRIPTOR = -1; +typedef +#if _WIN32 + HANDLE +#else + int +#endif + Descriptor; + +const Descriptor INVALID_DESCRIPTOR = +#if _WIN32 + INVALID_HANDLE_VALUE +#else + -1 +#endif + ; /** * Convert a native `Descriptor` to a POSIX file descriptor @@ -23,7 +40,11 @@ const Descriptor INVALID_DESCRIPTOR = -1; */ static inline Descriptor toDescriptor(int fd) { +#ifdef _WIN32 + return (HANDLE) _get_osfhandle(fd); +#else return fd; +#endif } /** @@ -33,7 +54,11 @@ static inline Descriptor toDescriptor(int fd) */ static inline int fromDescriptor(Descriptor fd, int flags) { +#ifdef _WIN32 + return _open_osfhandle((intptr_t) fd, flags); +#else return fd; +#endif } /** @@ -64,11 +89,21 @@ void writeLine(Descriptor fd, std::string s); */ std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0); -void drainFD(Descriptor fd, Sink & sink, bool block = true); +void drainFD( + Descriptor fd + , Sink & sink +#ifndef _WIN32 + , bool block = true +#endif + ); [[gnu::always_inline]] inline Descriptor getStandardOut() { +#ifndef _WIN32 return STDOUT_FILENO; +#else + return GetStdHandle(STD_OUTPUT_HANDLE); +#endif } /** @@ -100,6 +135,8 @@ public: void close(); }; +#ifndef _WIN32 // Not needed on Windows, where we don't fork + /** * Close all file descriptors except those listed in the given set. * Good practice in child processes. @@ -111,6 +148,15 @@ void closeMostFDs(const std::set & exceptions); */ void closeOnExec(Descriptor fd); +#endif + +#ifdef _WIN32 +# if _WIN32_WINNT >= 0x0600 +Path handleToPath(Descriptor handle); +std::wstring handleToFileName(Descriptor handle); +# endif +#endif + MakeError(EndOfFile, Error); } diff --git a/src/libutil/file-path.hh b/src/libutil/file-path.hh new file mode 100644 index 000000000000..6fb1001250b2 --- /dev/null +++ b/src/libutil/file-path.hh @@ -0,0 +1,52 @@ +#pragma once +///@file + +#include +#include + +#include "types.hh" + +namespace nix { + +/** + * Paths are just `std::filesystem::path`s. + * + * @todo drop `NG` suffix and replace the ones in `types.hh`. + */ +typedef std::filesystem::path PathNG; +typedef std::list PathsNG; +typedef std::set PathSetNG; + +/** + * Stop gap until `std::filesystem::path_view` from P1030R6 exists in a + * future C++ standard. + * + * @todo drop `NG` suffix and replace the one in `types.hh`. + */ +struct PathViewNG : std::basic_string_view +{ + using string_view = std::basic_string_view; + + using string_view::string_view; + + PathViewNG(const PathNG & path) + : std::basic_string_view(path.native()) + { } + + PathViewNG(const PathNG::string_type & path) + : std::basic_string_view(path) + { } + + const string_view & native() const { return *this; } + string_view & native() { return *this; } +}; + +std::string os_string_to_string(PathViewNG::string_view path); + +PathNG::string_type string_to_os_string(std::string_view s); + +std::optional maybePathNG(PathView path); + +PathNG pathNG(PathView path); + +} diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 89d3097317bd..b03bb767b6f1 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -1,5 +1,6 @@ #include "environment-variables.hh" #include "file-system.hh" +#include "file-path.hh" #include "file-path-impl.hh" #include "signals.hh" #include "finally.hh" @@ -18,6 +19,10 @@ #include #include +#ifdef _WIN32 +# include +#endif + namespace fs = std::filesystem; namespace nix { @@ -128,10 +133,10 @@ std::string_view baseNameOf(std::string_view path) return ""; auto last = path.size() - 1; - while (last > 0 && path[last] == '/') + while (last > 0 && NativePathTrait::isPathSep(path[last])) last -= 1; - auto pos = path.rfind('/', last); + auto pos = NativePathTrait::rfindPathSep(path, last); if (pos == path.npos) pos = 0; else @@ -164,11 +169,16 @@ struct stat stat(const Path & path) return st; } +#ifdef _WIN32 +# define STAT stat +#else +# define STAT lstat +#endif struct stat lstat(const Path & path) { struct stat st; - if (lstat(path.c_str(), &st)) + if (STAT(path.c_str(), &st)) throw SysError("getting status of '%1%'", path); return st; } @@ -177,7 +187,7 @@ struct stat lstat(const Path & path) std::optional maybeLstat(const Path & path) { std::optional st{std::in_place}; - if (lstat(path.c_str(), &*st)) + if (STAT(path.c_str(), &*st)) { if (errno == ENOENT || errno == ENOTDIR) st.reset(); @@ -207,6 +217,7 @@ bool pathAccessible(const Path & path) Path readLink(const Path & path) { +#ifndef _WIN32 checkInterrupt(); std::vector buf; for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) { @@ -220,13 +231,16 @@ Path readLink(const Path & path) else if (rlSize < bufSize) return std::string(buf.data(), rlSize); } +#else + // TODO modern Windows does in fact support symlinks + throw UnimplementedError("reading symbolic link '%1%'", path); +#endif } bool isLink(const Path & path) { - struct stat st = lstat(path); - return S_ISLNK(st.st_mode); + return getFileType(path) == DT_LNK; } @@ -274,7 +288,12 @@ unsigned char getFileType(const Path & path) std::string readFile(const Path & path) { - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + )); if (!fd) throw SysError("opening file '%1%'", path); return readFile(fd.get()); @@ -283,7 +302,12 @@ std::string readFile(const Path & path) void readFile(const Path & path, Sink & sink) { - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + )); if (!fd) throw SysError("opening file '%s'", path); drainFD(fd.get(), sink); @@ -292,7 +316,12 @@ void readFile(const Path & path, Sink & sink) void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) { - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + , mode)); if (!fd) throw SysError("opening file '%1%'", path); try { @@ -312,7 +341,12 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) void writeFile(const Path & path, Source & source, mode_t mode, bool sync) { - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + , mode)); if (!fd) throw SysError("opening file '%1%'", path); @@ -339,21 +373,23 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync) void syncParent(const Path & path) { - AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0); + AutoCloseFD fd = toDescriptor(open(dirOf(path).c_str(), O_RDONLY, 0)); if (!fd) throw SysError("opening file '%1%'", path); fd.fsync(); } -static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) +static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytesFreed) { +#ifndef _WIN32 checkInterrupt(); std::string name(baseNameOf(path)); struct stat st; - if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) { + if (fstatat(parentfd, name.c_str(), &st, + AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) return; throw SysError("getting status of '%1%'", path); } @@ -405,6 +441,10 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) if (errno == ENOENT) return; throw SysError("cannot unlink '%1%'", path); } +#else + // TODO implement + throw UnimplementedError("_deletePath"); +#endif } static void _deletePath(const Path & path, uint64_t & bytesFreed) @@ -413,7 +453,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed) if (dir == "") dir = "/"; - AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)}; + AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY)); if (!dirfd) { if (errno == ENOENT) return; throw SysError("opening directory '%1%'", path); @@ -436,11 +476,15 @@ Paths createDirs(const Path & path) if (path == "/") return created; struct stat st; - if (lstat(path.c_str(), &st) == -1) { + if (STAT(path.c_str(), &st) == -1) { created = createDirs(dirOf(path)); - if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST) + if (mkdir(path.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0777 +#endif + ) == -1 && errno != EEXIST) throw SysError("creating directory '%1%'", path); - st = lstat(path); + st = STAT(path); created.push_back(path); } @@ -526,7 +570,11 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, while (1) { checkInterrupt(); Path tmpDir = tempName(tmpRoot, prefix, includePid, counter); - if (mkdir(tmpDir.c_str(), mode) == 0) { + if (mkdir(tmpDir.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , mode +#endif + ) == 0) { #if __FreeBSD__ /* Explicitly set the group of the directory. This is to work around around problems caused by BSD's group @@ -552,17 +600,24 @@ std::pair createTempFile(const Path & prefix) Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX"); // Strictly speaking, this is UB, but who cares... // FIXME: use O_TMPFILE. - AutoCloseFD fd(mkstemp((char *) tmpl.c_str())); + AutoCloseFD fd = toDescriptor(mkstemp((char *) tmpl.c_str())); if (!fd) throw SysError("creating temporary file '%s'", tmpl); +#ifndef _WIN32 closeOnExec(fd.get()); +#endif return {std::move(fd), tmpl}; } void createSymlink(const Path & target, const Path & link) { +#ifndef _WIN32 if (symlink(target.c_str(), link.c_str())) throw SysError("creating symlink from '%1%' to '%2%'", link, target); +#else + // TODO modern Windows does in fact support symlinks + throw UnimplementedError("createSymlink"); +#endif } void replaceSymlink(const Path & target, const Path & link) @@ -583,7 +638,8 @@ void replaceSymlink(const Path & target, const Path & link) } } -void setWriteTime(const fs::path & p, const struct stat & st) +#ifndef _WIN32 +static void setWriteTime(const fs::path & p, const struct stat & st) { struct timeval times[2]; times[0] = { @@ -597,11 +653,14 @@ void setWriteTime(const fs::path & p, const struct stat & st) if (lutimes(p.c_str(), times) != 0) throw SysError("changing modification time of '%s'", p); } +#endif void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) { +#ifndef _WIN32 // TODO: Rewrite the `is_*` to use `symlink_status()` auto statOfFrom = lstat(from.path().c_str()); +#endif auto fromStatus = from.symlink_status(); // Mark the directory as writable so that we can delete its children @@ -621,7 +680,9 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) throw Error("file '%s' has an unsupported type", from.path()); } +#ifndef _WIN32 setWriteTime(to, statOfFrom); +#endif if (andDelete) { if (!fs::is_symlink(fromStatus)) fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); @@ -648,14 +709,18 @@ void moveFile(const Path & oldName, const Path & newName) auto newPath = fs::path(newName); // For the move to be as atomic as possible, copy to a temporary // directory - fs::path temp = createTempDir(newPath.parent_path(), "rename-tmp"); + fs::path temp = createTempDir( + os_string_to_string(PathViewNG { newPath.parent_path() }), + "rename-tmp"); Finally removeTemp = [&]() { fs::remove(temp); }; auto tempCopyTarget = temp / "copy-target"; if (e.code().value() == EXDEV) { fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); copy(fs::directory_entry(oldPath), tempCopyTarget, true); - renameFile(tempCopyTarget, newPath); + renameFile( + os_string_to_string(PathViewNG { tempCopyTarget }), + os_string_to_string(PathViewNG { newPath })); } } } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 06a993829c00..0c4e7cfdd489 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -14,6 +14,9 @@ #include #include #include +#ifdef _WIN32 +# include +#endif #include #include @@ -31,6 +34,17 @@ #define DT_DIR 3 #endif +/** + * Polyfill for MinGW + * + * Windows does in fact support symlinks, but the C runtime interfaces predate this. + * + * @todo get rid of this, and stop using `stat` when we want `lstat` too. + */ +#ifndef S_ISLNK +# define S_ISLNK(m) false +#endif + namespace nix { struct Sink; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 35ce0ac36111..91070ea89a9a 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -1,8 +1,15 @@ #include +#include "error.hh" #include "config.hh" #include "fs-sink.hh" +#if _WIN32 +# include +# include "file-path.hh" +# include "windows-error.hh" +#endif + namespace nix { void copyRecursive( @@ -65,8 +72,14 @@ static GlobalConfig::Register r1(&restoreSinkSettings); void RestoreSink::createDirectory(const Path & path) { Path p = dstPath + path; - if (mkdir(p.c_str(), 0777) == -1) - throw SysError("creating directory '%1%'", p); + if ( +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + mkdir(p.c_str(), 0777) == -1 +#else + !CreateDirectoryW(pathNG(p).c_str(), NULL) +#endif + ) + throw NativeSysError("creating directory '%1%'", p); }; struct RestoreRegularFile : CreateRegularFileSink { @@ -81,18 +94,28 @@ void RestoreSink::createRegularFile(const Path & path, std::function nextId{0}; +static uint64_t getPid() +{ +#ifndef _WIN32 + return getpid(); +#else + return GetCurrentProcessId(); +#endif +} + Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type, const std::string & s, const Logger::Fields & fields, ActivityId parent) - : logger(logger), id(nextId++ + (((uint64_t) getpid()) << 32)) + : logger(logger), id(nextId++ + (((uint64_t) getPid()) << 32)) { logger.startActivity(id, lvl, type, s, fields, parent); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 8039d4b80fc0..cbac7323e127 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -10,7 +10,7 @@ PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root) : root(std::move(root)) { assert(root.empty() || root.is_absolute()); - displayPrefix = root; + displayPrefix = root.string(); } PosixSourceAccessor::PosixSourceAccessor() @@ -19,10 +19,10 @@ PosixSourceAccessor::PosixSourceAccessor() std::pair PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) { - std::filesystem::path path2 = absPath(path.native()); + std::filesystem::path path2 = absPath(path.string()); return { PosixSourceAccessor { path2.root_path() }, - CanonPath { static_cast(path2.relative_path()) }, + CanonPath { path2.relative_path().string() }, }; } @@ -47,12 +47,21 @@ void PosixSourceAccessor::readFile( auto ap = makeAbsPath(path); - AutoCloseFD fd = open(ap.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW); + AutoCloseFD fd = toDescriptor(open(ap.string().c_str(), O_RDONLY + #ifndef _WIN32 + | O_NOFOLLOW | O_CLOEXEC + #endif + )); if (!fd) - throw SysError("opening file '%1%'", ap.native()); + throw SysError("opening file '%1%'", ap.string()); + + #ifndef _WIN32 + // make dead code identifer in scope + constexpr int _O_RDONLY = 0; + #endif struct stat st; - if (fstat(fd.get(), &st) == -1) + if (fstat(fromDescriptor(fd.get(), _O_RDONLY), &st) == -1) throw SysError("statting file"); sizeCallback(st.st_size); @@ -62,7 +71,7 @@ void PosixSourceAccessor::readFile( std::array buf; while (left) { checkInterrupt(); - ssize_t rd = read(fd.get(), buf.data(), (size_t) std::min(left, (off_t) buf.size())); + ssize_t rd = read(fromDescriptor(fd.get(), _O_RDONLY), buf.data(), (size_t) std::min(left, (off_t) buf.size())); if (rd == -1) { if (errno != EINTR) throw SysError("reading from file '%s'", showPath(path)); @@ -80,7 +89,7 @@ void PosixSourceAccessor::readFile( bool PosixSourceAccessor::pathExists(const CanonPath & path) { if (auto parent = path.parent()) assertNoSymlinks(*parent); - return nix::pathExists(makeAbsPath(path)); + return nix::pathExists(makeAbsPath(path).string()); } std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) @@ -89,7 +98,7 @@ std::optional PosixSourceAccessor::cachedLstat(const CanonPath & pa // Note: we convert std::filesystem::path to Path because the // former is not hashable on libc++. - Path absPath = makeAbsPath(path); + Path absPath = makeAbsPath(path).string(); { auto cache(_cache.lock()); @@ -127,11 +136,13 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & { assertNoSymlinks(path); DirEntries res; - for (auto & entry : nix::readDirectory(makeAbsPath(path))) { + for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { std::optional type; switch (entry.type) { case DT_REG: type = Type::tRegular; break; + #ifndef _WIN32 case DT_LNK: type = Type::tSymlink; break; + #endif case DT_DIR: type = Type::tDirectory; break; } res.emplace(entry.name, type); @@ -142,7 +153,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & std::string PosixSourceAccessor::readLink(const CanonPath & path) { if (auto parent = path.parent()) assertNoSymlinks(*parent); - return nix::readLink(makeAbsPath(path)); + return nix::readLink(makeAbsPath(path).string()); } std::optional PosixSourceAccessor::getPhysicalPath(const CanonPath & path) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 70c16ff0dce0..5ea27ccbed84 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -7,6 +7,11 @@ #include +#ifdef _WIN32 +# include +# include "windows-error.hh" +#endif + namespace nix { @@ -126,6 +131,14 @@ bool BufferedSource::hasData() size_t FdSource::readUnbuffered(char * data, size_t len) { +#ifdef _WIN32 + DWORD n; + checkInterrupt(); + if (!::ReadFile(fd, data, len, &n, NULL)) { + _good = false; + throw WinError("ReadFile when FdSource::readUnbuffered"); + } +#else ssize_t n; do { checkInterrupt(); @@ -133,6 +146,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len) } while (n == -1 && errno == EINTR); if (n == -1) { _good = false; throw SysError("reading from file"); } if (n == 0) { _good = false; throw EndOfFile(std::string(*endOfFileError)); } +#endif read += n; return n; } diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 096252f03459..4dc280f8c040 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -2,7 +2,12 @@ #include "environment-variables.hh" #include "sync.hh" -#include +#if _WIN32 +# include +# define isatty _isatty +#else +# include +#endif #include namespace nix { @@ -92,6 +97,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w static Sync> windowSize{{0, 0}}; +#ifndef _WIN32 void updateWindowSize() { struct winsize ws; @@ -101,6 +107,7 @@ void updateWindowSize() windowSize_->second = ws.ws_col; } } +#endif std::pair getWindowSize() diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9d8d0c743286..628833283d4a 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -21,12 +21,16 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits::max()); +#ifndef _WIN32 + /** * Recalculate the window size, updating a global variable. Used in the * `SIGWINCH` signal handler. */ void updateWindowSize(); +#endif + /** * @return the number of rows and columns of the terminal. * diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 805f31d80680..0f63496421b0 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -81,8 +81,10 @@ void ThreadPool::doWork(bool mainThread) { ReceiveInterrupts receiveInterrupts; +#ifndef _WIN32 // Does Windows need anything similar for async exit handling? if (!mainThread) unix::interruptCheck = [&]() { return (bool) quit; }; +#endif bool didWork = false; std::exception_ptr exc; diff --git a/src/libutil/unix/environment-variables.cc b/src/libutil/unix/environment-variables.cc index 9c6fd3b18081..4b9dae8f445e 100644 --- a/src/libutil/unix/environment-variables.cc +++ b/src/libutil/unix/environment-variables.cc @@ -1,4 +1,4 @@ -#include +#include #include "environment-variables.hh" diff --git a/src/libutil/unix/file-path.cc b/src/libutil/unix/file-path.cc new file mode 100644 index 000000000000..54a1cc278d43 --- /dev/null +++ b/src/libutil/unix/file-path.cc @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + return std::string { path }; +} + +PathNG::string_type string_to_os_string(std::string_view s) +{ + return std::string { s }; +} + +std::optional maybePathNG(PathView path) +{ + return { path }; +} + +PathNG pathNG(PathView path) +{ + return path; +} + +} diff --git a/src/libutil/users.hh b/src/libutil/users.hh index 449e5bbe924a..153cc73fdae4 100644 --- a/src/libutil/users.hh +++ b/src/libutil/users.hh @@ -3,16 +3,20 @@ #include "types.hh" -#include +#ifndef _WIN32 +# include +#endif namespace nix { std::string getUserName(); +#ifndef _WIN32 /** * @return the given user's home directory from /etc/passwd. */ Path getHomeOf(uid_t userId); +#endif /** * @return $HOME or the user's home directory from /etc/passwd. @@ -58,6 +62,8 @@ std::string expandTilde(std::string_view path); /** * Is the current user UID 0 on Unix? + * + * Currently always false on Windows, but that may change. */ bool isRootUser(); diff --git a/src/libutil/windows/environment-variables.cc b/src/libutil/windows/environment-variables.cc new file mode 100644 index 000000000000..25ab9d63ad9c --- /dev/null +++ b/src/libutil/windows/environment-variables.cc @@ -0,0 +1,17 @@ +#include "environment-variables.hh" + +#include "processenv.h" + +namespace nix { + +int unsetenv(const char *name) +{ + return -SetEnvironmentVariableA(name, nullptr); +} + +int setEnv(const char * name, const char * value) +{ + return -SetEnvironmentVariableA(name, value); +} + +} diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc new file mode 100644 index 000000000000..26f769b6650e --- /dev/null +++ b/src/libutil/windows/file-descriptor.cc @@ -0,0 +1,148 @@ +#include "file-system.hh" +#include "signals.hh" +#include "finally.hh" +#include "serialise.hh" +#include "windows-error.hh" +#include "file-path.hh" + +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string readFile(HANDLE handle) +{ + LARGE_INTEGER li; + if (!GetFileSizeEx(handle, &li)) + throw WinError("%s:%d statting file", __FILE__, __LINE__); + + return drainFD(handle, true, li.QuadPart); +} + + +void readFull(HANDLE handle, char * buf, size_t count) +{ + while (count) { + checkInterrupt(); + DWORD res; + if (!ReadFile(handle, (char *) buf, count, &res, NULL)) + throw WinError("%s:%d reading from file", __FILE__, __LINE__); + if (res == 0) throw EndOfFile("unexpected end-of-file"); + count -= res; + buf += res; + } +} + + +void writeFull(HANDLE handle, std::string_view s, bool allowInterrupts) +{ + while (!s.empty()) { + if (allowInterrupts) checkInterrupt(); + DWORD res; +#if _WIN32_WINNT >= 0x0600 + auto path = handleToPath(handle); // debug; do it before becuase handleToPath changes lasterror + if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { + throw WinError("writing to file %1%:%2%", handle, path); + } +#else + if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { + throw WinError("writing to file %1%", handle); + } +#endif + if (res > 0) + s.remove_prefix(res); + } +} + + +std::string readLine(HANDLE handle) +{ + std::string s; + while (1) { + checkInterrupt(); + char ch; + // FIXME: inefficient + DWORD rd; + if (!ReadFile(handle, &ch, 1, &rd, NULL)) { + throw WinError("reading a line"); + } else if (rd == 0) + throw EndOfFile("unexpected EOF reading a line"); + else { + if (ch == '\n') return s; + s += ch; + } + } +} + + +void drainFD(HANDLE handle, Sink & sink/*, bool block*/) +{ + std::vector buf(64 * 1024); + while (1) { + checkInterrupt(); + DWORD rd; + if (!ReadFile(handle, buf.data(), buf.size(), &rd, NULL)) { + WinError winError("%s:%d reading from handle %p", __FILE__, __LINE__, handle); + if (winError.lastError == ERROR_BROKEN_PIPE) + break; + throw winError; + } + else if (rd == 0) break; + sink({(char *) buf.data(), (size_t) rd}); + } +} + + +////////////////////////////////////////////////////////////////////// + + +void Pipe::create() +{ + SECURITY_ATTRIBUTES saAttr = {0}; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.lpSecurityDescriptor = NULL; + saAttr.bInheritHandle = TRUE; + + HANDLE hReadPipe, hWritePipe; + if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0)) + throw WinError("CreatePipe"); + + readSide = hReadPipe; + writeSide = hWritePipe; +} + + +////////////////////////////////////////////////////////////////////// + +#if _WIN32_WINNT >= 0x0600 + +std::wstring handleToFileName(HANDLE handle) { + std::vector buf(0x100); + DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED); + if (dw == 0) { + if (handle == GetStdHandle(STD_INPUT_HANDLE )) return L""; + if (handle == GetStdHandle(STD_OUTPUT_HANDLE)) return L""; + if (handle == GetStdHandle(STD_ERROR_HANDLE )) return L""; + return (boost::wformat(L"") % handle).str(); + } + if (dw > buf.size()) { + buf.resize(dw); + if (GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED) != dw-1) + throw WinError("GetFinalPathNameByHandleW"); + dw -= 1; + } + return std::wstring(buf.data(), dw); +} + + +Path handleToPath(HANDLE handle) { + return os_string_to_string(handleToFileName(handle)); +} + +#endif + +} diff --git a/src/libutil/windows/file-path.cc b/src/libutil/windows/file-path.cc new file mode 100644 index 000000000000..d2f385f50d0c --- /dev/null +++ b/src/libutil/windows/file-path.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "file-path-impl.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + std::wstring_convert> converter; + return converter.to_bytes(PathNG::string_type { path }); +} + +PathNG::string_type string_to_os_string(std::string_view s) +{ + std::wstring_convert> converter; + return converter.from_bytes(std::string { s }); +} + +std::optional maybePathNG(PathView path) +{ + if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait::isPathSep(path[2])) { + PathNG::string_type sw = string_to_os_string( + std::string { "\\\\?\\" } + path); + std::replace(sw.begin(), sw.end(), '/', '\\'); + return sw; + } + if (path.length() >= 7 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\' && + ('A' <= path[4] && path[4] <= 'Z') && path[5] == ':' && WindowsPathTrait::isPathSep(path[6])) { + PathNG::string_type sw = string_to_os_string(path); + std::replace(sw.begin(), sw.end(), '/', '\\'); + return sw; + } + return std::optional(); +} + +PathNG pathNG(PathView path) +{ + std::optional sw = maybePathNG(path); + if (!sw) { + // FIXME why are we not using the regular error handling? + std::cerr << "invalid path for WinAPI call ["< + +namespace nix { + +std::string getUserName() +{ + // Get the required buffer size + DWORD size = 0; + if (!GetUserNameA(nullptr, &size)) { + auto lastError = GetLastError(); + if (lastError != ERROR_INSUFFICIENT_BUFFER) + throw WinError(lastError, "cannot figure out size of user name"); + } + + std::string name; + // Allocate a buffer of sufficient size + // + // - 1 because no need for null byte + name.resize(size - 1); + + // Retrieve the username + if (!GetUserNameA(&name[0], &size)) + throw WinError("cannot figure out user name"); + + return name; +} + +Path getHome() +{ + static Path homeDir = []() + { + Path homeDir = getEnv("USERPROFILE").value_or("C:\\Users\\Default"); + assert(!homeDir.empty()); + return canonPath(homeDir); + }(); + return homeDir; +} + +bool isRootUser() { + return false; +} + +} diff --git a/src/libutil/windows/windows-error.cc b/src/libutil/windows/windows-error.cc new file mode 100644 index 000000000000..26faaae6d21e --- /dev/null +++ b/src/libutil/windows/windows-error.cc @@ -0,0 +1,31 @@ +#include "windows-error.hh" + +#include +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string WinError::renderError(DWORD lastError) +{ + LPSTR errorText = NULL; + + FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM // use system message tables to retrieve error text + |FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer on local heap for error text + |FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters + NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM + lastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&errorText, // output + 0, // minimum size for output buffer + NULL); // arguments - see note + + if (NULL != errorText ) { + std::string s2 { errorText }; + LocalFree(errorText); + return s2; + } + return fmt("CODE=%d", lastError); +} + +} diff --git a/src/libutil/windows/windows-error.hh b/src/libutil/windows/windows-error.hh new file mode 100644 index 000000000000..fdfd0f52c58b --- /dev/null +++ b/src/libutil/windows/windows-error.hh @@ -0,0 +1,51 @@ +#pragma once +///@file + +#include + +#include "error.hh" + +namespace nix { + +/** + * Windows Error type. + * + * Unless you need to catch a specific error number, don't catch this in + * portable code. Catch `SystemError` instead. + */ +class WinError : public SystemError +{ +public: + DWORD lastError; + + /** + * Construct using the explicitly-provided error number. + * `FormatMessageA` will be used to try to add additional + * information to the message. + */ + template + WinError(DWORD lastError, const Args & ... args) + : SystemError(""), lastError(lastError) + { + auto hf = HintFmt(args...); + err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), renderError(lastError)); + } + + /** + * Construct using `GetLastError()` and the ambient "last error". + * + * Be sure to not perform another last-error-modifying operation + * before calling this constructor! + */ + template + WinError(const Args & ... args) + : WinError(GetLastError(), args ...) + { + } + +private: + + std::string renderError(DWORD lastError); +}; + +} diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 1cc33558f102..3f469787da0c 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1336,16 +1336,26 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs if (opArgs.size() != 0) throw UsageError("no arguments expected"); +#ifndef _WIN32 // TODO Implement path locks on Windows. PathLocks lock; lockProfile(lock, globals.profile); +#endif auto [gens, curGen] = findGenerations(globals.profile); RunPager pager; for (auto & i : gens) { +#ifdef _WIN32 // TODO portable wrapper in libutil + tm * tp = localtime(&i.creationTime); + if (!tp) + throw Error("cannot convert time"); + auto & t = *tp; +#else tm t; - if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time"); + if (!localtime_r(&i.creationTime, &t)) + throw Error("cannot convert time"); +#endif logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||", i.number, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index dd27344aa60c..7b8bc5af82b1 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -155,8 +155,10 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, auto store2 = state.store.dynamic_pointer_cast(); if (store2) { +#ifndef _WIN32 // TODO Implement path locks on Windows. PathLocks lock; lockProfile(lock, profile); +#endif Path lockTokenCur = optimisticLockProfile(profile); if (lockToken != lockTokenCur) { diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 86e6f008db7f..1e17282254ff 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -168,7 +168,7 @@ static int main_nix_instantiate(int argc, char * * argv) for (auto & i : files) { auto p = state->findFile(i); if (auto fn = p.getPhysicalPath()) - std::cout << fn->native() << std::endl; + std::cout << fn->string() << std::endl; else throw Error("'%s' has no physical path", p); } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 7c8905da6360..719675cba2ef 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -4,10 +4,8 @@ #include "globals.hh" #include "build-result.hh" #include "store-cast.hh" -#include "gc-store.hh" +#include "local-fs-store.hh" #include "log-store.hh" -#include "local-store.hh" -#include "monitor-fd.hh" #include "serve-protocol.hh" #include "serve-protocol-impl.hh" #include "shared.hh" @@ -15,7 +13,12 @@ #include "legacy.hh" #include "posix-source-accessor.hh" #include "path-with-outputs.hh" -#include "posix-fs-canonicalise.hh" + +#ifndef _WIN32 // TODO implement on Windows or provide allowed-to-noop interface +# include "local-store.hh" +# include "monitor-fd.hh" +# include "posix-fs-canonicalise.hh" +#endif #include #include @@ -43,12 +46,14 @@ static bool noOutput = false; static std::shared_ptr store; +#ifndef _WIN32 // TODO reenable on Windows once we have `LocalStore` there ref ensureLocalStore() { auto store2 = std::dynamic_pointer_cast(store); if (!store2) throw Error("you don't have sufficient rights to use this command"); return ref(store2); } +#endif static StorePath useDeriver(const StorePath & path) @@ -550,7 +555,11 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) if (!store->isValidPath(info->path) || reregister) { /* !!! races */ if (canonicalise) +#ifdef _WIN32 // TODO implement on Windows + throw UnimplementedError("file attribute canonicalisation Is not implemented on Windows"); +#else canonicalisePathMetaData(store->printStorePath(info->path), {}); +#endif if (!hashGiven) { HashResult hash = hashPath( *store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }, @@ -563,7 +572,9 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) } } +#ifndef _WIN32 // TODO reenable on Windows once we have `LocalStore` there ensureLocalStore()->registerValidPaths(infos); +#endif } @@ -684,7 +695,7 @@ static void opDump(Strings opFlags, Strings opArgs) if (!opFlags.empty()) throw UsageError("unknown flag"); if (opArgs.size() != 1) throw UsageError("only one argument allowed"); - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); std::string path = *opArgs.begin(); dumpPath(path, sink); sink.flush(); @@ -712,7 +723,7 @@ static void opExport(Strings opFlags, Strings opArgs) for (auto & i : opArgs) paths.insert(store->followLinksToStorePath(i)); - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); store->exportPaths(paths, sink); sink.flush(); } @@ -825,7 +836,7 @@ static void opServe(Strings opFlags, Strings opArgs) if (!opArgs.empty()) throw UsageError("no arguments expected"); FdSource in(STDIN_FILENO); - FdSink out(STDOUT_FILENO); + FdSink out(getStandardOut()); /* Exchange the greeting. */ ServeProto::Version clientVersion = @@ -946,7 +957,9 @@ static void opServe(Strings opFlags, Strings opArgs) getBuildSettings(); try { +#ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); +#endif store->buildPaths(toDerivedPaths(paths)); out << 0; } catch (Error & e) { @@ -966,7 +979,9 @@ static void opServe(Strings opFlags, Strings opArgs) getBuildSettings(); +#ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); +#endif auto status = store->buildDerivation(drvPath, drv); ServeProto::write(*store, wconn, status); diff --git a/src/nix/cat.cc b/src/nix/cat.cc index 4df086d4fbac..ee904b0c5ef9 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -15,7 +15,8 @@ struct MixCat : virtual Args if (st.type != SourceAccessor::Type::tRegular) throw Error("path '%1%' is not a regular file", path); stopProgressBar(); - writeFull(STDOUT_FILENO, accessor->readFile(CanonPath(path))); + + writeFull(getStandardOut(), accessor->readFile(CanonPath(path))); } }; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index bb96f7786611..06ee200a630e 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -7,7 +7,10 @@ #include "outputs-spec.hh" #include "derivations.hh" #include "progress-bar.hh" -#include "run.hh" + +#ifndef _WIN32 // TODO re-enable on Windows +# include "run.hh" +#endif #include #include @@ -650,6 +653,9 @@ struct CmdDevelop : Common, MixEnvironment // This is to make sure the system shell doesn't leak into the build environment. setEnv("SHELL", shell.c_str()); +#ifdef _WIN32 // TODO re-enable on Windows + throw UnimplementedError("Cannot yet spawn processes on Windows"); +#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} @@ -670,6 +676,7 @@ struct CmdDevelop : Common, MixEnvironment } runProgramInStore(store, UseSearchPath::Use, shell, args, buildEnvironment.getSystem()); +#endif } }; diff --git a/src/nix/dump-path.cc b/src/nix/dump-path.cc index 0850d4c1cdb5..953d77d3194a 100644 --- a/src/nix/dump-path.cc +++ b/src/nix/dump-path.cc @@ -20,7 +20,7 @@ struct CmdDumpPath : StorePathCommand void run(ref store, const StorePath & storePath) override { - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); store->narFromPath(storePath, sink); sink.flush(); } @@ -55,7 +55,7 @@ struct CmdDumpPath2 : Command void run() override { - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); dumpPath(path, sink); sink.flush(); } diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 088be3b17827..f346ab6e0ed4 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -87,7 +87,11 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption // FIXME: disallow strings with contexts? writeFile(path, v.string_view()); else if (v.type() == nAttrs) { - if (mkdir(path.c_str(), 0777) == -1) + if (mkdir(path.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0777 +#endif + ) == -1) throw SysError("creating directory '%s'", path); for (auto & attr : *v.attrs) { std::string_view name = state->symbols[attr.name]; @@ -112,7 +116,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption else if (raw) { stopProgressBar(); - writeFull(STDOUT_FILENO, *state->coerceToString(noPos, *v, context, "while generating the eval command output")); + writeFull(getStandardOut(), *state->coerceToString(noPos, *v, context, "while generating the eval command output")); } else if (json) { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a846f6371149..d80fd770e803 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -913,11 +913,13 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand copyDir(templateDir, flakeDir); +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. if (!changedFiles.empty() && pathExists(flakeDir + "/.git")) { Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" }; for (auto & s : changedFiles) args.push_back(s); runProgram("git", true, args); } +#endif auto welcomeText = cursor->maybeGetAttr("welcomeText"); if (welcomeText) { notice("\n"); diff --git a/src/nix/local.mk b/src/nix/local.mk index 9f6f31b3a39a..305b0e9dfffc 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -4,19 +4,19 @@ nix_DIR := $(d) nix_SOURCES := \ $(wildcard $(d)/*.cc) \ - $(wildcard src/build-remote/*.cc) \ $(wildcard src/nix-build/*.cc) \ - $(wildcard src/nix-channel/*.cc) \ - $(wildcard src/nix-collect-garbage/*.cc) \ - $(wildcard src/nix-copy-closure/*.cc) \ - $(wildcard src/nix-daemon/*.cc) \ $(wildcard src/nix-env/*.cc) \ $(wildcard src/nix-instantiate/*.cc) \ $(wildcard src/nix-store/*.cc) ifdef HOST_UNIX nix_SOURCES += \ - $(wildcard $(d)/unix/*.cc) + $(wildcard $(d)/unix/*.cc) \ + $(wildcard src/build-remote/*.cc) \ + $(wildcard src/nix-channel/*.cc) \ + $(wildcard src/nix-collect-garbage/*.cc) \ + $(wildcard src/nix-copy-closure/*.cc) \ + $(wildcard src/nix-daemon/*.cc) endif INCLUDE_nix := -I $(d) diff --git a/src/nix/log.cc b/src/nix/log.cc index 9a9bd30f9619..7f590c708f6f 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -57,7 +57,7 @@ struct CmdLog : InstallableCommand if (!log) continue; stopProgressBar(); printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri()); - writeFull(STDOUT_FILENO, *log); + writeFull(getStandardOut(), *log); return; } diff --git a/src/nix/main.cc b/src/nix/main.cc index 25f81e48b2d4..c782a3b6d771 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -16,23 +16,28 @@ #include "markdown.hh" #include "memory-input-accessor.hh" #include "terminal.hh" +#include "users.hh" #include -#include -#include -#include -#include #include - #include +#ifndef _WIN32 +# include +# include +# include +# include +#endif + #if __linux__ # include "namespaces.hh" #endif +#ifndef _WIN32 extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); +#endif namespace nix { @@ -57,6 +62,7 @@ static bool haveProxyEnvironmentVariables() /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { +#ifndef _WIN32 struct ifaddrs * addrs; if (getifaddrs(&addrs)) @@ -80,6 +86,10 @@ static bool haveInternet() if (haveProxyEnvironmentVariables()) return true; return false; +#else + // TODO implement on Windows + return true; +#endif } std::string programPath; @@ -342,10 +352,12 @@ void mainWrapped(int argc, char * * argv) /* The chroot helper needs to be run before any threads have been started. */ +#ifndef _WIN32 if (argc > 0 && argv[0] == chrootHelperName) { chrootHelper(argc, argv); return; } +#endif initNix(); initGC(); @@ -364,6 +376,9 @@ void mainWrapped(int argc, char * * argv) programPath = argv[0]; auto programName = std::string(baseNameOf(programPath)); + auto extensionPos = programName.find_last_of("."); + if (extensionPos != std::string::npos) + programName.erase(extensionPos); if (argc > 1 && std::string_view(argv[1]) == "__build-remote") { programName = "build-remote"; @@ -525,9 +540,11 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { +#ifndef _WIN32 // TODO implement on Windows // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); +#endif return nix::handleExceptions(argv[0], [&]() { nix::mainWrapped(argc, argv); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index b64e6d899ccd..01b06d0a76bf 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -95,7 +95,7 @@ std::tuple prefetchFile( if (executable) mode = 0700; - AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode); + AutoCloseFD fd = toDescriptor(open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); if (!fd) throw SysError("creating temporary file '%s'", tmpFile); FdSink sink(fd.get()); diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index dfef44869315..1e277cbbee76 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -177,7 +177,7 @@ struct CmdKeyGenerateSecret : Command throw UsageError("required argument '--key-name' is missing"); stopProgressBar(); - writeFull(STDOUT_FILENO, SecretKey::generate(*keyName).to_string()); + writeFull(getStandardOut(), SecretKey::generate(*keyName).to_string()); } }; @@ -199,7 +199,7 @@ struct CmdKeyConvertSecretToPublic : Command { SecretKey secretKey(drainFD(STDIN_FILENO)); stopProgressBar(); - writeFull(STDOUT_FILENO, secretKey.toPublicKey().to_string()); + writeFull(getStandardOut(), secretKey.toPublicKey().to_string()); } }; diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index d7e9edf0ae45..89f0819cef52 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -1,9 +1,12 @@ #include "util.hh" #include "types.hh" #include "file-system.hh" -#include "processes.hh" #include "terminal.hh" +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. +# include "processes.hh" +#endif + #include #include @@ -421,6 +424,7 @@ namespace nix { ASSERT_EQ(string2Int("-100"), -100); } +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes /* ---------------------------------------------------------------------------- * statusOk * --------------------------------------------------------------------------*/ @@ -429,6 +433,7 @@ namespace nix { ASSERT_EQ(statusOk(0), true); ASSERT_EQ(statusOk(1), false); } +#endif /* ----------------------------------------------------------------------------