diff --git a/.github/workflows/ci-wasm32-wasi.yml b/.github/workflows/ci-wasm32-wasi.yml new file mode 100644 index 0000000..5133c1c --- /dev/null +++ b/.github/workflows/ci-wasm32-wasi.yml @@ -0,0 +1,27 @@ +name: CI (wasm32-wasi) +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + env: + GHC_WASM_META: 45f73c3e075fa38efe84055b0dba87996948101d + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 + + - name: Build + run: | + nix shell "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org&rev=${GHC_WASM_META}"#{wasmtime,wasm32-wasi-cabal-9_12,wasm32-wasi-ghc-9_12} --command \ + wasm32-wasi-cabal update + nix shell "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org&rev=${GHC_WASM_META}"#{wasmtime,wasm32-wasi-cabal-9_12,wasm32-wasi-ghc-9_12} --command \ + wasm32-wasi-cabal build + + - name: Test + run: | + nix shell "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org&rev=${GHC_WASM_META}"#{wasmtime,wasm32-wasi-cabal-9_12,wasm32-wasi-ghc-9_12} --command \ + wasm32-wasi-cabal test --test-wrapper ./tests/run-wasmtime.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 7275f3c..0bfbbab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + + - Add wasm32-wasi support. + ## Version 0.7.3 (2024-10-11) - Fix `sysmacros.h` include for GNU/Hurd diff --git a/cabal.project b/cabal.project new file mode 100644 index 0000000..3e51bbc --- /dev/null +++ b/cabal.project @@ -0,0 +1,8 @@ +packages: unix-compat.cabal + +if arch(wasm32) + -- See https://github.com/haskellari/splitmix/pull/73 + source-repository-package + type: git + location: https://github.com/amesgen/splitmix.git + tag: cea9e31bdd849eb0c17611bb99e33d590e126164 diff --git a/cbits/mktemp.c b/cbits/mktemp.c index b9ec050..a653efa 100644 --- a/cbits/mktemp.c +++ b/cbits/mktemp.c @@ -41,13 +41,21 @@ #include #include #include + +#if defined(__wasm__) +#include +#else #include #include -static int random(uint32_t *); +#define open _open +#define stat _stat +#endif + +static int unixcompat_random(uint32_t *); static int _gettemp(char *, int *); -static const unsigned char padchar[] = +static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; int unixcompat_mkstemp(char *path) @@ -64,7 +72,7 @@ static int _gettemp(char *path, int *doopen) { char *start, *trv, *suffp, *carryp; char *pad; - struct _stat sbuf; + struct stat sbuf; int rval; uint32_t randidx, randval; char carrybuf[MAXPATHLEN]; @@ -84,7 +92,7 @@ static int _gettemp(char *path, int *doopen) /* Fill space with random characters */ while (trv >= path && *trv == 'X') { - if (!random(&randval)) { + if (!unixcompat_random(&randval)) { /* this should never happen */ errno = EIO; return 0; @@ -104,7 +112,7 @@ static int _gettemp(char *path, int *doopen) for (; trv > path; --trv) { if (*trv == '/') { *trv = '\0'; - rval = _stat(path, &sbuf); + rval = stat(path, &sbuf); *trv = '/'; if (rval != 0) return (0); @@ -120,11 +128,11 @@ static int _gettemp(char *path, int *doopen) for (;;) { if (doopen) { if ((*doopen = - _open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) + open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) return (1); if (errno != EEXIST) return (0); - } else if (_stat(path, &sbuf)) + } else if (stat(path, &sbuf)) return (errno == ENOENT); /* If we have a collision, cycle through the space of filenames */ @@ -154,7 +162,14 @@ static int _gettemp(char *path, int *doopen) /*NOTREACHED*/ } -static int random(uint32_t *value) +#if defined(__wasm__) +static int unixcompat_random(uint32_t *value) +{ + int r = getentropy(value, sizeof(uint32_t)); + return r == 0 ? 1 : 0; +} +#else +static int unixcompat_random(uint32_t *value) { /* This handle is never released. Windows will clean up when the process * exits. Python takes this approach when emulating /dev/urandom, and if @@ -171,3 +186,4 @@ static int random(uint32_t *value) return 1; } +#endif diff --git a/include/HsUnixCompat.h b/include/HsUnixCompat.h index 32476d4..0adf96d 100644 --- a/include/HsUnixCompat.h +++ b/include/HsUnixCompat.h @@ -4,5 +4,3 @@ unsigned int unix_major(dev_t dev); unsigned int unix_minor(dev_t dev); dev_t unix_makedev(unsigned int maj, unsigned int min); - -#define NEED_setSymbolicLinkOwnerAndGroup !HAVE_LCHOWN diff --git a/src/System/PosixCompat/Extensions.hsc b/src/System/PosixCompat/Extensions.hsc index f9aa7bd..7e637c7 100644 --- a/src/System/PosixCompat/Extensions.hsc +++ b/src/System/PosixCompat/Extensions.hsc @@ -12,7 +12,7 @@ module System.PosixCompat.Extensions ( ) where -#ifndef mingw32_HOST_OS +#if !(defined(mingw32_HOST_OS) || defined(wasm32_HOST_ARCH)) #include "HsUnixCompat.h" #endif @@ -27,7 +27,7 @@ type CMinor = CUInt -- -- The portable implementation always returns @0@. deviceMajor :: DeviceID -> CMajor -#ifdef mingw32_HOST_OS +#if defined(mingw32_HOST_OS) || defined(wasm32_HOST_ARCH) deviceMajor _ = 0 #else deviceMajor dev = unix_major dev @@ -39,7 +39,7 @@ foreign import ccall unsafe "unix_major" unix_major :: CDev -> CUInt -- -- The portable implementation always returns @0@. deviceMinor :: DeviceID -> CMinor -#ifdef mingw32_HOST_OS +#if defined(mingw32_HOST_OS) || defined(wasm32_HOST_ARCH) deviceMinor _ = 0 #else deviceMinor dev = unix_minor dev @@ -49,7 +49,7 @@ foreign import ccall unsafe "unix_minor" unix_minor :: CDev -> CUInt -- | Creates a 'DeviceID' for a device file given a major and minor number. makeDeviceID :: CMajor -> CMinor -> DeviceID -#ifdef mingw32_HOST_OS +#if defined(mingw32_HOST_OS) || defined(wasm32_HOST_ARCH) makeDeviceID _ _ = 0 #else makeDeviceID ma mi = unix_makedev ma mi diff --git a/src/System/PosixCompat/Files.hsc b/src/System/PosixCompat/Files.hsc index eddfa1e..9c1bf66 100644 --- a/src/System/PosixCompat/Files.hsc +++ b/src/System/PosixCompat/Files.hsc @@ -106,11 +106,11 @@ module System.PosixCompat.Files ( #ifndef mingw32_HOST_OS -#include "HsUnixCompat.h" +#include "HsUnixConfig.h" import System.Posix.Files -#if NEED_setSymbolicLinkOwnerAndGroup +#if !HAVE_LCHOWN import System.PosixCompat.Types setSymbolicLinkOwnerAndGroup :: FilePath -> UserID -> GroupID -> IO () diff --git a/src/System/PosixCompat/Process.hs b/src/System/PosixCompat/Process.hs index 47d3e43..4a2b585 100644 --- a/src/System/PosixCompat/Process.hs +++ b/src/System/PosixCompat/Process.hs @@ -8,7 +8,7 @@ module System.PosixCompat.Process ( getProcessID ) where -#ifdef mingw32_HOST_OS +#if defined(mingw32_HOST_OS) import System.Posix.Types (ProcessID) import System.Win32.Process (getCurrentProcessId) @@ -16,6 +16,13 @@ import System.Win32.Process (getCurrentProcessId) getProcessID :: IO ProcessID getProcessID = fromIntegral <$> getCurrentProcessId +#elif defined(wasm32_HOST_ARCH) + +import System.Posix.Types (ProcessID) + +getProcessID :: IO ProcessID +getProcessID = pure 1 + #else import System.Posix.Process diff --git a/src/System/PosixCompat/Temp.hs b/src/System/PosixCompat/Temp.hs index 8575fc1..63b60f6 100644 --- a/src/System/PosixCompat/Temp.hs +++ b/src/System/PosixCompat/Temp.hs @@ -11,13 +11,13 @@ module System.PosixCompat.Temp ( mkstemp ) where -#ifndef mingw32_HOST_OS +#if !(defined(mingw32_HOST_OS) || defined(wasm32_HOST_ARCH)) -- Re-export unix package import System.Posix.Temp #elif defined(__GLASGOW_HASKELL__) --- Windows w/ GHC, we have fdToHandle so we +-- Window/WASM w/ GHC, we have fdToHandle so we -- can use our own implementation of mkstemp. import System.IO (Handle) diff --git a/tests/run-wasmtime.sh b/tests/run-wasmtime.sh new file mode 100755 index 0000000..62466df --- /dev/null +++ b/tests/run-wasmtime.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +TMP="$(mktemp -d --suffix=-unix-compat)" +trap 'rm -rf -- "$TMP"' EXIT +wasmtime --dir "$TMP::/" "$@" +exit "$?" diff --git a/unix-compat.cabal b/unix-compat.cabal index 85f41fe..c635e45 100644 --- a/unix-compat.cabal +++ b/unix-compat.cabal @@ -69,10 +69,13 @@ Library else build-depends: unix >= 2.7.2.0 && < 2.9 - include-dirs: include - includes: HsUnixCompat.h - install-includes: HsUnixCompat.h - c-sources: cbits/HsUnixCompat.c + if arch(wasm32) + c-sources: cbits/mktemp.c + else + include-dirs: include + includes: HsUnixCompat.h + install-includes: HsUnixCompat.h + c-sources: cbits/HsUnixCompat.c if os(solaris) cc-options: -DSOLARIS