mirror of
https://github.com/NixOS/nix
synced 2025-07-04 23:51:47 +02:00
Build a minimized Nix with MinGW
At this point many features are stripped out, but this works: - Can run libnix{util,store,expr} unit tests - Can run some Nix commands Co-Authored-By volth <volth@volth.com> Co-Authored-By Brian McKenna <brian@brianmckenna.org>
This commit is contained in:
parent
2248a3f545
commit
8433027e35
111 changed files with 1162 additions and 140 deletions
17
src/libutil/windows/environment-variables.cc
Normal file
17
src/libutil/windows/environment-variables.cc
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "environment-variables.hh"
|
||||
|
||||
#include "processenv.h"
|
||||
|
||||
namespace nix {
|
||||
|
||||
int unsetenv(const char *name)
|
||||
{
|
||||
return -SetEnvironmentVariableA(name, nullptr);
|
||||
}
|
||||
|
||||
int setEnv(const char * name, const char * value)
|
||||
{
|
||||
return -SetEnvironmentVariableA(name, value);
|
||||
}
|
||||
|
||||
}
|
148
src/libutil/windows/file-descriptor.cc
Normal file
148
src/libutil/windows/file-descriptor.cc
Normal file
|
@ -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 <fileapi.h>
|
||||
#include <error.h>
|
||||
#include <namedpipeapi.h>
|
||||
#include <namedpipeapi.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
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<unsigned char> 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<wchar_t> buf(0x100);
|
||||
DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED);
|
||||
if (dw == 0) {
|
||||
if (handle == GetStdHandle(STD_INPUT_HANDLE )) return L"<stdin>";
|
||||
if (handle == GetStdHandle(STD_OUTPUT_HANDLE)) return L"<stdout>";
|
||||
if (handle == GetStdHandle(STD_ERROR_HANDLE )) return L"<stderr>";
|
||||
return (boost::wformat(L"<unnnamed handle %X>") % 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
|
||||
|
||||
}
|
52
src/libutil/windows/file-path.cc
Normal file
52
src/libutil/windows/file-path.cc
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
|
||||
#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<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.to_bytes(PathNG::string_type { path });
|
||||
}
|
||||
|
||||
PathNG::string_type string_to_os_string(std::string_view s)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(std::string { s });
|
||||
}
|
||||
|
||||
std::optional<PathNG> maybePathNG(PathView path)
|
||||
{
|
||||
if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait<char>::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<char>::isPathSep(path[6])) {
|
||||
PathNG::string_type sw = string_to_os_string(path);
|
||||
std::replace(sw.begin(), sw.end(), '/', '\\');
|
||||
return sw;
|
||||
}
|
||||
return std::optional<PathNG::string_type>();
|
||||
}
|
||||
|
||||
PathNG pathNG(PathView path)
|
||||
{
|
||||
std::optional<PathNG::string_type> sw = maybePathNG(path);
|
||||
if (!sw) {
|
||||
// FIXME why are we not using the regular error handling?
|
||||
std::cerr << "invalid path for WinAPI call ["<<path<<"]"<<std::endl;
|
||||
_exit(111);
|
||||
}
|
||||
return *sw;
|
||||
}
|
||||
|
||||
}
|
48
src/libutil/windows/processes.cc
Normal file
48
src/libutil/windows/processes.cc
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "current-process.hh"
|
||||
#include "environment-variables.hh"
|
||||
#include "signals.hh"
|
||||
#include "processes.hh"
|
||||
#include "finally.hh"
|
||||
#include "serialise.hh"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/prctl.h>
|
||||
# include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||
const std::optional<std::string> & input, bool isInteractive)
|
||||
{
|
||||
throw UnimplementedError("Cannot shell out to git on Windows yet");
|
||||
}
|
||||
|
||||
// Output = error code + "standard out" output stream
|
||||
std::pair<int, std::string> runProgram(RunOptions && options)
|
||||
{
|
||||
throw UnimplementedError("Cannot shell out to git on Windows yet");
|
||||
}
|
||||
|
||||
void runProgram2(const RunOptions & options)
|
||||
{
|
||||
throw UnimplementedError("Cannot shell out to git on Windows yet");
|
||||
}
|
||||
|
||||
}
|
41
src/libutil/windows/signals-impl.hh
Normal file
41
src/libutil/windows/signals-impl.hh
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* User interruption. */
|
||||
|
||||
static inline void setInterrupted(bool isInterrupted)
|
||||
{
|
||||
/* Do nothing for now */
|
||||
}
|
||||
|
||||
static inline bool getInterrupted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void setInterruptThrown()
|
||||
{
|
||||
/* Do nothing for now */
|
||||
}
|
||||
|
||||
void inline checkInterrupt()
|
||||
{
|
||||
/* Do nothing for now */
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing, unlike Unix counterpart, but allows avoiding C++
|
||||
*/
|
||||
struct ReceiveInterrupts
|
||||
{
|
||||
/**
|
||||
* Explicit destructor avoids dead code warnings.
|
||||
*/
|
||||
~ReceiveInterrupts() {}
|
||||
};
|
||||
|
||||
}
|
50
src/libutil/windows/users.cc
Normal file
50
src/libutil/windows/users.cc
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "util.hh"
|
||||
#include "users.hh"
|
||||
#include "environment-variables.hh"
|
||||
#include "file-system.hh"
|
||||
#include "windows-error.hh"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
31
src/libutil/windows/windows-error.cc
Normal file
31
src/libutil/windows/windows-error.cc
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "windows-error.hh"
|
||||
|
||||
#include <error.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
51
src/libutil/windows/windows-error.hh
Normal file
51
src/libutil/windows/windows-error.hh
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <errhandlingapi.h>
|
||||
|
||||
#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<typename... Args>
|
||||
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<typename... Args>
|
||||
WinError(const Args & ... args)
|
||||
: WinError(GetLastError(), args ...)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::string renderError(DWORD lastError);
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue