diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc0954679b..a32b2b54ae 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -137,6 +137,12 @@ jobs: shell: C:\cygwin64\bin\bash.exe --login -o igncr '{0}' test-suites: "testmockpkg testinstall" + # Test MinGW/MSYS2 compilation support + - os: windows-2022 + shell: msys2 {0} + test-suites: "testinstall" + extra: "MINGW=yes NO_COVERAGE=1" + env: TEST_SUITES: ${{ matrix.test-suites }} @@ -149,15 +155,31 @@ jobs: python-version: "3.11" - uses: gap-actions/setup-cygwin@v1 - if: ${{ runner.os == 'Windows' }} + if: ${{ runner.os == 'Windows' && !contains(matrix.extra, 'MINGW=yes') }} with: PKGS_TO_INSTALL: 'wget,git,gcc-g++,gcc-core,m4,libgmp-devel,make,automake,libtool,autoconf,autoconf2.5,zlib-devel,libreadline-devel,libmpc-devel,libmpfr-devel,xdg-utils,pkg-config' + - uses: msys2/setup-msys2@v2 + if: ${{ runner.os == 'Windows' && contains(matrix.extra, 'MINGW=yes') }} + with: + msystem: MINGW64 + install: >- + git + make + autoconf + automake + libtool + mingw-w64-x86_64-gcc + mingw-w64-x86_64-gmp + mingw-w64-x86_64-zlib + mingw-w64-x86_64-readline + mingw-w64-x86_64-pkg-config + # There are two cygwin installs on github actions (ours, # and a preinstalled one which we can't use as not enough packages are installed. # Due to conflicts between these two, we cannot spawn new Cygwin processes and # then use IO between processes - - if: ${{ runner.os == 'Windows' }} + - if: ${{ runner.os == 'Windows' && !contains(matrix.extra, 'MINGW=yes') }} name: "Remove tests which do not work on github actions in Windows" run: | rm tst/testinstall/testunix/streamio.tst tst/testinstall/testunix/streams.tst diff --git a/.gitignore b/.gitignore index 5d67442b54..afb23102e7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,16 @@ /extern/build/ /extern/install/ +# Temporary build directories and artifacts +native-build/ +src/ffdata.c +src/ffdata.h + /ffgen +/ffgen.exe /gac /gap +/gap.exe /sysinfo.gap /sysinfo.gap-* /libgap*.dylib* @@ -75,6 +82,11 @@ doc/ref/user_pref_list.xml # external dependencies /extern/gmp*/ +# Ignore zlib build artifacts +/extern/zlib/*.o +/extern/zlib/*.a +/extern/zlib/*.so* +/extern/zlib/zlibrc.o # tests, test logs, package build logs /dev/log diff --git a/INSTALL.md b/INSTALL.md index 41791b4d82..40e9d23c74 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -195,7 +195,8 @@ Change to the directory `gap-4.X.Y` (which you just created by unpacking). To get started quickly you may simply build GAP with default settings by issuing the two commands - ./configure + ./configure # Linux, macOS + bash ./configure # Windows make (note that on BSD systems you have to call `gmake` instead of `make`). diff --git a/Makefile.rules b/Makefile.rules index f69cf6e172..60cf87dbe5 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -438,6 +438,16 @@ ifneq (,$(findstring cygwin,$(host_os))) LIBGAP_FULL = libgap$(SHLIB_EXT) LINK_SHLIB_FLAGS = -shared -Wl,--enable-auto-image-base -Wl,--out-implib,libgap.dll.a +else ifneq (,$(findstring mingw,$(host_os))$(findstring msys,$(host_os))) + SHLIB_EXT=.dll + LIBGAP_FULL = libgap$(SHLIB_MAJOR)$(SHLIB_EXT) + + LINK_SHLIB_FLAGS = -shared -Wl,--enable-auto-image-base -Wl,--out-implib,libgap.dll.a + + GAP_CPPFLAGS += -DPIC + GAP_CFLAGS += -fPIC + GAP_CXXFLAGS += -fPIC + GAP_LDFLAGS += -Wl,--export-all-symbols else ifneq (,$(findstring darwin,$(host_os))) SHLIB_EXT=.dylib LIBGAP_FULL = libgap.$(SHLIB_MAJOR)$(SHLIB_EXT) @@ -736,11 +746,19 @@ EXTERN_FILES += $(GMP_FILES) gmp: $(GMP_FILES) $(GMP_FILES): touch "$(abs_srcdir)/extern/gmp/doc/gmp.info" +ifneq (,$(findstring mingw,$(host_os))$(findstring msys,$(host_os))) + MAKE=$(MAKE) $(srcdir)/cnf/build-extern.sh gmp "$(abs_srcdir)/extern/gmp" \ + $(EXTERN_CONFIGFLAGS) \ + ABI="$(ABI)" \ + --enable-static \ + --disable-shared +else MAKE=$(MAKE) $(srcdir)/cnf/build-extern.sh gmp "$(abs_srcdir)/extern/gmp" \ $(EXTERN_CONFIGFLAGS) \ ABI="$(ABI)" \ --disable-static \ --enable-shared +endif .PHONY: gmp @@ -775,6 +793,10 @@ ifneq (,$(findstring cygwin,$(host_os))) $(ZLIB_FILES): MAKE=$(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" $(srcdir)/cnf/build-cygwin-zlib.sh +else ifneq (,$(findstring mingw,$(host_os))$(findstring msys,$(host_os))) +$(ZLIB_FILES): + MAKE=$(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" $(srcdir)/cnf/build-mingw-zlib.sh + else $(ZLIB_FILES): diff --git a/README.md b/README.md index 2b9582a831..14da3e24b6 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,13 @@ On macOS, you can install the dependencies in several ways: * using Fink: `fink install autoconf2.6 gmp5 readline7` * using MacPorts: `port install autoconf gmp readline` +On Windows, you need to install the MinGW-w64 toolchain, for example by: + winget install -e --id RProject.Rtools + $env:Path = "$env:RTOOLS45_HOME\x86_64-w64-mingw32.static.posix\bin;$env:RTOOLS45_HOME\usr\bin;" + $env:Path # in PowerShell + set PATH=%RTOOLS45_HOME%\x86_64-w64-mingw32.static.posix\bin;%RTOOLS45_HOME%\usr\bin;%PATH% # in cmd.exe + pacman -Sy + pacman -S autoconf automake libtool + On other operating systems, you will need to figure out equivalent commands to install the required dependencies. @@ -89,7 +96,8 @@ to install the required dependencies. Then to build GAP, first run this command to generate the `configure` script: - ./autogen.sh + ./autogen.sh # Linux, macOS + bash autogen.sh # Windows Afterwards you can proceed as described in `INSTALL.md`. If you are on macOS, we recommend that you take a look at section "GAP for macOS" of `INSTALL.md` diff --git a/cnf/build-extern.sh b/cnf/build-extern.sh index 9d02c96dde..e4ce64ecfd 100755 --- a/cnf/build-extern.sh +++ b/cnf/build-extern.sh @@ -26,12 +26,30 @@ if [[ ( ! "$builddir/config.status" -nt "$src/configure" ) fi $MAKE -C "$builddir" -if ! $MAKE -C "$builddir" check; then - echo "=== FAILED checking $pkg ===" - echo "The copy of $pkg distributed with GAP has failed to pass its internal checks" - echo "You can either install the library from a different source, or use" - echo "a newer release of GAP" - exit 1 +# Skip tests for cross-compilation as they cannot run on host system +# Check if --host was passed and differs from --build (cross-compilation) +if echo "$@" | grep -q -- "--host=" && echo "$@" | grep -q -- "--build="; then + host_arg=$(echo "$@" | grep -o -- "--host=[^ ]*" | cut -d= -f2) + build_arg=$(echo "$@" | grep -o -- "--build=[^ ]*" | cut -d= -f2) + if [ "$host_arg" != "$build_arg" ]; then + echo "=== SKIPPING tests for $pkg (cross-compilation: build=$build_arg, host=$host_arg) ===" + else + if ! $MAKE -C "$builddir" check; then + echo "=== FAILED checking $pkg ===" + echo "The copy of $pkg distributed with GAP has failed to pass its internal checks" + echo "You can either install the library from a different source, or use" + echo "a newer release of GAP" + exit 1 + fi + fi +else + if ! $MAKE -C "$builddir" check; then + echo "=== FAILED checking $pkg ===" + echo "The copy of $pkg distributed with GAP has failed to pass its internal checks" + echo "You can either install the library from a different source, or use" + echo "a newer release of GAP" + exit 1 + fi fi $MAKE -C "$builddir" install diff --git a/cnf/build-mingw-zlib.sh b/cnf/build-mingw-zlib.sh new file mode 100755 index 0000000000..0f460504f1 --- /dev/null +++ b/cnf/build-mingw-zlib.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# zlib on MinGW requires a special build procedure +# Build static library with proper cross-compilation + +cd extern/zlib && + +# Extract prefix from CC (e.g., "x86_64-w64-mingw32-gcc " -> "x86_64-w64-mingw32-") +PREFIX=$(echo "${CC}" | sed 's/gcc.*$//') + +# Build static library with cross-compiler +make -f win32/Makefile.gcc \ + CC="${CC}" \ + PREFIX="${PREFIX}" \ + libz.a && + +# Install static library and headers +mkdir -p ../install/zlib/lib ../install/zlib/include && +cp libz.a ../install/zlib/lib/ && +cp zlib.h zconf.h ../install/zlib/include/ \ No newline at end of file diff --git a/configure.ac b/configure.ac index 5004b7b37a..1a4cb8fc33 100644 --- a/configure.ac +++ b/configure.ac @@ -595,7 +595,14 @@ AS_IF([test x$gmp_found = xno],[ GMP_PREFIX='${abs_builddir}/extern/install/gmp' GMP_CPPFLAGS='-I${abs_builddir}/extern/install/gmp/include' GMP_LDFLAGS='-L${abs_builddir}/extern/install/gmp/lib' - GMP_LIBS='-Wl,-rpath,${abs_builddir}/extern/install/gmp/lib -lgmp' + case "$host_os" in + *mingw* | *msys* ) + GMP_LIBS='-lgmp' + ;; + *) + GMP_LIBS='-Wl,-rpath,${abs_builddir}/extern/install/gmp/lib -lgmp' + ;; + esac ]) AC_SUBST([BUILD_GMP]) @@ -673,7 +680,14 @@ AS_IF([test x$zlib_found = xno],[ BUILD_ZLIB=yes ZLIB_CPPFLAGS='-I${abs_builddir}/extern/install/zlib/include' ZLIB_LDFLAGS='-L${abs_builddir}/extern/install/zlib/lib' - ZLIB_LIBS='-Wl,-rpath,${abs_builddir}/extern/install/zlib/lib -lz' + case "$host_os" in + *mingw* | *msys* ) + ZLIB_LIBS='-lz' + ;; + *) + ZLIB_LIBS='-Wl,-rpath,${abs_builddir}/extern/install/zlib/lib -lz' + ;; + esac ]) AC_SUBST([BUILD_ZLIB]) @@ -955,6 +969,19 @@ case "$host_cpu" in ;; esac +dnl +dnl Detect Windows/MinGW platform +dnl +case "$host_os" in + *mingw* | *msys* ) + AC_DEFINE([SYS_IS_MINGW], [1], [define as 1 on MinGW/Windows]) + AC_DEFINE([GAP_DISABLE_SUBPROCESS_CODE], [1], [disable subprocess support on MinGW]) + ;; + *cygwin* ) + AC_DEFINE([SYS_IS_CYGWIN32], [1], [define as 1 on Cygwin]) + ;; +esac + dnl dnl check for the existence of various header files @@ -1016,6 +1043,8 @@ AC_CHECK_FUNCS([setitimer]) dnl check for functions dealing with virtual memory AC_CHECK_FUNCS([vm_allocate sbrk madvise sysconf]) +AC_CHECK_FUNCS([realpath readlink mkdtemp]) +AC_CHECK_FUNCS([lstat ttyname]) dnl check for large-file support (for accessing files whose sizes or inodes require 64bits) AC_SYS_LARGEFILE diff --git a/dev/ci-configure-gap.sh b/dev/ci-configure-gap.sh index c2dbd67540..a486afb702 100755 --- a/dev/ci-configure-gap.sh +++ b/dev/ci-configure-gap.sh @@ -5,7 +5,7 @@ set -ex SRCDIR=${SRCDIR:-$PWD} # print some data useful for debugging issues with the build -gcov --version +gcov --version || echo "gcov not available" printenv | sort git show --pretty=fuller -s diff --git a/environment-win.yml b/environment-win.yml new file mode 100644 index 0000000000..05e4651b88 --- /dev/null +++ b/environment-win.yml @@ -0,0 +1,29 @@ +# GAP Development Environment +# +# This conda environment provides the necessary tools and libraries for +# developing and testing GAP on Windows. +# +# Usage: +# conda env create -f environment-win.yml +# conda activate gap-dev + +name: gap-dev +channels: + - conda-forge +dependencies: + # Core development tools + - make + - pkg-config + - m2-autoconf + - m2-libtool + - m2-m4 + - m2-grep + - m2-sed + + # C/C++ compiler and build tools + - m2w64-gcc + - ccache + + # Required libraries + - m2w64-gmp # GNU Multiple Precision Arithmetic Library + - m2w64-zlib # Compression library diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000000..d26f940639 --- /dev/null +++ b/environment.yml @@ -0,0 +1,29 @@ +# GAP Development Environment +# +# This conda environment provides the necessary tools and libraries for +# developing and testing GAP on Linux and macOS. +# +# Usage: +# conda env create -f environment.yml +# conda activate gap-dev + +name: gap-dev +channels: + - conda-forge +dependencies: + # Core development tools + - make + - pkg-config + - autoconf + - automake + - libtool + + # C/C++ compiler and build tools + - compilers + - ccache + + # Required libraries + - gmp # GNU Multiple Precision Arithmetic Library + - zlib # Compression library + - readline # Command line editing library + diff --git a/src/common.h b/src/common.h index 02818f567c..abc8bf0def 100644 --- a/src/common.h +++ b/src/common.h @@ -14,6 +14,7 @@ #include #include +#include "config.h" #include "debug.h" // check if we are on a 64 or 32 bit machine; in the former @@ -63,11 +64,20 @@ GAP_STATIC_ASSERT(sizeof(void *) == SIZEOF_VOID_P, "sizeof(void *) is wrong"); // wanted to, a lot more work (and planning) would have to be invested into // that anyway. #ifndef EXPORT_INLINE -#ifdef HPCGAP -#define EXPORT_INLINE static inline -#else -#define EXPORT_INLINE inline -#endif +# ifdef HPCGAP +# define EXPORT_INLINE static inline +# elif defined(DEBUG_FORCE_EXTERN_INLINE) + // A translation unit (debug.c) that wants externally visible + // inline symbols can define DEBUG_FORCE_EXTERN_INLINE before including + // this header to switch them to 'extern inline'. +# define EXPORT_INLINE extern inline +# elif defined(SYS_IS_MINGW) + // On MinGW we normally use 'static inline' to avoid multiple definition + // errors. +# define EXPORT_INLINE static inline +# else +# define EXPORT_INLINE inline +# endif #endif diff --git a/src/debug.c b/src/debug.c index d0e981b4f3..c69d30308e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -8,11 +8,8 @@ ** SPDX-License-Identifier: GPL-2.0-or-later */ -// set EXPORT_INLINE to 'extern inline' to force an instance of each -// function marked with EXPORT_INLINE to be actually emitted, so that -// it can e.g. be accessed from gdb or lldb, or from software using -// libgap as a shared library -#define EXPORT_INLINE extern inline +// Request that common.h use 'extern inline' (see common.h for details) +#define DEBUG_FORCE_EXTERN_INLINE 1 #include "common.h" diff --git a/src/funcs.h b/src/funcs.h index 56c3d1bc77..0f000351be 100644 --- a/src/funcs.h +++ b/src/funcs.h @@ -18,6 +18,7 @@ #define GAP_FUNCS_H #include "common.h" +#include "config.h" /**************************************************************************** ** @@ -64,4 +65,15 @@ EXPORT_INLINE void CheckRecursionBefore( void ) */ StructInitInfo * InitInfoFuncs ( void ); +// Handle platform differences for setjmp/longjmp +#ifdef SYS_IS_MINGW +// On MinGW/Windows, _setjmp requires two arguments, so use regular setjmp +#define GAP_SETJMP(jmp_buf) setjmp(jmp_buf) +#define GAP_LONGJMP(jmp_buf, val) longjmp(jmp_buf, val) +#else +// On POSIX systems, use _setjmp/_longjmp for better performance +#define GAP_SETJMP(jmp_buf) _setjmp(jmp_buf) +#define GAP_LONGJMP(jmp_buf, val) _longjmp(jmp_buf, val) +#endif + #endif // GAP_FUNCS_H diff --git a/src/gap_all.h b/src/gap_all.h index 1c69e2810a..c36e3848c5 100644 --- a/src/gap_all.h +++ b/src/gap_all.h @@ -37,6 +37,7 @@ extern "C" { #include "gap.h" #include "gapstate.h" #include "gaptime.h" +#include "gaputils.h" #include "gasman.h" #include "gvars.h" #include "info.h" @@ -76,6 +77,9 @@ extern "C" { #include "trans.h" #include "trycatch.h" #include "vars.h" +#include "vec8bit.h" +#include "vecffe.h" +#include "vecgf2.h" #include "vector.h" #include "version.h" #include "weakptr.h" diff --git a/src/gaptime.c b/src/gaptime.c index c0fe220192..7254c817b9 100644 --- a/src/gaptime.c +++ b/src/gaptime.c @@ -62,7 +62,7 @@ UInt SyTime(void) // a substitute (it is not perfect, as NanosecondsSinceEpoch() // is walltime, while RUSAGE_SELF is CPU time). return SyNanosecondsSinceEpoch()/1000000000; -#else +#elif defined(HAVE_GETRUSAGE) struct rusage buf; if (getrusage(RUSAGE_SELF, &buf)) { @@ -71,6 +71,10 @@ UInt SyTime(void) (Int)strerror(errno), (Int)errno); } return buf.ru_utime.tv_sec * 1000 + buf.ru_utime.tv_usec / 1000; +#else + // Fallback for systems without getrusage (e.g., Windows/MinGW) + // Use wall clock time instead of CPU time as an approximation + return SyNanosecondsSinceEpoch()/1000000; #endif } @@ -210,9 +214,11 @@ static Obj FuncRuntime(Obj self) static Obj FuncRUNTIMES(Obj self) { UInt tmp; - struct rusage buf; Obj res = NEW_PLIST(T_PLIST, 4); +#ifdef HAVE_GETRUSAGE + struct rusage buf; + if (getrusage(RUSAGE_SELF, &buf)) { ErrorMayQuit("RUNTIMES: call to getrusage(RUSAGE_SELF) failed: " "%s (errno %d)", @@ -234,6 +240,15 @@ static Obj FuncRUNTIMES(Obj self) tmp = buf.ru_stime.tv_sec * 1000 + buf.ru_stime.tv_usec / 1000; ASS_LIST(res, 4, ObjInt_UInt(tmp)); +#else + // Fallback for systems without getrusage (e.g., Windows/MinGW) + // Return current time for user and system time, zero for children + tmp = SyTime(); + ASS_LIST(res, 1, ObjInt_UInt(tmp)); // user time + ASS_LIST(res, 2, ObjInt_UInt(0)); // system time (unavailable) + ASS_LIST(res, 3, ObjInt_UInt(0)); // children user time (unavailable) + ASS_LIST(res, 4, ObjInt_UInt(0)); // children system time (unavailable) +#endif return res; } diff --git a/src/gasman.c b/src/gasman.c index 6712ebec79..cd7d8beb1a 100644 --- a/src/gasman.c +++ b/src/gasman.c @@ -2009,7 +2009,7 @@ static UInt CollectBags_Mark(UInt FullBags) } // mark from the stack - _setjmp(RegsBags); + GAP_SETJMP(RegsBags); #if defined(SYS_IS_SPARC) SparcStackFuncBags(); #endif diff --git a/src/gasman.h b/src/gasman.h index b53c9eb8a4..e073750a67 100644 --- a/src/gasman.h +++ b/src/gasman.h @@ -36,6 +36,7 @@ #ifndef GAP_GASMAN_H #define GAP_GASMAN_H +#include "funcs.h" // for GAP_SETJMP/GAP_LONGJMP #include "common.h" diff --git a/src/iostream.c b/src/iostream.c index 68bdf6a4da..14869fd08c 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -44,7 +44,9 @@ #include #include #include +#ifdef HAVE_TERMIOS_H #include +#endif #include #ifdef HAVE_SYS_WAIT_H diff --git a/src/main.c b/src/main.c index 69d5e3dd95..8c761cd83d 100644 --- a/src/main.c +++ b/src/main.c @@ -60,9 +60,17 @@ find_yourself(const char * argv0, char * result, size_t resultsize) // absolute path, like '/usr/bin/gap' if (argv0[0] == '/') { +#ifdef HAVE_REALPATH if (realpath(argv0, result) && !access(result, F_OK)) { return; // success } +#else + // Fallback: just copy the path and check if it exists + gap_strlcpy(result, argv0, resultsize); + if (!access(result, F_OK)) { + return; // success + } +#endif } // relative path, like 'bin/gap.sh' else if (strchr(argv0, '/')) { @@ -70,9 +78,17 @@ find_yourself(const char * argv0, char * result, size_t resultsize) return; gap_strlcat(tmpbuf, "/", sizeof(tmpbuf)); gap_strlcat(tmpbuf, argv0, sizeof(tmpbuf)); +#ifdef HAVE_REALPATH if (realpath(tmpbuf, result) && !access(result, F_OK)) { return; // success } +#else + // Fallback: just copy the constructed path and check if it exists + gap_strlcpy(result, tmpbuf, resultsize); + if (!access(result, F_OK)) { + return; // success + } +#endif } // executable name, like 'gap' else { @@ -83,9 +99,17 @@ find_yourself(const char * argv0, char * result, size_t resultsize) gap_strlcpy(tmpbuf, pathitem, sizeof(tmpbuf)); gap_strlcat(tmpbuf, "/", sizeof(tmpbuf)); gap_strlcat(tmpbuf, argv0, sizeof(tmpbuf)); +#ifdef HAVE_REALPATH if (realpath(tmpbuf, result) && !access(result, F_OK)) { return; // success } +#else + // Fallback: just copy the constructed path and check if it exists + gap_strlcpy(result, tmpbuf, resultsize); + if (!access(result, F_OK)) { + return; // success + } +#endif } } @@ -108,23 +132,29 @@ static void SetupGAPLocation(const char * argv0, char * GAPExecLocation) // try Linux procfs if (!*locBuf) { +#ifdef HAVE_READLINK ssize_t ret = readlink("/proc/self/exe", locBuf, sizeof(locBuf)); if (ret < 0) *locBuf = 0; // reset buffer after error +#endif } // try FreeBSD / DragonFly BSD procfs if (!*locBuf) { +#ifdef HAVE_READLINK ssize_t ret = readlink("/proc/curproc/file", locBuf, sizeof(locBuf)); if (ret < 0) *locBuf = 0; // reset buffer after error +#endif } // try NetBSD procfs if (!*locBuf) { +#ifdef HAVE_READLINK ssize_t ret = readlink("/proc/curproc/exe", locBuf, sizeof(locBuf)); if (ret < 0) *locBuf = 0; // reset buffer after error +#endif } // if we are still failing, go and search the path @@ -133,8 +163,13 @@ static void SetupGAPLocation(const char * argv0, char * GAPExecLocation) } // resolve symlinks (if present) +#ifdef HAVE_REALPATH if (!realpath(locBuf, GAPExecLocation)) *GAPExecLocation = 0; // reset buffer after error +#else + // Fallback: just copy the path without resolving symlinks + gap_strlcpy(GAPExecLocation, locBuf, GAP_PATH_MAX); +#endif // now strip the executable name off length = strlen(GAPExecLocation); diff --git a/src/profile.c b/src/profile.c index c6c193adaa..5b79cb41e8 100644 --- a/src/profile.c +++ b/src/profile.c @@ -459,8 +459,9 @@ static inline Int8 CPUmicroseconds(void) return (Int8)buf.ru_utime.tv_sec * 1000000 + (Int8)buf.ru_utime.tv_usec; #else - // Should never get here! - abort(); + // Fallback for systems without getrusage (e.g., Windows/MinGW) + // Use wall clock time instead of CPU time + return SyNanosecondsSinceEpoch() / 1000; #endif } diff --git a/src/read.c b/src/read.c index 1c12349574..255a63f0b8 100644 --- a/src/read.c +++ b/src/read.c @@ -77,7 +77,7 @@ #define TRY_IF_NO_ERROR \ if (!rs->s.NrError) { \ volatile Int recursionDepth = GetRecursionDepth(); \ - if (_setjmp(STATE(ReadJmpError))) { \ + if (GAP_SETJMP(STATE(ReadJmpError))) { \ SetRecursionDepth(recursionDepth); \ rs->s.NrError++; \ } \ diff --git a/src/streams.c b/src/streams.c index 1a45a105ef..99ebdcbd6c 100644 --- a/src/streams.c +++ b/src/streams.c @@ -13,6 +13,18 @@ #include "streams.h" +#include "config.h" + +// Include system headers first to avoid conflicts on Windows/MinGW +// where dirent.h includes system io.h which can conflict with GAP's io.h +#include +#include +#include +#include +#include +#include +#include + #include "bool.h" #include "calls.h" #include "error.h" @@ -38,16 +50,6 @@ #include "trycatch.h" #include "vars.h" -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - #ifdef HAVE_SELECT // For FuncUNIXSelect #include @@ -1041,8 +1043,17 @@ static Obj FuncTmpDirectory(Obj self) const char * extra = "/gaptempdirXXXXXX"; AppendCStr(name, extra, strlen(extra)); +#ifdef HAVE_MKDTEMP if (mkdtemp(CSTR_STRING(name)) == 0) return Fail; +#else + // Fallback for systems without mkdtemp (like MinGW) + if (mktemp(CSTR_STRING(name)) == 0) + return Fail; + // Try to create the directory + if (SyMkdir(CONST_CSTR_STRING(name)) == -1) + return Fail; +#endif return name; } @@ -1135,6 +1146,7 @@ static Obj FuncGAP_chdir(Obj self, Obj path) static Obj FuncGAP_realpath(Obj self, Obj path) { RequireStringRep(SELF_NAME, path); +#ifdef HAVE_REALPATH char resolved_path[PATH_MAX]; if (NULL == realpath(CONST_CSTR_STRING(path), resolved_path)) { @@ -1142,6 +1154,11 @@ static Obj FuncGAP_realpath(Obj self, Obj path) return Fail; } return MakeString(resolved_path); +#else + // Fallback for systems without realpath (like MinGW) + // Just return the original path as-is + return CopyObj(path, 0); +#endif } diff --git a/src/sysenv.h b/src/sysenv.h index 88661e2a58..38f03b1e26 100644 --- a/src/sysenv.h +++ b/src/sysenv.h @@ -21,10 +21,13 @@ #include #define environ (*_NSGetEnviron()) -#elif !defined(environ) - -extern char ** environ; +#elif defined(SYS_IS_MINGW) +// MinGW exposes environ via +#include +#endif +#ifndef environ +extern char ** environ; // generic fallback #endif #endif // GAP_SYSENV_H diff --git a/src/sysfiles.c b/src/sysfiles.c index c2cf83db00..e116db22fa 100644 --- a/src/sysfiles.c +++ b/src/sysfiles.c @@ -14,40 +14,32 @@ // ensure we can access large files #define _FILE_OFFSET_BITS 64 -#include "sysfiles.h" - -#include "bool.h" -#include "calls.h" -#include "error.h" -#include "gapstate.h" -#include "gaputils.h" -#include "gvars.h" -#include "io.h" -#include "lists.h" -#include "modules.h" -#include "plist.h" -#include "precord.h" -#include "read.h" -#include "records.h" -#include "stats.h" -#include "stringobj.h" -#include "sysopt.h" -#include "sysstr.h" -#include "system.h" - -#include "hpc/thread.h" +#ifdef SYS_IS_MINGW +// Feature test macros needed for POSIX functions on MinGW +#define _GNU_SOURCE +#define _POSIX_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif #include "config.h" +// System includes first to avoid conflicts with GAP headers #include #include #include #include #include +#ifdef HAVE_TERMIOS_H #include +#endif #include #include +#ifdef SYS_IS_MINGW +// On MinGW, some POSIX functions like access, read, write are in io.h +#include +#endif + #include #include #include @@ -80,7 +72,33 @@ #include +#ifndef SYS_IS_MINGW #include +#endif + +// GAP includes after system includes to avoid header conflicts +#include "sysfiles.h" + +#include "bool.h" +#include "calls.h" +#include "error.h" +#include "gapstate.h" +#include "gaputils.h" +#include "gvars.h" +#include "io.h" +#include "lists.h" +#include "modules.h" +#include "plist.h" +#include "precord.h" +#include "read.h" +#include "records.h" +#include "stats.h" +#include "stringobj.h" +#include "sysopt.h" +#include "sysstr.h" +#include "system.h" + +#include "hpc/thread.h" // 'EndLineHook' is a GAP-level variable which can be set to a function to be @@ -356,6 +374,7 @@ static Obj FuncCrcString(Obj self, Obj str) Obj SyGetOsRelease(void) { Obj r = NEW_PREC(0); +#ifndef SYS_IS_MINGW struct utsname buf; if (!uname(&buf)) { AssPRec(r, RNamName("sysname"), MakeImmString(buf.sysname)); @@ -364,6 +383,7 @@ Obj SyGetOsRelease(void) AssPRec(r, RNamName("version"), MakeImmString(buf.version)); AssPRec(r, RNamName("machine"), MakeImmString(buf.machine)); } +#endif return r; } @@ -867,7 +887,9 @@ Int SyIsEndOfFile ( ** continue signals if this particular version of UNIX supports them, so we ** can turn the terminal line back to cooked mode before stopping GAP. */ +#ifdef HAVE_TERMIOS_H static struct termios syOld, syNew; // old and new terminal state +#endif #ifdef SIGTSTP @@ -898,6 +920,7 @@ UInt syStartraw ( Int fid ) else { return 0; } } +#ifdef HAVE_TERMIOS_H // try to get the terminal attributes, will fail if not terminal const int fd = SyBufFileno(fid); GAP_ASSERT(fd >= 0); @@ -928,6 +951,11 @@ UInt syStartraw ( Int fid ) // indicate success return 1; +#else + // On systems without termios (e.g., Windows), we can't do raw mode + // Return 0 to indicate that raw mode is not available + return 0; +#endif } @@ -943,6 +971,7 @@ void syStopraw ( if ( SyWindow ) return; +#ifdef HAVE_TERMIOS_H #ifdef SIGTSTP // remove signal handler for stop signal( SIGTSTP, SIG_DFL ); @@ -953,6 +982,8 @@ void syStopraw ( GAP_ASSERT(fd >= 0); if (tcsetattr(fd, TCSANOW, &syOld) == -1) fputs("gap: 'tcsetattr' could not turn off raw mode!\n",stderr); +#endif + // On systems without termios, nothing to do } @@ -1014,12 +1045,16 @@ static void syAnswerIntr(int signr) void SyInstallAnswerIntr ( void ) { +#ifdef HAVE_SIGACTION struct sigaction sa; sa.sa_handler = syAnswerIntr; sigemptyset(&(sa.sa_mask)); sa.sa_flags = SA_RESTART; sigaction( SIGINT, &sa, NULL ); +#elif defined(HAVE_SIGNAL) + signal(SIGINT, syAnswerIntr); +#endif } @@ -2925,7 +2960,11 @@ Int SyMkdir ( const Char * name ) { Int res; SyClearErrorNo(); +#ifdef SYS_IS_MINGW + res = mkdir(name); +#else res = mkdir(name, 0777); +#endif if (res == -1) SySetErrorNo(); return res; @@ -2964,7 +3003,11 @@ char SyFileType(const Char * path) int res; struct stat ourlstatbuf; +#ifdef HAVE_LSTAT res = lstat(path, &ourlstatbuf); +#else + res = stat(path, &ourlstatbuf); +#endif if (res < 0) { SySetErrorNo(); return 0; @@ -2973,8 +3016,10 @@ char SyFileType(const Char * path) return 'F'; if (S_ISDIR(ourlstatbuf.st_mode)) return 'D'; +#ifdef S_ISLNK if (S_ISLNK(ourlstatbuf.st_mode)) return 'L'; +#endif #ifdef S_ISCHR if (S_ISCHR(ourlstatbuf.st_mode)) return 'C'; @@ -3200,8 +3245,13 @@ void InitSysFiles(void) if (syBuf[0].isTTY) { // if stdin is on a terminal, make sure stdout in on the same terminal if (stat_in.st_dev != stat_out.st_dev || - stat_in.st_ino != stat_out.st_ino) + stat_in.st_ino != stat_out.st_ino) { +#ifdef HAVE_TTYNAME syBuf[0].echo = open(ttyname(fileno(stdin)), O_WRONLY); +#else + // fallback: keep the default stdout echo +#endif + } } // set up stdout @@ -3219,8 +3269,13 @@ void InitSysFiles(void) if (syBuf[2].isTTY) { // if stderr is on a terminal, make sure errin in on the same terminal if (stat_in.st_dev != stat_err.st_dev || - stat_in.st_ino != stat_err.st_ino) + stat_in.st_ino != stat_err.st_ino) { +#ifdef HAVE_TTYNAME syBuf[2].fp = open(ttyname(fileno(stderr)), O_RDONLY); +#else + // fallback: keep the default stdin fp +#endif + } } // set up errout diff --git a/src/sysmem.c b/src/sysmem.c index 039272385b..97aac8a411 100644 --- a/src/sysmem.c +++ b/src/sysmem.c @@ -23,8 +23,8 @@ #ifdef GAP_MEM_CHECK #include -#include // for qsort #endif +#include // for qsort, calloc #include // for fputs #include // for ftruncate, getpid, unlink, sbrk, sysconf diff --git a/src/system.c b/src/system.c index 43a109574c..2622464d71 100644 --- a/src/system.c +++ b/src/system.c @@ -573,9 +573,9 @@ static void InitSysOpts(void) #endif // defined(SYS_IS_64_BIT) #ifdef SYS_IS_64_BIT - SyAllocPool = 4096L*1024*1024; // Note this is in bytes! + SyAllocPool = 4096LL*1024*1024; // Note this is in bytes! #else - SyAllocPool = 1536L*1024*1024; // Note this is in bytes! + SyAllocPool = 1536LL*1024*1024; // Note this is in bytes! #endif // defined(SYS_IS_64_BIT) SyStorOverrun = SY_STOR_OVERRUN_CLEAR; SyStorKill = 0; diff --git a/src/trycatch.c b/src/trycatch.c index 12a68deb2e..187716fc23 100644 --- a/src/trycatch.c +++ b/src/trycatch.c @@ -58,5 +58,6 @@ void GAP_THROW(void) int depth = STATE(TryCatchDepth); for (int i = 0; i < ARRAY_SIZE(throwObservers) && throwObservers[i]; ++i) (throwObservers[i])(depth); - _longjmp(STATE(ReadJmpError), 1); + + GAP_LONGJMP(STATE(ReadJmpError), 1); } diff --git a/src/trycatch.h b/src/trycatch.h index 939432cb82..856694fe3f 100644 --- a/src/trycatch.h +++ b/src/trycatch.h @@ -13,7 +13,7 @@ #ifndef GAP_TRYCATCH_H #define GAP_TRYCATCH_H -#include "funcs.h" // for SetRecursionDepth +#include "funcs.h" // for SetRecursionDepth, GAP_SETJMP/GAP_LONGJMP #include "gapstate.h" #include "system.h" // for NORETURN @@ -21,6 +21,7 @@ #include // for memcpy + /**************************************************************************** ** *T TryCatchMode @@ -87,7 +88,7 @@ void InvokeTryCatchHandler(TryCatchMode mode); GAP_TryCatchEnv gap__env; \ gap_safe_trycatch(&gap__env); \ InvokeTryCatchHandler(TryEnter); \ - if (!_setjmp(STATE(ReadJmpError))) \ + if (!GAP_SETJMP(STATE(ReadJmpError))) \ for (gap__i = 1; gap__i; gap__i = 0, \ InvokeTryCatchHandler(TryLeave), \ gap_restore_trycatch(&gap__env))