diff --git a/Makefile b/Makefile index c3dc83c77221..fec24b3f4f3d 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 \ @@ -17,7 +19,10 @@ makefiles = \ src/libmain/local.mk \ src/libexpr/local.mk \ src/libcmd/local.mk \ - src/nix/local.mk \ + src/nix/local.mk + +ifdef HOST_UNIX +makefiles += \ src/resolve-system-dependencies/local.mk \ scripts/local.mk \ misc/bash/local.mk \ @@ -27,6 +32,7 @@ makefiles = \ misc/launchd/local.mk \ misc/upstart/local.mk endif +endif ifeq ($(ENABLE_UNIT_TESTS), yes) makefiles += \ @@ -39,6 +45,7 @@ makefiles += \ endif ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) +ifdef HOST_UNIX makefiles += \ tests/functional/local.mk \ tests/functional/ca/local.mk \ @@ -47,6 +54,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 = @@ -71,8 +79,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/command.cc b/src/libcmd/command.cc index 369fa600441c..59afdec0f09c 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -328,8 +328,10 @@ void MixEnvironment::setEnviron() { if (!keep.empty()) throw UsageError("--keep does not make sense without --ignore-environment"); +#ifndef _WIN32 for (const auto & var : unset) unsetenv(var.c_str()); +#endif } } 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..c74658b0a6e2 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -3,8 +3,8 @@ #include "finally.hh" #include "terminal.hh" -#include #if HAVE_LOWDOWN +#include #include #endif diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 3e34ecdb6ad1..a0dbfee581ed 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 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 restoreSignals(); +#endif if (g_signal_received) { g_signal_received = 0; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 7cecc60b77cc..b17d670a9f1b 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 runProgram2(RunOptions { .program = settings.nixBinDir+ "/" + program, .args = args, .environment = subprocessEnv, .input = input, }); +#endif return; } @@ -228,8 +230,10 @@ ReplExitStatus NixRepl::mainLoop() printMsg(lvlError, e.msg()); } catch (Error & e) { printMsg(lvlError, e.msg()); +#ifndef _WIN32 } catch (Interrupted & e) { printMsg(lvlError, e.msg()); +#endif } // We handled the current input fully, so we should clear it @@ -357,7 +361,9 @@ ProcessLineResult NixRepl::processLine(std::string line) if (line.empty()) return ProcessLineResult::PromptAgain; +#ifndef _WIN32 _isInterrupted = false; +#endif std::string command, arg; @@ -480,6 +486,7 @@ ProcessLineResult NixRepl::processLine(std::string line) reloadFiles(); } +#ifndef _WIN32 else if (command == ":e" || command == ":edit") { Value v; evalString(arg, v); @@ -514,6 +521,7 @@ ProcessLineResult NixRepl::processLine(std::string line) state->resetFileCache(); reloadFiles(); } +#endif else if (command == ":t") { Value v; diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 2ccbe327fad1..52baff26eae9 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -65,8 +65,10 @@ Strings EvalSettings::getDefaultNixPath() if (!evalSettings.restrictEval && !evalSettings.pureEval) { add(getNixDefExpr() + "/channels"); + #ifndef _WIN32 add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); add(rootChannelsDir()); + #endif } return res; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 86251adf7e57..565ac1dd406c 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 +# include +#endif + #if HAVE_BOEHMGC #define GC_INCLUDE_NEW @@ -2633,9 +2635,11 @@ void EvalState::maybePrintStats() void EvalState::printStatistics() { +#ifndef _WIN32 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 *); @@ -2652,7 +2656,9 @@ void EvalState::printStatistics() if (outPath != "-") fs.open(outPath, std::fstream::out); json topObj = json::object(); +#ifndef _WIN32 topObj["cpuTime"] = cpuTime; +#endif topObj["envs"] = { {"number", nrEnvs}, {"elements", nrValuesInEnvs}, diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f459712905a8..d4401fae5656 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -160,6 +160,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 7292718284fe..6ee17633f96e 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 + /* 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) { @@ -4576,6 +4583,7 @@ void EvalState::createBaseEnv() )", }); +#ifndef _WIN32 // Miscellaneous if (evalSettings.enableNativeCode) { addPrimOp({ @@ -4589,6 +4597,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 b723554cc369..389ef9bdf5fc 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 +# 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; @@ -348,7 +350,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto act = (Activity *) payload; act->result(resFetchStatus, trim(std::string_view(str, len))); + #ifndef _WIN32 return _isInterrupted ? -1 : 0; + #else + return 0; + #endif } static int transferProgressCallback(const git_indexer_progress * stats, void * payload) @@ -361,7 +367,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this stats->indexed_deltas, stats->total_deltas, stats->received_bytes / (1024.0 * 1024.0))); + #ifndef _WIN32 return _isInterrupted ? -1 : 0; + #else + return 0; + #endif } void fetch( @@ -377,12 +387,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 runProgram(RunOptions { .program = "git", .searchPath = true, @@ -392,6 +403,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 +434,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this } writeFile(allowedSignersFile, allowedSigners); +#ifndef _WIN32 // Run verification command auto [status, output] = runProgram(RunOptions { .program = "git", @@ -450,6 +465,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/libfetchers/git.cc b/src/libfetchers/unix/git.cc similarity index 100% rename from src/libfetchers/git.cc rename to src/libfetchers/unix/git.cc diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/unix/mercurial.cc similarity index 100% rename from src/libfetchers/mercurial.cc rename to src/libfetchers/unix/mercurial.cc diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index ce45eae2ba11..7d8688785beb 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -408,7 +408,11 @@ class ProgressBar : public Logger } } +#ifndef _WIN32 auto width = getWindowSize().second; +#else + auto width = 0; +#endif if (width <= 0) width = std::numeric_limits::max(); writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 7bced0aa429a..626c3db69d58 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 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 + } @@ -308,7 +317,9 @@ void printVersion(const std::string & programName) void showManPage(const std::string & name) { restoreProcessContext(); +#ifndef _WIN32 setenv("MANPATH", settings.nixManDir.c_str(), 1); +#endif execlp("man", "man", name.c_str(), nullptr); throw SysError("command 'man %1%' failed", name.c_str()); } @@ -329,7 +340,9 @@ int handleExceptions(const std::string & programName, std::function fun) condition is discharged before we reach printMsg() below, since otherwise it will throw an (uncaught) exception. */ +#ifndef _WIN32 setInterruptThrown(); +#endif throw; } } catch (Exit & e) { @@ -362,6 +375,7 @@ RunPager::RunPager() stopProgressBar(); +#ifndef _WIN32 Pipe toPager; toPager.create(); @@ -383,11 +397,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 try { if (pid != -1) { std::cout.flush(); @@ -397,6 +413,7 @@ RunPager::~RunPager() } catch (...) { ignoreException(); } +#endif } diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 99c3dffab300..70d81fe27a58 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -1,7 +1,9 @@ #pragma once ///@file +#ifndef _WIN32 #include "processes.hh" +#endif #include "args.hh" #include "args/root.hh" #include "common-args.hh" @@ -89,8 +91,10 @@ public: ~RunPager(); private: +#ifndef _WIN32 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/stack.cc index 10f71c1dcadd..9369a9a50cc8 100644 --- a/src/libmain/stack.cc +++ b/src/libmain/stack.cc @@ -11,6 +11,7 @@ namespace nix { +#ifndef _WIN32 static void sigsegvHandler(int signo, siginfo_t * info, void * ctx) { /* Detect stack overflows by comparing the faulting address with @@ -73,5 +74,6 @@ void defaultStackOverflowHandler(siginfo_t * info, void * ctx) { [[gnu::unused]] auto res = write(2, msg, strlen(msg)); _exit(1); // maybe abort instead? } +#endif } diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index bea2bb370d1b..bff372609bd0 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -126,7 +126,11 @@ void BinaryCacheStore::writeNarInfo(ref narInfo) AutoCloseFD openFile(const Path & path) { - auto fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + auto fd = toDesc(open(path.c_str(), O_RDONLY + #ifndef _WIN32 + | O_CLOEXEC + #endif + )); if (!fd) throw SysError("opening file '%1%'", path); return fd; diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 31a6b32f19c7..a5397ced933b 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 + , 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 2c808015dd5b..5a8ad31a2179 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,5 +1,4 @@ #include "daemon.hh" -#include "monitor-fd.hh" #include "worker-protocol.hh" #include "worker-protocol-impl.hh" #include "build-result.hh" @@ -15,6 +14,10 @@ #include "args.hh" #include "git.hh" +#ifndef _WIN32 +# include "monitor-fd.hh" +#endif + namespace nix::daemon { Sink & operator << (Sink & sink, const Logger::Fields & fields) @@ -1017,7 +1020,9 @@ void processConnection( TrustedFlag trusted, RecursiveFlag recursive) { +#ifndef _WIN32 auto monitor = !recursive ? std::make_unique(from.fd) : nullptr; +#endif /* Exchange the greeting. */ unsigned int magic = readInt(from); @@ -1038,7 +1043,9 @@ void processConnection( unsigned int opCount = 0; Finally finally([&]() { +#ifndef _WIN32 _isInterrupted = false; +#endif printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); @@ -1079,8 +1086,10 @@ void processConnection( WorkerProto::Op op; try { op = (enum WorkerProto::Op) readInt(from); +#ifndef _WIN32 } catch (Interrupted & e) { break; +#endif } catch (EndOfFile & e) { break; } diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index bab21bf517bb..aacfa3453bfb 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -1,5 +1,4 @@ #include "filetransfer.hh" -#include "namespaces.hh" #include "globals.hh" #include "store-api.hh" #include "s3.hh" @@ -12,6 +11,10 @@ #include #endif +#if __linux__ +# include "namespaces.hh" +#endif + #include #include @@ -254,12 +257,17 @@ struct curlFileTransfer : public FileTransfer int progressCallback(double dltotal, double dlnow) { + #ifndef _WIN32 try { - act.progress(dlnow, dltotal); + act.progress(dlnow, dltotal); } catch (nix::Interrupted &) { - assert(_isInterrupted); + assert(_isInterrupted); } return _isInterrupted; + #else + act.progress(dlnow, dltotal); + return false; + #endif } static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow) @@ -463,7 +471,11 @@ struct curlFileTransfer : public FileTransfer if (errorSink) response = std::move(errorSink->s); auto exc = + #ifndef _WIN32 code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted + #else + false + #endif ? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri) : httpStatus != 0 ? FileTransferError(err, @@ -513,10 +525,12 @@ struct curlFileTransfer : public FileTransfer Sync state_; + #ifndef _WIN32 /* 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; @@ -536,8 +550,10 @@ struct curlFileTransfer : public FileTransfer fileTransferSettings.httpConnections.get()); #endif + #ifndef _WIN32 wakeupPipe.create(); fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK); + #endif workerThread = std::thread([&]() { workerThreadEntry(); }); } @@ -558,17 +574,23 @@ struct curlFileTransfer : public FileTransfer auto state(state_.lock()); state->quit = true; } + #ifndef _WIN32 writeFull(wakeupPipe.writeSide.get(), " ", false); + #endif } void workerThreadMain() { /* Cause this thread to be notified on SIGINT. */ + #ifndef _WIN32 auto callback = createInterruptCallback([&]() { stopWorkerThread(); }); + #endif + #if __linux__ unshareFilesystem(); + #endif std::map> items; @@ -602,9 +624,11 @@ struct curlFileTransfer : public FileTransfer /* Wait for activity, including wakeup events. */ int numfds = 0; struct curl_waitfd extraFDs[1]; + #ifndef _WIN32 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() @@ -663,7 +687,9 @@ struct curlFileTransfer : public FileTransfer { try { workerThreadMain(); + #ifndef _WIN32 } catch (nix::Interrupted & e) { + #endif } catch (std::exception & e) { printError("unexpected error in download thread: %s", e.what()); } @@ -688,7 +714,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 writeFull(wakeupPipe.writeSide.get(), " "); + #endif } #if ENABLE_S3 diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index b8500fef14aa..3d569c761a54 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,10 +356,14 @@ 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 void *handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); + #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 b8958898de17..5fbe70599a24 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..e3d41105a3b6 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,7 +1,9 @@ #include "derivations.hh" #include "parsed-derivations.hh" #include "globals.hh" -#include "local-store.hh" +#ifndef _WIN32 +# include "local-store.hh" +#endif #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..ec2b892d8a7c 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 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 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 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 PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, curGen] = findGenerations(profile); @@ -266,8 +274,10 @@ void switchGeneration( std::optional dstGen, bool dryRun) { +#ifndef _WIN32 PathLocks lock; lockProfile(lock, profile); +#endif auto [gens, curGen] = findGenerations(profile); @@ -292,11 +302,13 @@ void switchGeneration( } +#ifndef _WIN32 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..770adae298b7 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -6,8 +6,12 @@ */ #include "types.hh" -#include "pathlocks.hh" +#ifndef _WIN32 +# include "pathlocks.hh" +#endif + +#include #include @@ -187,11 +191,13 @@ void switchGeneration( std::optional dstGen, bool dryRun); +#ifndef _WIN32 /** * 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..f1dcb46e156f 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 = toDesc(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(fromDesc(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/sqlite.cc b/src/libstore/sqlite.cc index 06abfb90bb52..5f9a3e4530a7 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -8,6 +8,10 @@ #include +#ifdef _WIN32 +# include +#endif + namespace nix { SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, HintFmt && hf) @@ -256,10 +260,14 @@ void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning) /* Sleep for a while since retrying the transaction right away is likely to fail again. */ checkInterrupt(); + #ifndef _WIN32 struct timespec t; t.tv_sec = 0; t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */ nanosleep(&t, 0); + #else + Sleep(rand() % 100); + #endif } } diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 30fe73adb09f..295e31039532 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 + 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 + 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 + 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..ee348a652a7d 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 +# include "processes.hh" +#endif + namespace nix { class SSHMaster @@ -21,7 +24,9 @@ private: struct State { +#ifndef _WIN32 Pid sshMaster; +#endif std::unique_ptr tmpDir; Path socketPath; }; @@ -31,13 +36,19 @@ private: void addCommonSSHOpts(Strings & args); bool isMasterRunning(); +#ifndef _WIN32 + 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 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..c5cf7ab6ff75 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 static bool isNonUriPath(const std::string & spec) { return @@ -1295,9 +1300,11 @@ 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) { + #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 +1349,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 99% rename from src/libstore/build/local-derivation-goal.cc rename to src/libstore/unix/build/local-derivation-goal.cc index 914fffd160e5..f8794e783d5f 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -17,7 +17,6 @@ #include "cgroup.hh" #include "personality.hh" #include "current-process.hh" -#include "namespaces.hh" #include "child.hh" #include "unix-domain-socket.hh" #include "posix-fs-canonicalise.hh" @@ -40,18 +39,19 @@ /* Includes required for chroot support. */ #if __linux__ -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_SECCOMP -#include -#endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) +# include +# include +# include +# include +# include +# include +# include +# include +# include "namespaces.hh" +# if HAVE_SECCOMP +# include +# endif +# define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif #if __APPLE__ 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..32259e0d51d2 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 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 f80f43ef08a1..51d3eb6509cb 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -2,13 +2,15 @@ #include #include "current-process.hh" -#include "namespaces.hh" #include "util.hh" #include "finally.hh" #include "file-system.hh" -#include "processes.hh" #include "signals.hh" +#ifndef _WIN32 +# include "processes.hh" +#endif + #ifdef __APPLE__ # include #endif @@ -17,9 +19,12 @@ # include # include # include "cgroup.hh" +# 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,14 +85,20 @@ void setStackSize(rlim_t stackSize) } } } +#endif void restoreProcessContext(bool restoreMounts) { + #ifndef _WIN32 restoreSignals(); + #endif if (restoreMounts) { + #if __linux__ restoreMountNamespace(); + #endif } + #ifndef _WIN32 if (savedStackSize) { struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0) { @@ -94,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..90e53a858eb6 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,10 +17,12 @@ namespace nix { */ unsigned int getMaxCPU(); +#ifndef _WIN32 /** * Change the stack size. */ void setStackSize(rlim_t stackSize); +#endif /** * Restore the original inherited Unix process context (such as signal diff --git a/src/libutil/error.hh b/src/libutil/error.hh index d23625a54f1d..4cbb1fedfa73 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -243,6 +243,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..cf3569c7d551 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,12 @@ 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 + assert(block); + drainFD(fd, sink); +#else drainFD(fd, sink, block); +#endif return std::move(sink.s); } @@ -68,9 +78,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 +96,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 8daeab66e897..04e93af93537 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 toDesc(int fd) { +#ifdef _WIN32 + return (HANDLE) _get_osfhandle(fd); +#else return fd; +#endif } /** @@ -33,7 +54,11 @@ static inline Descriptor toDesc(int fd) */ static inline int fromDesc(Descriptor fd, int flags) { +#ifdef _WIN32 + return _open_osfhandle((intptr_t) fd, flags); +#else return fd; +#endif } /** @@ -67,12 +92,18 @@ std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0 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 } /** @@ -104,6 +135,8 @@ public: void close(); }; +#ifndef _WIN32 + /** * Close all file descriptors except those listed in the given set. * Good practice in child processes. @@ -115,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..a8741be9a677 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 + 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 = toDesc(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 = toDesc(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 = toDesc(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 = toDesc(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 = toDesc(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,9 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) if (errno == ENOENT) return; throw SysError("cannot unlink '%1%'", path); } +#else + throw UnimplementedError("_deletePath"); +#endif } static void _deletePath(const Path & path, uint64_t & bytesFreed) @@ -413,7 +452,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed) if (dir == "") dir = "/"; - AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)}; + AutoCloseFD dirfd = toDesc(open(dir.c_str(), O_RDONLY)); if (!dirfd) { if (errno == ENOENT) return; throw SysError("opening directory '%1%'", path); @@ -436,11 +475,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 + , 0777 +#endif + ) == -1 && errno != EEXIST) throw SysError("creating directory '%1%'", path); - st = lstat(path); + st = STAT(path); created.push_back(path); } @@ -526,7 +569,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 + , 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 +599,23 @@ 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 = toDesc(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 + throw UnimplementedError("createSymlink"); +#endif } void replaceSymlink(const Path & target, const Path & link) @@ -583,7 +636,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 +651,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 +678,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 +707,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 dd071e1af794..b774f71c57c4 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..93ff920e2600 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 + 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,26 @@ 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..627a7f007f2a 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 = toDesc(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(fromDesc(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(fromDesc(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..597af758a414 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 { @@ -89,6 +94,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w ////////////////////////////////////////////////////////////////////// +#ifndef _WIN32 static Sync> windowSize{{0, 0}}; @@ -107,5 +113,6 @@ std::pair getWindowSize() { return *windowSize.lock(); } +#endif } diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9d8d0c743286..f84303bdce74 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -21,6 +21,8 @@ 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. @@ -35,4 +37,6 @@ void updateWindowSize(); */ std::pair getWindowSize(); +#endif + } diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 9a7dfee568fa..b585e6b4fc85 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 if (!mainThread) interruptCheck = [&]() { return (bool) quit; }; +#endif bool didWork = false; std::exception_ptr exc; @@ -109,7 +111,10 @@ void ThreadPool::doWork(bool mainThread) try { std::rethrow_exception(exc); } catch (std::exception & e) { - if (!dynamic_cast(&e) && + if (true && +#ifndef _WIN32 + !dynamic_cast(&e) && +#endif !dynamic_cast(&e)) ignoreException(); } catch (...) { 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/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-build/nix-build.cc b/src/nix-build/nix-build.cc index 8276be8e8bc2..0c20be57b63f 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -287,8 +287,10 @@ static void main_nix_build(int argc, char * * argv) left = {"default.nix"}; } +#ifndef _WIN32 if (runEnv) setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1); +#endif PackageInfos drvs; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 177344044a6d..77e77c381ff6 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 PathLocks lock; lockProfile(lock, globals.profile); +#endif auto [gens, curGen] = findGenerations(globals.profile); RunPager pager; for (auto & i : gens) { +#ifdef _WIN32 + 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..caf3450fbbd8 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 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..ba766fd8b367 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 +# 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 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 + 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 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 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 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/daemon.cc b/src/nix/daemon.cc index 8afcbe98282e..e58486dcfeb6 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -1,5 +1,7 @@ ///@file +#ifndef _WIN32 + #include "signals.hh" #include "unix-domain-socket.hh" #include "command.hh" @@ -586,3 +588,5 @@ struct CmdDaemon : StoreCommand }; static auto rCmdDaemon = registerCommand2({"daemon"}); + +#endif diff --git a/src/nix/develop.cc b/src/nix/develop.cc index c1842f2d57aa..d8af2ae73708 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -287,7 +287,10 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore throw Error("get-env.sh failed to produce an environment"); } -struct Common : InstallableCommand, MixProfile +struct Common : InstallableCommand +#ifndef _WIN32 +, MixProfile +#endif { std::set ignoreVars{ "BASHOPTS", @@ -473,7 +476,9 @@ struct Common : InstallableCommand, MixProfile auto strPath = store->printStorePath(shellOutPath); +#ifndef _WIN32 updateProfile(shellOutPath); +#endif debug("reading environment file '%s'", strPath); @@ -603,7 +608,9 @@ struct CmdDevelop : Common, MixEnvironment setEnviron(); // prevent garbage collection until shell exits +#ifndef _WIN32 setenv("NIX_GCROOT", gcroot.c_str(), 1); +#endif Path shell = "bash"; @@ -648,6 +655,7 @@ struct CmdDevelop : Common, MixEnvironment // Override SHELL with the one chosen for this environment. // This is to make sure the system shell doesn't leak into the build environment. +#ifndef _WIN32 setenv("SHELL", shell.c_str(), 1); // If running a phase or single command, don't want an interactive shell running after @@ -670,6 +678,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..cf4cd0e8ec62 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 + , 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..64b5fdafed73 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 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/fmt.cc b/src/nix/fmt.cc index 059904150f30..9cd073503cec 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -1,3 +1,5 @@ +#ifndef _WIN32 + #include "command.hh" #include "installable-value.hh" #include "run.hh" @@ -54,3 +56,5 @@ struct CmdFmt : SourceExprCommand { }; static auto r2 = registerCommand("fmt"); + +#endif diff --git a/src/nix/local.mk b/src/nix/local.mk index 55544b5643b2..e94c93bd58be 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -4,15 +4,19 @@ nix_DIR := $(d) nix_SOURCES := \ $(wildcard $(d)/*.cc) \ - $(wildcard src/build-remote/*.cc) \ $(wildcard src/nix-build/*.cc) \ + $(wildcard src/nix-env/*.cc) \ + $(wildcard src/nix-instantiate/*.cc) \ + $(wildcard src/nix-store/*.cc) + +ifdef HOST_UNIX +nix_SOURCES += \ + $(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) \ - $(wildcard src/nix-env/*.cc) \ - $(wildcard src/nix-instantiate/*.cc) \ - $(wildcard src/nix-store/*.cc) \ + $(wildcard src/nix-daemon/*.cc) +endif nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain -I src/libcmd -I doc/manual 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 36256f3d0c6d..67fd250abd05 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -2,7 +2,6 @@ #include "args/root.hh" #include "current-process.hh" -#include "namespaces.hh" #include "command.hh" #include "common-args.hh" #include "eval.hh" @@ -17,19 +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 { @@ -54,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)) @@ -77,6 +86,9 @@ static bool haveInternet() if (haveProxyEnvironmentVariables()) return true; return false; +#else + return true; +#endif } std::string programPath; @@ -339,10 +351,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(); @@ -361,6 +375,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"; @@ -522,9 +539,11 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { +#ifndef _WIN32 // 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..857e0f8f6516 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 = toDesc(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/run.cc b/src/nix/run.cc index e868376790fb..0d99731da021 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -1,3 +1,5 @@ +#ifndef _WIN32 + #include "current-process.hh" #include "run.hh" #include "command-installable-value.hh" @@ -287,3 +289,5 @@ void chrootHelper(int argc, char * * argv) throw Error("mounting the Nix store on '%s' is not supported on this platform", storeDir); #endif } + +#endif \ No newline at end of file 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/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 4c7a74e16f19..68e1e633324f 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -1,3 +1,5 @@ +#ifndef _WIN32 + #include "processes.hh" #include "command.hh" #include "common-args.hh" @@ -158,3 +160,5 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand }; static auto rCmdUpgradeNix = registerCommand("upgrade-nix"); + +#endif diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index b1426edaeee6..ecf2e4947cdf 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -90,11 +90,13 @@ namespace nix { ASSERT_THAT(*p->value, IsIntEq(123)); } + #ifndef _WIN32 TEST_F(PrimOpTest, getEnv) { setenv("_NIX_UNIT_TEST_ENV_VALUE", "test value", 1); auto v = eval("builtins.getEnv \"_NIX_UNIT_TEST_ENV_VALUE\""); ASSERT_THAT(v, IsStringEq("test value")); } + #endif TEST_F(PrimOpTest, seq) { ASSERT_THROW(eval("let x = throw \"test\"; in builtins.seq x { }"), ThrownError); diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index d7e9edf0ae45..d14a08878ef5 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 +# include "processes.hh" +#endif + #include #include @@ -421,6 +424,7 @@ namespace nix { ASSERT_EQ(string2Int("-100"), -100); } +#ifndef _WIN32 /* ---------------------------------------------------------------------------- * statusOk * --------------------------------------------------------------------------*/ @@ -429,6 +433,7 @@ namespace nix { ASSERT_EQ(statusOk(0), true); ASSERT_EQ(statusOk(1), false); } +#endif /* ----------------------------------------------------------------------------