feat: add MinGW cross-compilation support for Windows builds on Linux (#2108)

This commit is contained in:
涵曦
2025-11-26 00:07:43 +08:00
committed by GitHub
parent 5608d39789
commit 234e134967
19 changed files with 3101 additions and 2 deletions

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1,5 @@
#include "compat.h"
#include "dlfcn.c"
#include "unistd.c"
#include "wepoll.c"

View File

@@ -0,0 +1,4 @@
#pragma once
#include "unistd.h"
#include "dlfcn.h"

20
3rd/compat-mingw/dlfcn.c Normal file
View File

@@ -0,0 +1,20 @@
#include "dlfcn.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void *dlopen(const char *path, int flag) {
return LoadLibraryA(path);
}
const char *dlerror() {
DWORD err = GetLastError();
HLOCAL LocalAddress = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err, 0, (PTSTR)&LocalAddress, 0, NULL);
return (LPSTR)LocalAddress;
}
void *dlsym(void *dl, const char *sym) {
return GetProcAddress(dl, sym);
}

7
3rd/compat-mingw/dlfcn.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
enum { RTLD_NOW, RTLD_GLOBAL };
void *dlopen(const char *path, int flag);
const char *dlerror();
void *dlsym(void *dl, const char *sym);

3
3rd/compat-mingw/netdb.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
#include <ws2tcpip.h>

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1,3 @@
#pragma once
#include "wepoll.h"

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1,17 @@
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#undef FD_SETSIZE
#define FD_SETSIZE 1024
#include <winsock2.h>
#include <windows.h>
#include <conio.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include "socket_poll.h"
#include "socket_epoll.h"

381
3rd/compat-mingw/unistd.c Normal file
View File

@@ -0,0 +1,381 @@
#include "unistd.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <conio.h>
#include <errno.h>
// WSA error to errno mapping function
static void set_errno_from_wsa_error(int wsa_error) {
switch (wsa_error) {
case WSAECONNRESET:
errno = ECONNRESET;
break;
case WSAECONNABORTED:
errno = ECONNABORTED;
break;
case WSAECONNREFUSED:
errno = ECONNREFUSED;
break;
case WSAENETDOWN:
errno = ENETDOWN;
break;
case WSAENETUNREACH:
errno = ENETUNREACH;
break;
case WSAEHOSTDOWN:
errno = EHOSTDOWN;
break;
case WSAEHOSTUNREACH:
errno = EHOSTUNREACH;
break;
case WSAETIMEDOUT:
errno = ETIMEDOUT;
break;
case WSAENOTCONN:
errno = ENOTCONN;
break;
case WSAEWOULDBLOCK:
errno = EAGAIN;
break;
case WSAEINTR:
errno = EINTR;
break;
case WSAEINVAL:
errno = EINVAL;
break;
case WSAEACCES:
errno = EACCES;
break;
case WSAEADDRINUSE:
errno = EADDRINUSE;
break;
case WSAEADDRNOTAVAIL:
errno = EADDRNOTAVAIL;
break;
default:
errno = EIO; // Generic I/O error for unknown cases
break;
}
}
// Windows Socket initialization
static int winsock_initialized = 0;
static int init_winsock(void) {
if (!winsock_initialized) {
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
return -1;
}
winsock_initialized = 1;
}
return 0;
}
static void cleanup_winsock(void) {
if (winsock_initialized) {
WSACleanup();
winsock_initialized = 0;
}
}
// Auto-initialize Winsock when the library is loaded
__attribute__((constructor))
static void auto_init_winsock(void) {
init_winsock();
}
// Auto-cleanup Winsock when the library is unloaded
__attribute__((destructor))
static void auto_cleanup_winsock(void) {
cleanup_winsock();
}
static LONGLONG get_cpu_freq() {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return freq.QuadPart;
}
int kill(pid_t pid, int exit_code) {
return TerminateProcess((HANDLE)(uintptr_t)pid, exit_code);
}
#define NANOSEC 1000000000
#define MICROSEC 1000000
void usleep(size_t us) {
if (us > 1000) {
Sleep(us / 1000);
return;
}
LONGLONG delta = get_cpu_freq() / MICROSEC * us;
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
LONGLONG start = counter.QuadPart;
for (;;) {
QueryPerformanceCounter(&counter);
if (counter.QuadPart - start >= delta)
return;
}
}
void sleep(size_t ms) {
Sleep(ms);
}
int clock_gettime(int what, struct timespec* ti) {
switch (what) {
case CLOCK_MONOTONIC:
static __int64 Freq = 0;
static __int64 Start = 0;
static __int64 StartTime = 0;
if (Freq == 0) {
StartTime = time(NULL);
QueryPerformanceFrequency((LARGE_INTEGER*)&Freq);
QueryPerformanceCounter((LARGE_INTEGER*)&Start);
}
__int64 Count = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&Count);
// 乘以1000把秒化为毫秒
__int64 now = (__int64)((double)(Count - Start) / (double)Freq * 1000.0) + StartTime * 1000;
ti->tv_sec = now / 1000;
ti->tv_nsec = (now - now / 1000 * 1000) * 1000 * 1000;
return 0;
case CLOCK_REALTIME:
SYSTEMTIME st;
GetSystemTime(&st); // 获取 UTC 时间
// 将 SYSTEMTIME 转换为 UNIX 时间戳
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
ULARGE_INTEGER u64;
u64.LowPart = ft.dwLowDateTime;
u64.HighPart = ft.dwHighDateTime;
ti->tv_sec = (uint32_t)((u64.QuadPart - 116444736000000000ULL) / 10000000); // 转换为秒
ti->tv_nsec = (uint32_t)((u64.QuadPart % 10000000) * 100); // 获取纳秒部分
return 0; // 响应成功
case CLOCK_THREAD_CPUTIME_ID:
// 获取当前线程的 CPU 时间
FILETIME creation_time, exit_time, kernel_time, user_time;
if (GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time, &kernel_time, &user_time)) {
ULARGE_INTEGER u64;
u64.LowPart = user_time.dwLowDateTime;
u64.HighPart = user_time.dwHighDateTime;
ti->tv_sec = (uint32_t)((u64.QuadPart - 116444736000000000ULL) / 10000000); // 转换为秒
ti->tv_nsec = (uint32_t)((u64.QuadPart % 10000000) * 100); // 获取纳秒部分
return 0;
} else {
return -1; // 获取失败
}
}
return -1;
}
int flock(int fd, int flag) {
// Not implemented
return 3;
}
int fcntl(int fd, int cmd, long arg) {
if (cmd == F_GETFL)
return 0;
if (cmd == F_SETFL && arg == O_NONBLOCK) {
u_long ulOption = 1;
ioctlsocket(fd, FIONBIO, &ulOption);
}
return 1;
}
void sigfillset(int* flag) {
// Not implemented
}
int sigemptyset(int* set) {
/*Not implemented*/
return 0;
}
void sigaction(int flag, struct sigaction* action, void* param) {
// Not implemented
}
static void socket_keepalive(int fd) {
int keepalive = 1;
int ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalive,
sizeof(keepalive));
assert(ret != SOCKET_ERROR);
}
int pipe(int fd[2]) {
int listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_fd == INVALID_SOCKET) {
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
srand(time(NULL));
// use random port(range from 60000 to 60999) to simulate pipe()
int port;
for (;;) {
port = 60000 + rand() % 1000;
sin.sin_port = htons(port);
if (!bind(listen_fd, (struct sockaddr*)&sin, sizeof(sin)))
break;
}
if (listen(listen_fd, 5) == SOCKET_ERROR) {
closesocket(listen_fd);
return -1;
}
socket_keepalive(listen_fd);
int client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_fd == INVALID_SOCKET) {
closesocket(listen_fd);
return -1;
}
if (connect(client_fd, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) {
closesocket(listen_fd);
closesocket(client_fd);
return -1;
}
struct sockaddr_in client_addr;
size_t name_len = sizeof(client_addr);
int client_sock = accept(listen_fd, (struct sockaddr*)&client_addr, &name_len);
if (client_sock == INVALID_SOCKET) {
closesocket(listen_fd);
closesocket(client_fd);
return -1;
}
closesocket(listen_fd); // Close listen socket as it's no longer needed
fd[0] = client_sock;
fd[1] = client_fd;
socket_keepalive(client_sock);
socket_keepalive(client_fd);
return 0;
}
int write(int fd, const void* ptr, unsigned int sz) {
WSABUF vecs[1];
vecs[0].buf = (char*)ptr;
vecs[0].len = sz;
DWORD bytesSent;
if (WSASend(fd, vecs, 1, &bytesSent, 0, NULL, NULL)) {
int wsa_error = WSAGetLastError();
set_errno_from_wsa_error(wsa_error);
return -1;
} else {
return bytesSent;
}
}
int read(int fd, void* buffer, unsigned int sz) {
WSABUF vecs[1];
vecs[0].buf = buffer;
vecs[0].len = sz;
DWORD bytesRecv = 0;
DWORD flags = 0;
if (WSARecv(fd, vecs, 1, &bytesRecv, &flags, NULL, NULL)) {
int wsa_error = WSAGetLastError();
if (wsa_error == WSAECONNRESET) {
return 0; // Connection closed by peer
}
// Map WSA error to errno for better error reporting
set_errno_from_wsa_error(wsa_error);
return -1;
} else {
return bytesRecv;
}
}
// Wrapper for recv function with better error handling
int compat_recv(SOCKET s, char *buf, int len, int flags) {
WSABUF vecs[1];
vecs[0].buf = buf;
vecs[0].len = len;
DWORD bytesRecv = 0;
DWORD wsaFlags = 0;
if (WSARecv(s, vecs, 1, &bytesRecv, &wsaFlags, NULL, NULL)) {
int wsa_error = WSAGetLastError();
// Handle non-blocking operations - these are not real errors
if (wsa_error == WSAEWOULDBLOCK || wsa_error == WSAEINTR) {
// For non-blocking sockets, this is normal - no data available right now
set_errno_from_wsa_error(wsa_error);
return -1; // Caller should check errno == EAGAIN
}
if (wsa_error == WSAECONNRESET) {
return 0; // Connection closed by peer
}
// Map WSA error to errno for better error reporting
set_errno_from_wsa_error(wsa_error);
return -1;
} else {
return bytesRecv;
}
}
int close(int fd) {
shutdown(fd, SD_BOTH);
return closesocket(fd);
}
int daemon(int a, int b) {
// Not implemented
return 0;
}
char* strsep(char** stringp, const char* delim) {
char* s;
const char* spanp;
int c, sc;
char* tok;
if ((s = *stringp) == NULL)
return (NULL);
for (tok = s;;) {
c = *s++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
*stringp = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}

117
3rd/compat-mingw/unistd.h Normal file
View File

@@ -0,0 +1,117 @@
#pragma once
#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <process.h>
#include <io.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
// Define missing errno values for network errors
#ifndef ECONNRESET
#define ECONNRESET 104
#endif
#ifndef ECONNABORTED
#define ECONNABORTED 103
#endif
#ifndef ECONNREFUSED
#define ECONNREFUSED 111
#endif
#ifndef ENETDOWN
#define ENETDOWN 100
#endif
#ifndef ENETUNREACH
#define ENETUNREACH 101
#endif
#ifndef EHOSTDOWN
#define EHOSTDOWN 112
#endif
#ifndef EHOSTUNREACH
#define EHOSTUNREACH 113
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT 110
#endif
#ifndef ENOTCONN
#define ENOTCONN 107
#endif
#ifndef EADDRINUSE
#define EADDRINUSE 98
#endif
#ifndef EADDRNOTAVAIL
#define EADDRNOTAVAIL 99
#endif
// Include winsock2.h for gethostname and other network functions
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
// Undefine Windows legacy keywords that conflict with variable names
#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif
// Socket compatibility macros
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
#define ssize_t size_t
#define random rand
#define srandom srand
#define snprintf _snprintf
#define localtime_r _localtime64_s
#define pid_t int
int kill(pid_t pid, int exit_code);
void usleep(size_t us);
void sleep(size_t ms);
int clock_gettime(int what, struct timespec *ti);
enum { LOCK_EX, LOCK_NB };
int flock(int fd, int flag);
struct sigaction {
void (*sa_handler)(int);
int sa_flags;
int sa_mask;
};
enum { SIGPIPE, SIGHUP, SA_RESTART };
void sigfillset(int *flag);
int sigemptyset(int* set);
void sigaction(int flag, struct sigaction *action, void* param);
int pipe(int fd[2]);
int daemon(int a, int b);
#define O_NONBLOCK 1
#define F_SETFL 0
#define F_GETFL 1
int fcntl(int fd, int cmd, long arg);
char *strsep(char **stringp, const char *delim);
int write(int fd, const void* ptr, unsigned int sz);
int read(int fd, void* buffer, unsigned int sz);
// Wrapper function for recv with better error handling
int compat_recv(SOCKET s, char *buf, int len, int flags);
// Macro to redirect recv calls to our wrapper
#define recv compat_recv
int close(int fd);
#define getpid _getpid
#define open _open
#define dup2 _dup2

2253
3rd/compat-mingw/wepoll.c Normal file

File diff suppressed because it is too large Load Diff

113
3rd/compat-mingw/wepoll.h Normal file
View File

@@ -0,0 +1,113 @@
/*
* wepoll - epoll for Windows
* https://github.com/piscisaureus/wepoll
*
* Copyright 2012-2020, Bert Belder <bertbelder@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef WEPOLL_H_
#define WEPOLL_H_
#ifndef WEPOLL_EXPORT
#define WEPOLL_EXPORT
#endif
#include <stdint.h>
enum EPOLL_EVENTS {
EPOLLIN = (int) (1U << 0),
EPOLLPRI = (int) (1U << 1),
EPOLLOUT = (int) (1U << 2),
EPOLLERR = (int) (1U << 3),
EPOLLHUP = (int) (1U << 4),
EPOLLRDNORM = (int) (1U << 6),
EPOLLRDBAND = (int) (1U << 7),
EPOLLWRNORM = (int) (1U << 8),
EPOLLWRBAND = (int) (1U << 9),
EPOLLMSG = (int) (1U << 10), /* Never reported. */
EPOLLRDHUP = (int) (1U << 13),
EPOLLONESHOT = (int) (1U << 31)
};
#define EPOLLIN (1U << 0)
#define EPOLLPRI (1U << 1)
#define EPOLLOUT (1U << 2)
#define EPOLLERR (1U << 3)
#define EPOLLHUP (1U << 4)
#define EPOLLRDNORM (1U << 6)
#define EPOLLRDBAND (1U << 7)
#define EPOLLWRNORM (1U << 8)
#define EPOLLWRBAND (1U << 9)
#define EPOLLMSG (1U << 10)
#define EPOLLRDHUP (1U << 13)
#define EPOLLONESHOT (1U << 31)
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_MOD 2
#define EPOLL_CTL_DEL 3
typedef void* HANDLE;
typedef uintptr_t SOCKET;
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
SOCKET sock; /* Windows specific */
HANDLE hnd; /* Windows specific */
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events and flags */
epoll_data_t data; /* User data variable */
};
#ifdef __cplusplus
extern "C" {
#endif
WEPOLL_EXPORT HANDLE epoll_create(int size);
WEPOLL_EXPORT HANDLE epoll_create1(int flags);
WEPOLL_EXPORT int epoll_close(HANDLE ephnd);
WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd,
int op,
SOCKET sock,
struct epoll_event* event);
WEPOLL_EXPORT int epoll_wait(HANDLE ephnd,
struct epoll_event* events,
int maxevents,
int timeout);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WEPOLL_H_ */