From 1faf3aff1b2493f94aac0d0e292579ab68f483c2 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Wed, 12 Oct 2022 17:47:09 -0500 Subject: [PATCH] nim: patch in support for running native --- README.md | 5 + overlay/default.nix | 11 + overlay/nim.patch | 2253 +++++++++++++++++++++++++++++++++++++++++++ tests/nim.nix | 6 +- 4 files changed, 2270 insertions(+), 5 deletions(-) create mode 100644 overlay/nim.patch diff --git a/README.md b/README.md index bdc6c4e..ee4ce5b 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,11 @@ the port definition using a `nativeBuildInputs` attribute. Note that the hash checked into `ports.nix` is an *output hash*. The port hash within the Genode source tree is an *explicit input hash*. +### Building a package from a test + +```sh +nix build -L .#checks.x86_64-linux.«TEST_NAME».nodes.machine.config.genode.init.children.«CHILD_NAME».package +``` ## Legal diff --git a/overlay/default.nix b/overlay/default.nix index 3c103e8..49115ca 100644 --- a/overlay/default.nix +++ b/overlay/default.nix @@ -165,6 +165,17 @@ in nullPkgs // { # Stay clear of upstream on this one. addPatchesHost [ ./ncurses/genode.patch ] prev.ncurses; + nim-unwrapped = + # programs are still linking to posix library + overrideAttrsTarget ({ patches, ... }: { + version = "1.6.8"; + src = fetchurl { + url = "https://nim-lang.org/download/nim-1.6.8.tar.xz"; + hash = "sha256-D1tlzbYPeK9BywdcI4mDaJoeH34lyBnxeYYsGKSEz1c="; + }; + patches = patches ++ [ ./nim.patch ]; + }) prev.nim-unwrapped; + nimPackages = # Packages from the Nimble flake with adjustments. prev.nimPackages.overrideScope' (_: prev': { diff --git a/overlay/nim.patch b/overlay/nim.patch new file mode 100644 index 0000000..dfd6111 --- /dev/null +++ b/overlay/nim.patch @@ -0,0 +1,2253 @@ +From b50326bf9b53f02d7a1244dbf12a00b35a0ff71e Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Sun, 30 Jan 2022 10:20:04 +0100 +Subject: [PATCH 1/8] nimNoLibc float formatting + +--- + lib/pure/strutils.nim | 6 ++- + lib/system/formatfloat.nim | 76 +++++++++++++++++++------------------- + lib/system/strmantle.nim | 67 ++++++++++++++++++--------------- + 3 files changed, 80 insertions(+), 69 deletions(-) + +diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim +index 361fa72ef..9c6dd5225 100644 +--- a/lib/pure/strutils.nim ++++ b/lib/pure/strutils.nim +@@ -1839,7 +1839,7 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. + inc skip, a[s[skip + subLast]] + return -1 + +-when not (defined(js) or defined(nimdoc) or defined(nimscript)): ++when not (defined(js) or defined(nimdoc) or defined(nimscript) or defined(nimNoLibc)): + func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. + importc: "memchr", header: "".} + func c_strstr(haystack, needle: cstring): cstring {. +@@ -2327,7 +2327,7 @@ func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} = + + + # floating point formatting: +-when not defined(js): ++when not (defined(js) or defined(nimNoLibc)): + func c_sprintf(buf, frmt: cstring): cint {.header: "", + importc: "sprintf", varargs} + +@@ -2377,6 +2377,8 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, + # Depending on the locale either dot or comma is produced, + # but nothing else is possible: + if result[i] in {'.', ','}: result[i] = decimalSep ++ elif defined(nimNoLibc): ++ discard # TODO + else: + const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e'] + var +diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim +index 3bcd3257b..c81c08dda 100644 +--- a/lib/system/formatfloat.nim ++++ b/lib/system/formatfloat.nim +@@ -7,14 +7,14 @@ + # distribution, for details about the copyright. + # + +-proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} ++from system/memory import nimCopyMem + + proc addCstringN(result: var string, buf: cstring; buflen: int) = + # no nimvm support needed, so it doesn't need to be fast here either + let oldLen = result.len + let newLen = oldLen + buflen + result.setLen newLen +- c_memcpy(result[oldLen].addr, buf, buflen.csize_t) ++ nimCopyMem(result[oldLen].addr, buf, buflen.csize_t) + + import dragonbox, schubfach + +@@ -30,8 +30,9 @@ proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int + result = float32ToChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' + +-proc c_sprintf(buf, frmt: cstring): cint {.header: "", +- importc: "sprintf", varargs, noSideEffect.} ++when not defined(nimNoLibc): ++ proc c_sprintf(buf, frmt: cstring): cint {.header: "", ++ importc: "sprintf", varargs, noSideEffect.} + + proc writeToBuffer(buf: var array[65, char]; value: cstring) = + var i = 0 +@@ -39,42 +40,43 @@ proc writeToBuffer(buf: var array[65, char]; value: cstring) = + buf[i] = value[i] + inc i + +-proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = +- ## This is the implementation to format floats. +- ## +- ## returns the amount of bytes written to `buf` not counting the +- ## terminating '\0' character. +- var n: int = c_sprintf(addr buf, "%.16g", value) +- var hasDot = false +- for i in 0..n-1: +- if buf[i] == ',': +- buf[i] = '.' +- hasDot = true +- elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: +- hasDot = true +- if not hasDot: +- buf[n] = '.' +- buf[n+1] = '0' +- buf[n+2] = '\0' +- result = n + 2 +- else: +- result = n +- # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' +- # of '-1.#IND' are produced. +- # We want to get rid of these here: +- if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: +- writeToBuffer(buf, "nan") +- result = 3 +- elif buf[n-1] == 'F': +- if buf[0] == '-': +- writeToBuffer(buf, "-inf") +- result = 4 ++when defined(c_sprintf): ++ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = ++ ## This is the implementation to format floats. ++ ## ++ ## returns the amount of bytes written to `buf` not counting the ++ ## terminating '\0' character. ++ var n: int = c_sprintf(addr buf, "%.16g", value) ++ var hasDot = false ++ for i in 0..n-1: ++ if buf[i] == ',': ++ buf[i] = '.' ++ hasDot = true ++ elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: ++ hasDot = true ++ if not hasDot: ++ buf[n] = '.' ++ buf[n+1] = '0' ++ buf[n+2] = '\0' ++ result = n + 2 + else: +- writeToBuffer(buf, "inf") ++ result = n ++ # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' ++ # of '-1.#IND' are produced. ++ # We want to get rid of these here: ++ if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: ++ writeToBuffer(buf, "nan") + result = 3 ++ elif buf[n-1] == 'F': ++ if buf[0] == '-': ++ writeToBuffer(buf, "-inf") ++ result = 4 ++ else: ++ writeToBuffer(buf, "inf") ++ result = 3 + + proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = +- when defined(nimPreviewFloatRoundtrip): ++ when defined(nimPreviewFloatRoundtrip) or not defined(writeFloatToBufferSprintf): + writeFloatToBufferRoundtrip(buf, value) + else: + writeFloatToBufferSprintf(buf, value) +@@ -92,7 +94,7 @@ proc addFloatSprintf*(result: var string; x: float) = + doAssert false + else: + var buffer {.noinit.}: array[65, char] +- let n = writeFloatToBufferSprintf(buffer, x) ++ let n = writeFloatToBuffer(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + + proc nimFloatToString(a: float): cstring = +diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim +index 9cf4f9e55..084e1db6b 100644 +--- a/lib/system/strmantle.nim ++++ b/lib/system/strmantle.nim +@@ -43,8 +43,9 @@ proc hashString(s: string): int {.compilerproc.} = + h = h + h shl 15 + result = cast[int](h) + +-proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. +- importc: "strtod", header: "", noSideEffect.} ++when not defined(nimNoLibc): ++ proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. ++ importc: "strtod", header: "", noSideEffect.} + + const + IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} +@@ -180,36 +181,42 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, + number = sign * integer.float * powtens[slop] * powtens[absExponent-slop] + return i - start + +- # if failed: slow path with strtod. +- var t: array[500, char] # flaviu says: 325 is the longest reasonable literal +- var ti = 0 +- let maxlen = t.high - "e+000".len # reserve enough space for exponent +- +- let endPos = i +- result = endPos - start +- i = start +- # re-parse without error checking, any error should be handled by the code above. +- if i < endPos and s[i] == '.': i.inc +- while i < endPos and s[i] in {'0'..'9','+','-'}: +- if ti < maxlen: +- t[ti] = s[i]; inc(ti) +- inc(i) +- while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point ++ when defined(c_strtod): ++ # if failed: slow path with strtod. ++ var t: array[500, char] # flaviu says: 325 is the longest reasonable literal ++ var ti = 0 ++ let maxlen = t.high - "e+000".len # reserve enough space for exponent ++ ++ let endPos = i ++ result = endPos - start ++ i = start ++ # re-parse without error checking, any error should be handled by the code above. ++ if i < endPos and s[i] == '.': i.inc ++ while i < endPos and s[i] in {'0'..'9','+','-'}: ++ if ti < maxlen: ++ t[ti] = s[i]; inc(ti) + inc(i) ++ while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point ++ inc(i) + +- # insert exponent +- t[ti] = 'E' +- inc(ti) +- t[ti] = if expNegative: '-' else: '+' +- inc(ti, 4) +- +- # insert adjusted exponent +- t[ti-1] = ('0'.ord + absExponent mod 10).char +- absExponent = absExponent div 10 +- t[ti-2] = ('0'.ord + absExponent mod 10).char +- absExponent = absExponent div 10 +- t[ti-3] = ('0'.ord + absExponent mod 10).char +- number = c_strtod(addr t, nil) ++ # insert exponent ++ t[ti] = 'E' ++ inc(ti) ++ t[ti] = if expNegative: '-' else: '+' ++ inc(ti, 4) ++ ++ # insert adjusted exponent ++ t[ti-1] = ('0'.ord + absExponent mod 10).char ++ absExponent = absExponent div 10 ++ t[ti-2] = ('0'.ord + absExponent mod 10).char ++ absExponent = absExponent div 10 ++ t[ti-3] = ('0'.ord + absExponent mod 10).char ++ number = c_strtod(addr t, nil) ++ else: ++ number = NaN ++ raise newException( ++ FloatInexactDefect, ++ "insufficent precision in platform-specific parser") + + when defined(nimHasInvariant): + {.pop.} # staticBoundChecks +-- +2.37.2 + + +From e7d190166a317d5a2d73bdee5777cff826319f61 Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Tue, 15 Feb 2022 16:28:41 -0600 +Subject: [PATCH 2/8] Massive stub-out for nimNoLibc + +--- + lib/pure/asyncfile.nim | 2 +- + lib/pure/dynlib.nim | 16 ++-- + lib/pure/os.nim | 183 +++++++++++++++++++++++---------------- + lib/pure/osproc.nim | 2 +- + lib/pure/reservedmem.nim | 2 +- + lib/pure/segfaults.nim | 2 +- + lib/pure/selectors.nim | 7 +- + lib/pure/streams.nim | 2 +- + lib/pure/terminal.nim | 2 +- + lib/pure/unittest.nim | 2 +- + lib/std/tempfiles.nim | 2 +- + lib/system/dyncalls.nim | 36 ++++---- + lib/system/gc_common.nim | 4 +- + lib/system/mmdisp.nim | 2 +- + lib/system/strmantle.nim | 2 +- + lib/system/syslocks.nim | 2 +- + 16 files changed, 153 insertions(+), 115 deletions(-) + +diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim +index 222a89b97..6c5fd8a92 100644 +--- a/lib/pure/asyncfile.nim ++++ b/lib/pure/asyncfile.nim +@@ -28,7 +28,7 @@ import asyncdispatch, os + + when defined(windows) or defined(nimdoc): + import winlean +-else: ++elif defined(posix): + import posix + + type +diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim +index 48fd91b8f..b5567a5af 100644 +--- a/lib/pure/dynlib.nim ++++ b/lib/pure/dynlib.nim +@@ -118,7 +118,7 @@ when defined(posix) and not defined(nintendoswitch): + proc unloadLib(lib: LibHandle) = discard dlclose(lib) + proc symAddr(lib: LibHandle, name: cstring): pointer = dlsym(lib, name) + +-elif defined(nintendoswitch): ++elif defined(nintendoswitch) or defined(nimNoLibc): + # + # ========================================================================= + # Nintendo switch DevkitPro sdk does not have these. Raise an error if called. +@@ -126,19 +126,19 @@ elif defined(nintendoswitch): + # + + proc dlclose(lib: LibHandle) = +- raise newException(OSError, "dlclose not implemented on Nintendo Switch!") ++ raise newException(OSError, "dlclose not implemented on this platform!") + proc dlopen(path: cstring, mode: int): LibHandle = +- raise newException(OSError, "dlopen not implemented on Nintendo Switch!") ++ raise newException(OSError, "dlopen not implemented on this platform!") + proc dlsym(lib: LibHandle, name: cstring): pointer = +- raise newException(OSError, "dlsym not implemented on Nintendo Switch!") ++ raise newException(OSError, "dlsym not implemented on this platform!") + proc loadLib(path: string, global_symbols = false): LibHandle = +- raise newException(OSError, "loadLib not implemented on Nintendo Switch!") ++ raise newException(OSError, "loadLib not implemented on this platform!") + proc loadLib(): LibHandle = +- raise newException(OSError, "loadLib not implemented on Nintendo Switch!") ++ raise newException(OSError, "loadLib not implemented on this platform!") + proc unloadLib(lib: LibHandle) = +- raise newException(OSError, "unloadLib not implemented on Nintendo Switch!") ++ raise newException(OSError, "unloadLib not implemented on this platform!") + proc symAddr(lib: LibHandle, name: cstring): pointer = +- raise newException(OSError, "symAddr not implemented on Nintendo Switch!") ++ raise newException(OSError, "symAddr not implemented on this platform!") + + elif defined(genode): + # +diff --git a/lib/pure/os.nim b/lib/pure/os.nim +index 24945095f..8674b5e05 100644 +--- a/lib/pure/os.nim ++++ b/lib/pure/os.nim +@@ -36,6 +36,9 @@ import strutils, pathnorm + + const weirdTarget = defined(nimscript) or defined(js) + ++const fileSystemMissing = not defined(nimdoc) and ( ++ defined(fileSystemMissing) or (defined(genode) and not defined(posix))) ++ + since (1, 1): + const + invalidFilenameChars* = {'/', '\\', ':', '*', '?', '"', '<', '>', '|', '^', '\0'} ## \ +@@ -1163,7 +1166,8 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", + ## See also: + ## * `dirExists proc <#dirExists,string>`_ + ## * `symlinkExists proc <#symlinkExists,string>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + wrapUnary(a, getFileAttributesW, filename) + else: +@@ -1182,7 +1186,8 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] + ## See also: + ## * `fileExists proc <#fileExists,string>`_ + ## * `symlinkExists proc <#symlinkExists,string>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + wrapUnary(a, getFileAttributesW, dir) + else: +@@ -1202,7 +1207,8 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", + ## See also: + ## * `fileExists proc <#fileExists,string>`_ + ## * `dirExists proc <#dirExists,string>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + wrapUnary(a, getFileAttributesW, link) + else: +@@ -1237,46 +1243,48 @@ proc findExe*(exe: string, followSymlinks: bool = true; + ## meets the actual file. This behavior can be disabled if desired + ## by setting `followSymlinks = false`. + +- if exe.len == 0: return +- template checkCurrentDir() = +- for ext in extensions: +- result = addFileExt(exe, ext) +- if fileExists(result): return +- when defined(posix): +- if '/' in exe: checkCurrentDir() ++ when fileSystemMissing: discard + else: +- checkCurrentDir() +- let path = getEnv("PATH") +- for candidate in split(path, PathSep): +- if candidate.len == 0: continue +- when defined(windows): +- var x = (if candidate[0] == '"' and candidate[^1] == '"': +- substr(candidate, 1, candidate.len-2) else: candidate) / +- exe ++ if exe.len == 0: return ++ template checkCurrentDir() = ++ for ext in extensions: ++ result = addFileExt(exe, ext) ++ if fileExists(result): return ++ when defined(posix): ++ if '/' in exe: checkCurrentDir() + else: +- var x = expandTilde(candidate) / exe +- for ext in extensions: +- var x = addFileExt(x, ext) +- if fileExists(x): +- when not defined(windows): +- while followSymlinks: # doubles as if here +- if x.symlinkExists: +- var r = newString(maxSymlinkLen) +- var len = readlink(x, r, maxSymlinkLen) +- if len < 0: +- raiseOSError(osLastError(), exe) +- if len > maxSymlinkLen: +- r = newString(len+1) +- len = readlink(x, r, len) +- setLen(r, len) +- if isAbsolute(r): +- x = r ++ checkCurrentDir() ++ let path = getEnv("PATH") ++ for candidate in split(path, PathSep): ++ if candidate.len == 0: continue ++ when defined(windows): ++ var x = (if candidate[0] == '"' and candidate[^1] == '"': ++ substr(candidate, 1, candidate.len-2) else: candidate) / ++ exe ++ else: ++ var x = expandTilde(candidate) / exe ++ for ext in extensions: ++ var x = addFileExt(x, ext) ++ if fileExists(x): ++ when not defined(windows): ++ while followSymlinks: # doubles as if here ++ if x.symlinkExists: ++ var r = newString(maxSymlinkLen) ++ var len = readlink(x, r, maxSymlinkLen) ++ if len < 0: ++ raiseOSError(osLastError(), exe) ++ if len > maxSymlinkLen: ++ r = newString(len+1) ++ len = readlink(x, r, len) ++ setLen(r, len) ++ if isAbsolute(r): ++ x = r ++ else: ++ x = parentDir(x) / r + else: +- x = parentDir(x) / r +- else: +- break +- return x +- result = "" ++ break ++ return x ++ result = "" + + when weirdTarget: + const times = "fake const" +@@ -1289,7 +1297,8 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", + ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ + ## * `getCreationTime proc <#getCreationTime,string>`_ + ## * `fileNewer proc <#fileNewer,string,string>`_ +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + var res: Stat + if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) + result = res.st_mtim.toTime +@@ -1307,7 +1316,8 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeir + ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ + ## * `getCreationTime proc <#getCreationTime,string>`_ + ## * `fileNewer proc <#fileNewer,string,string>`_ +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + var res: Stat + if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) + result = res.st_atim.toTime +@@ -1329,7 +1339,8 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdT + ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ + ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ + ## * `fileNewer proc <#fileNewer,string,string>`_ +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + var res: Stat + if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) + result = res.st_ctim.toTime +@@ -1348,7 +1359,8 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} = + ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ + ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ + ## * `getCreationTime proc <#getCreationTime,string>`_ +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + # If we don't have access to nanosecond resolution, use '>=' + when not StatHasNanoseconds: + result = getLastModificationTime(a) >= getLastModificationTime(b) +@@ -1371,7 +1383,8 @@ when not defined(nimscript): + ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `currentSourcePath template `_ + ## * `getProjectPath proc `_ +- when defined(nodejs): ++ when fileSystemMissing: discard ++ elif defined(nodejs): + var ret: cstring + {.emit: "`ret` = process.cwd();".} + return $ret +@@ -1428,7 +1441,8 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: + raiseOSError(osLastError(), newDir) +@@ -1565,7 +1579,8 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", + ## + ## See also: + ## * `sameFileContent proc <#sameFileContent,string,string>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + var success = true + var f1 = openHandle(path1) + var f2 = openHandle(path2) +@@ -1625,7 +1640,8 @@ proc getFilePermissions*(filename: string): set[FilePermission] {. + ## See also: + ## * `setFilePermissions proc <#setFilePermissions,string,set[FilePermission]>`_ + ## * `FilePermission enum <#FilePermission>`_ +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + var a: Stat + if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename) + result = {} +@@ -1671,7 +1687,8 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission], + ## See also: + ## * `getFilePermissions <#getFilePermissions,string>`_ + ## * `FilePermission enum <#FilePermission>`_ +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + var p = 0.Mode + if fpUserRead in permissions: p = p or S_IRUSR.Mode + if fpUserWrite in permissions: p = p or S_IWUSR.Mode +@@ -1750,7 +1767,8 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} = + ## * `createHardlink proc <#createHardlink,string,string>`_ + ## * `expandSymlink proc <#expandSymlink,string>`_ + +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2 + # allows anyone with developer mode on to create a link + let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +@@ -1773,7 +1791,8 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = + ## + ## See also: + ## * `createSymlink proc <#createSymlink,string,string>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + result = symlinkPath + else: + result = newString(maxSymlinkLen) +@@ -1950,7 +1969,8 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE + ## * `removeFile proc <#removeFile,string>`_ + ## * `moveFile proc <#moveFile,string,string>`_ + result = true +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + let f = newWideCString(file) + else: +@@ -1991,7 +2011,8 @@ proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.} + ## Returns false in case of `EXDEV` error or `AccessDeniedError` on windows (if `isDir` is true). + ## In case of other errors `OSError` is raised. + ## Returns true in case of success. +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + let s = newWideCString(source) + let d = newWideCString(dest) +@@ -2001,16 +2022,18 @@ proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.} + else: + result = c_rename(source, dest) == 0'i32 + +- if not result: +- let err = osLastError() +- let isAccessDeniedError = +- when defined(windows): +- const AccessDeniedError = OSErrorCode(5) +- isDir and err == AccessDeniedError +- else: +- err == EXDEV.OSErrorCode +- if not isAccessDeniedError: +- raiseOSError(err, $(source, dest)) ++ when fileSystemMissing: discard ++ else: ++ if not result: ++ let err = osLastError() ++ let isAccessDeniedError = ++ when defined(windows): ++ const AccessDeniedError = OSErrorCode(5) ++ isDir and err == AccessDeniedError ++ else: ++ err == EXDEV.OSErrorCode ++ if not isAccessDeniedError: ++ raiseOSError(err, $(source, dest)) + + proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", + tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} = +@@ -2093,7 +2116,8 @@ template defaultWalkFilter(item): bool = + template walkCommon(pattern: string, filter) = + ## Common code for getting the files and directories with the + ## specified `pattern` +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + var + f: WIN32_FIND_DATA + res: int +@@ -2189,7 +2213,8 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", + ## Returns the full (`absolute`:idx:) path of an existing file `filename`. + ## + ## Raises `OSError` in case of an error. Follows symlinks. +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + var bufsize = MAX_PATH.int32 + when useWinUnicode: + var unused: WideCString = nil +@@ -2302,7 +2327,8 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): + for k, v in items(staticWalkDir(dir, relative)): + yield (k, v) + else: +- when weirdTarget: ++ when fileSystemMissing: discard ++ elif weirdTarget: + for k, v in items(staticWalkDir(dir, relative)): + yield (k, v) + elif defined(windows): +@@ -2425,7 +2451,8 @@ iterator walkDirRec*(dir: string, + # Future work can provide a way to customize this and do error reporting. + + proc rawRemoveDir(dir: string) {.noWeirdTarget.} = +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + wrapUnary(res, removeDirectoryW, dir) + else: +@@ -2468,7 +2495,8 @@ proc rawCreateDir(dir: string): bool {.noWeirdTarget.} = + # + # This is a thin wrapper over mkDir (or alternatives on other systems), + # so in case of a pre-existing path we don't check that it is a directory. +- when defined(solaris): ++ when fileSystemMissing: discard ++ elif defined(solaris): + let res = mkdir(dir, 0o777) + if res == 0'i32: + result = true +@@ -2618,7 +2646,8 @@ proc createHardlink*(src, dest: string) {.noWeirdTarget.} = + ## + ## See also: + ## * `createSymlink proc <#createSymlink,string,string>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + when useWinUnicode: + var wSrc = newWideCString(src) + var wDst = newWideCString(dest) +@@ -3217,7 +3246,8 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", + tags: [ReadIOEffect], noWeirdTarget.} = + ## Returns the file size of `file` (in bytes). ``OSError`` is + ## raised in case of an error. +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + var a: WIN32_FIND_DATA + var resA = findFirstFile(file, a) + if resA == -1: raiseOSError(osLastError(), file) +@@ -3234,6 +3264,10 @@ when defined(windows) or weirdTarget: + type + DeviceId* = int32 + FileId* = int64 ++elif fileSystemMissing: ++ type ++ DeviceId* = int ++ FileId* = int + else: + type + DeviceId* = Dev +@@ -3344,7 +3378,8 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noWeirdTarget.} = + ## * `getFileInfo(path) proc <#getFileInfo,string>`_ + + # Done: ID, Kind, Size, Permissions, Link Count +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + var rawInfo: BY_HANDLE_FILE_INFORMATION + # We have to use the super special '_get_osfhandle' call (wrapped above) + # To transform the C file descriptor to a native file handle. +@@ -3386,7 +3421,8 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget. + ## See also: + ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ + ## * `getFileInfo(file) proc <#getFileInfo,File>`_ +- when defined(windows): ++ when fileSystemMissing: discard ++ elif defined(windows): + var + handle = openHandle(path, followSymlink) + rawInfo: BY_HANDLE_FILE_INFORMATION +@@ -3484,7 +3520,8 @@ proc getCurrentProcessId*(): int {.noWeirdTarget.} = + proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} = + ## Sets the `file`'s last modification time. `OSError` is raised in case of + ## an error. +- when defined(posix): ++ when fileSystemMissing: discard ++ elif defined(posix): + let unixt = posix.Time(t.toUnix) + let micro = convert(Nanoseconds, Microseconds, t.nanosecond) + var timevals = [Timeval(tv_sec: unixt, tv_usec: micro), +diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim +index 6a0ac9a8b..0c9b5b427 100644 +--- a/lib/pure/osproc.nim ++++ b/lib/pure/osproc.nim +@@ -25,7 +25,7 @@ export quoteShell, quoteShellWindows, quoteShellPosix + + when defined(windows): + import winlean +-else: ++elif defined(posix): + import posix + + when defined(linux) and defined(useClone): +diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim +index 232a2b383..55e194dcb 100644 +--- a/lib/pure/reservedmem.nim ++++ b/lib/pure/reservedmem.nim +@@ -79,7 +79,7 @@ when defined(windows): + if r == cast[typeof(r)](0): + raiseOSError(osLastError()) + +-else: ++elif defined(posix): + import posix + + let allocationGranularity = sysconf(SC_PAGESIZE) +diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim +index b0eac2299..b1aec8265 100644 +--- a/lib/pure/segfaults.nim ++++ b/lib/pure/segfaults.nim +@@ -64,7 +64,7 @@ when defined(windows): + {.pop.} + c_signal(SIGSEGV, segfaultHandler) + +-else: ++elif defined(posix): + import posix + + var sa: Sigaction +diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim +index ec441f6da..21bef9187 100644 +--- a/lib/pure/selectors.nim ++++ b/lib/pure/selectors.nim +@@ -27,7 +27,8 @@ + ## + ## TODO: `/dev/poll`, `event ports` and filesystem events. + +-import os, nativesockets ++when not defined(nimNoLibc): ++ import os, nativesockets + + const hasThreadSupport = compileOption("threads") and defined(threadsafe) + +@@ -232,7 +233,7 @@ when defined(nimdoc): + ## + ## For *poll* and *select* selectors `-1` is returned. + +-else: ++elif not defined(nimNoLibc): + import strutils + when hasThreadSupport: + import locks +@@ -285,7 +286,7 @@ else: + proc setNonBlocking(fd: cint) {.inline.} = + setBlocking(fd.SocketHandle, false) + +- when not defined(windows): ++ when not defined(windows) and defined(posix): + import posix + + template setKey(s, pident, pevents, pparam, pdata: untyped) = +diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim +index 99fe7e073..cf60c8f70 100644 +--- a/lib/pure/streams.nim ++++ b/lib/pure/streams.nim +@@ -1467,7 +1467,7 @@ when false: + when defined(windows): + # do not import windows as this increases compile times: + discard +- else: ++ elif defined(posix): + import posix + + proc hsSetPosition(s: FileHandleStream, pos: int) = +diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim +index 25b1a4cdd..03b79bbee 100644 +--- a/lib/pure/terminal.nim ++++ b/lib/pure/terminal.nim +@@ -247,7 +247,7 @@ when defined(windows): + let term = getTerminal() + if f == stderr: term.hStderr else: term.hStdout + +-else: ++elif defined(posix): + import termios, posix, os, parseutils + + proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) = +diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim +index b1c01f8db..c715e12c3 100644 +--- a/lib/pure/unittest.nim ++++ b/lib/pure/unittest.nim +@@ -113,7 +113,7 @@ import macros, strutils, streams, times, sets, sequtils + when declared(stdout): + import os + +-const useTerminal = not defined(js) ++const useTerminal = not (defined(js) or defined(nimNoLibc)) + + when useTerminal: + import terminal +diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim +index ce84c2a37..49b8f766e 100644 +--- a/lib/std/tempfiles.nim ++++ b/lib/std/tempfiles.nim +@@ -41,7 +41,7 @@ when defined(windows): + + proc close_osfandle(fd: cint): cint {. + importc: "_close", header: "".} +-else: ++elif defined(posix): + import posix + + proc c_fdopen( +diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim +index 36c2c5fe1..39eb24fb0 100644 +--- a/lib/system/dyncalls.nim ++++ b/lib/system/dyncalls.nim +@@ -20,15 +20,15 @@ const + proc nimLoadLibraryError(path: string) = + # carefully written to avoid memory allocation: + const prefix = "could not load: " +- cstderr.rawWrite(prefix) +- cstderr.rawWrite(path) ++ writeToStdErr(prefix) ++ writeToStdErr(path) + when not defined(nimDebugDlOpen) and not defined(windows): +- cstderr.rawWrite("\n(compile with -d:nimDebugDlOpen for more information)") ++ writeToStdErr("\n(compile with -d:nimDebugDlOpen for more information)") + when defined(windows): + const badExe = "\n(bad format; library may be wrong architecture)" + let loadError = GetLastError() + if loadError == ERROR_BAD_EXE_FORMAT: +- cstderr.rawWrite(badExe) ++ writeToStdErr(badExe) + when defined(guiapp): + # Because console output is not shown in GUI apps, display the error as a + # message box instead: +@@ -46,14 +46,14 @@ proc nimLoadLibraryError(path: string) = + if loadError == ERROR_BAD_EXE_FORMAT and msgLeft >= badExe.len: + copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len) + discard MessageBoxA(nil, msg[0].addr, nil, 0) +- cstderr.rawWrite("\n") ++ writeToStdErr("\n") + quit(1) + + proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} = + # carefully written to avoid memory allocation: +- cstderr.rawWrite("could not import: ") +- cstderr.rawWrite(name) +- cstderr.rawWrite("\n") ++ writeToStdErr("could not import: ") ++ writeToStdErr(name) ++ writeToStdErr("\n") + quit(1) + + # this code was inspired from Lua's source code: +@@ -98,8 +98,8 @@ when defined(posix): + when defined(nimDebugDlOpen): + let error = dlerror() + if error != nil: +- cstderr.rawWrite(error) +- cstderr.rawWrite("\n") ++ writeToStdErr(error) ++ writeToStdErr("\n") + + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = + result = dlsym(lib, name) +@@ -176,22 +176,22 @@ elif defined(genode): + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = + raiseAssert("nimGetProcAddr not implemented") + +-elif defined(nintendoswitch) or defined(freertos) or defined(zephyr): ++elif defined(nintendoswitch) or defined(freertos) or defined(zephyr) or defined(nimNoLibc): + proc nimUnloadLibrary(lib: LibHandle) = +- cstderr.rawWrite("nimUnLoadLibrary not implemented") +- cstderr.rawWrite("\n") ++ writeToStdErr("nimUnLoadLibrary not implemented") ++ writeToStdErr("\n") + quit(1) + + proc nimLoadLibrary(path: string): LibHandle = +- cstderr.rawWrite("nimLoadLibrary not implemented") +- cstderr.rawWrite("\n") ++ writeToStdErr("nimLoadLibrary not implemented") ++ writeToStdErr("\n") + quit(1) + + + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = +- cstderr.rawWrite("nimGetProAddr not implemented") +- cstderr.rawWrite(name) +- cstderr.rawWrite("\n") ++ writeToStdErr("nimGetProAddr not implemented") ++ writeToStdErr(name) ++ writeToStdErr("\n") + quit(1) + + else: +diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim +index ea8857ece..4fff6d50f 100644 +--- a/lib/system/gc_common.nim ++++ b/lib/system/gc_common.nim +@@ -471,7 +471,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = + globalMarkers[globalMarkersLen] = markerProc + inc globalMarkersLen + else: +- cstderr.rawWrite("[GC] cannot register global variable; too many global variables") ++ writeToStdErr("[GC] cannot register global variable; too many global variables") + quit 1 + + proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = +@@ -479,5 +479,5 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} + threadLocalMarkers[threadLocalMarkersLen] = markerProc + inc threadLocalMarkersLen + else: +- cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables") ++ writeToStdErr("[GC] cannot register thread local variable; too many thread local variables") + quit 1 +diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim +index 3317ba627..422618c0f 100644 +--- a/lib/system/mmdisp.nim ++++ b/lib/system/mmdisp.nim +@@ -45,7 +45,7 @@ else: + + proc raiseOutOfMem() {.noinline.} = + if outOfMemHook != nil: outOfMemHook() +- cstderr.rawWrite("out of memory\n") ++ writeToStdErr("out of memory\n") + quit(1) + + when defined(boehmgc): +diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim +index 084e1db6b..2521fab9f 100644 +--- a/lib/system/strmantle.nim ++++ b/lib/system/strmantle.nim +@@ -181,7 +181,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, + number = sign * integer.float * powtens[slop] * powtens[absExponent-slop] + return i - start + +- when defined(c_strtod): ++ when not defined(nimNoLibc): + # if failed: slow path with strtod. + var t: array[500, char] # flaviu says: 325 is the longest reasonable literal + var ti = 0 +diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim +index 2f0c8b0ba..0b0324def 100644 +--- a/lib/system/syslocks.nim ++++ b/lib/system/syslocks.nim +@@ -97,7 +97,7 @@ elif defined(genode): + proc broadcastSysCond(cond: var SysCond) {. + noSideEffect, importcpp.} + +-else: ++elif defined(posix): + type + SysLockObj {.importc: "pthread_mutex_t", pure, final, + header: """#include +-- +2.37.2 + + +From 11e259afe417e39fda794c83304cfd815da2dfd6 Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Thu, 26 May 2022 12:52:54 -0500 +Subject: [PATCH 3/8] times: Genode native time + +--- + lib/genode/constructibles.nim | 9 +++++++++ + lib/genode/env.nim | 4 ++-- + lib/genode/rtc.nim | 21 +++++++++++++++++++++ + lib/pure/os.nim | 2 ++ + lib/pure/times.nim | 34 +++++++++++++++++++++++++++++++--- + 5 files changed, 65 insertions(+), 5 deletions(-) + create mode 100644 lib/genode/constructibles.nim + create mode 100644 lib/genode/rtc.nim + +diff --git a/lib/genode/constructibles.nim b/lib/genode/constructibles.nim +new file mode 100644 +index 000000000..8bf6f5162 +--- /dev/null ++++ b/lib/genode/constructibles.nim +@@ -0,0 +1,9 @@ ++type Constructible*[T] {. ++ importcpp: "Genode::Constructible", ++ header: "", byref, pure.} = object ++ ++proc construct*[T](x: Constructible[T]) {.importcpp.} ++ ## Construct a constructible C++ object. ++ ++proc destruct*[T](x: Constructible[T]) {.importcpp.} ++ ## Destruct a constructible C++ object. +diff --git a/lib/genode/env.nim b/lib/genode/env.nim +index ef4a25883..0c2f35a22 100644 +--- a/lib/genode/env.nim ++++ b/lib/genode/env.nim +@@ -21,9 +21,9 @@ when not defined(genode): + + type + GenodeEnvObj {.importcpp: "Genode::Env", header: "", pure.} = object +- GenodeEnvPtr = ptr GenodeEnvObj ++ GenodeEnvPtr* = ptr GenodeEnvObj + + const runtimeEnvSym = "nim_runtime_env" + + when not defined(nimscript): +- var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr ++ var runtimeEnv* {.importcpp: runtimeEnvSym.}: GenodeEnvPtr +diff --git a/lib/genode/rtc.nim b/lib/genode/rtc.nim +new file mode 100644 +index 000000000..d2f03454f +--- /dev/null ++++ b/lib/genode/rtc.nim +@@ -0,0 +1,21 @@ ++import ./env, ./constructibles ++ ++const rtcHeader = "" ++ ++type ++ ConnectionBase {.importcpp: "Rtc::Connection", header: rtcHeader.} = object ++ Connection = Constructible[ConnectionBase] ++ ++ Timestamp* {.importcpp: "Rtc::Timestamp", header: rtcHeader.} = object ++ microsecond*, second*, minute*, hour*, day*, month*, year*: cuint ++ ++proc construct(c: Connection; env: GenodeEnvPtr; label: cstring) {. ++ importcpp: "#.construct(*#, @)", tags: [IOEffect].} ++ ++proc timestamp*(c: Connection): Timestamp {. ++ importcpp: "#->current_time()".} ++ # There is an RPC call here but its not listed effect. ++ ++proc initRtcConnection*(env: GenodeEnvPtr; label = ""): Connection = ++ ## Open a new **RTC** connection. ++ result.construct(env, label) +diff --git a/lib/pure/os.nim b/lib/pure/os.nim +index 8674b5e05..a1f9c8451 100644 +--- a/lib/pure/os.nim ++++ b/lib/pure/os.nim +@@ -62,6 +62,8 @@ elif defined(posix): + + proc toTime(ts: Timespec): times.Time {.inline.} = + result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) ++elif defined(genode): ++ import times + else: + {.error: "OS module not ported to your operating system!".} + +diff --git a/lib/pure/times.nim b/lib/pure/times.nim +index 113f73d2a..a18c9be4f 100644 +--- a/lib/pure/times.nim ++++ b/lib/pure/times.nim +@@ -252,6 +252,11 @@ elif defined(windows): + + proc localtime(a1: var CTime): ptr Tm {.importc, header: "", sideEffect.} + ++elif defined(genode): ++ # Genode without POSIX ++ import ../genode/[env, rtc] ++ var rtcConn = initRtcConnection(runtimeEnv, "times") ++ + type + Month* = enum ## Represents a month. Note that the enum starts at `1`, + ## so `ord(month)` will give the month number in the +@@ -894,6 +899,9 @@ proc toWinTime*(t: Time): int64 = + ## since `1601-01-01T00:00:00Z`). + result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100 + ++when defined(genode): ++ proc `+=`*(t: var Time, b: TimeInterval) {.tags: [], gcsafe.} ++ + proc getTime*(): Time {.tags: [TimeEffect], benign.} = + ## Gets the current time as a `Time` with up to nanosecond resolution. + when defined(js): +@@ -915,6 +923,16 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = + var f {.noinit.}: FILETIME + getSystemTimeAsFileTime(f) + result = fromWinTime(rdFileTime(f)) ++ elif defined(genode): ++ var ts = rtcConn.timestamp() ++ result += TimeInterval( ++ microseconds: int ts.microsecond, ++ seconds: int ts.second, ++ minutes: int ts.minute, ++ hours: int ts.hour, ++ days: int ts.day, ++ months: int ts.month, ++ years: int ts.year) + + proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = + ## Computes the duration between two points in time. +@@ -1222,7 +1240,7 @@ when defined(js): + result.time = adjTime + initDuration(seconds = result.utcOffset) + result.isDst = false + +-else: ++elif defined(posix): + proc toAdjUnix(tm: Tm): int64 = + let epochDay = toEpochDay(tm.tm_mday, (tm.tm_mon + 1).Month, + tm.tm_year.int + 1900) +@@ -1284,6 +1302,13 @@ else: + result.utcOffset = finalOffset + result.isDst = dst + ++elif defined(genode): ++ proc localZonedTimeFromTime(time: Time): ZonedTime {.benign.} = ++ result.time = time ++ ++ proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.benign.} = ++ result.time = adjTime ++ + proc utcTzInfo(time: Time): ZonedTime = + ZonedTime(utcOffset: 0, isDst: false, time: time) + +@@ -1877,9 +1902,9 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, + else: + result = false + of yy: +- # Assumes current century ++ # Assumes 21st century + var year = takeInt(2..2) +- var thisCen = now().year div 100 ++ var thisCen = 20 + parsed.year = some(thisCen*100 + year) + result = year > 0 + of yyyy: +@@ -2608,6 +2633,9 @@ proc epochTime*(): float {.tags: [TimeEffect].} = + var secs = i64 div rateDiff + var subsecs = i64 mod rateDiff + result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001 ++ elif defined(genode): ++ var t = now().toTime ++ result = t.seconds.toBiggestFloat + t.nanosecond.toBiggestFloat * 0.000_000_000_1 + elif defined(js): + result = newDate().getTime() / 1000 + else: +-- +2.37.2 + + +From 7f3934aba501406c95d69ce271e5456c426f0ba6 Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Thu, 26 May 2022 13:05:06 -0500 +Subject: [PATCH 4/8] os: add support for Genode without POSIX + +--- + lib/pure/os.nim | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/lib/pure/os.nim b/lib/pure/os.nim +index a1f9c8451..67d481e13 100644 +--- a/lib/pure/os.nim ++++ b/lib/pure/os.nim +@@ -1755,6 +1755,8 @@ proc isAdmin*: bool {.noWeirdTarget.} = + if freeSid(administratorsGroup) != nil: + raiseOSError(osLastError(), "failed to free SID for Administrators group") + ++ elif defined(genode): # non-POSIX ++ discard + else: + result = geteuid() == 0 + +@@ -3238,11 +3240,15 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTar + ## Sleeps `milsecs` milliseconds. + when defined(windows): + winlean.sleep(int32(milsecs)) +- else: ++ elif defined(posix): + var a, b: Timespec + a.tv_sec = posix.Time(milsecs div 1000) + a.tv_nsec = (milsecs mod 1000) * 1000 * 1000 + discard posix.nanosleep(a, b) ++ elif defined(genode): ++ raise newException(Defect, "blocking sleep not available on Genode") ++ else: ++ {.error: "sleep not ported to your operating system!".} + + proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", + tags: [ReadIOEffect], noWeirdTarget.} = +@@ -3266,14 +3272,14 @@ when defined(windows) or weirdTarget: + type + DeviceId* = int32 + FileId* = int64 +-elif fileSystemMissing: +- type +- DeviceId* = int +- FileId* = int +-else: ++elif defined(posix): + type + DeviceId* = Dev + FileId* = Ino ++else: ++ type ++ DeviceId* = int ++ FileId* = int + + type + FileInfo* = object +@@ -3516,6 +3522,8 @@ proc getCurrentProcessId*(): int {.noWeirdTarget.} = + proc GetCurrentProcessId(): DWORD {.stdcall, dynlib: "kernel32", + importc: "GetCurrentProcessId".} + result = GetCurrentProcessId().int ++ elif defined(genode): # non-POSIX ++ discard + else: + result = getpid() + +-- +2.37.2 + + +From df882bfdc80e35a09c6e5c8517eda9d54e91a0c4 Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Tue, 11 Oct 2022 14:31:21 -0500 +Subject: [PATCH 5/8] Genode native modules + +--- + lib/genode/entrypoints.nim | 10 ++++ + lib/genode/rtc.nim | 4 +- + lib/genode/signals.nim | 93 ++++++++++++++++++++++++++++++++++++++ + lib/genode/timers.nim | 13 ++++++ + lib/pure/times.nim | 2 +- + 5 files changed, 119 insertions(+), 3 deletions(-) + create mode 100644 lib/genode/entrypoints.nim + create mode 100644 lib/genode/signals.nim + create mode 100644 lib/genode/timers.nim + +diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim +new file mode 100644 +index 000000000..360aa274d +--- /dev/null ++++ b/lib/genode/entrypoints.nim +@@ -0,0 +1,10 @@ ++type ++ EntrypointObj {. ++ importcpp: "Genode::Entrypoint", ++ header: "", ++ pure.} = object ++ Entrypoint* = ptr EntrypointObj ++ ## Opaque Entrypoint object. ++ ++proc ep*(env: GenodeEnvPtr): Entrypoint {. ++ importcpp: "(&#->ep())".} +diff --git a/lib/genode/rtc.nim b/lib/genode/rtc.nim +index d2f03454f..00a13049b 100644 +--- a/lib/genode/rtc.nim ++++ b/lib/genode/rtc.nim +@@ -1,4 +1,4 @@ +-import ./env, ./constructibles ++import ./constructibles + + const rtcHeader = "" + +@@ -9,7 +9,7 @@ type + Timestamp* {.importcpp: "Rtc::Timestamp", header: rtcHeader.} = object + microsecond*, second*, minute*, hour*, day*, month*, year*: cuint + +-proc construct(c: Connection; env: GenodeEnvPtr; label: cstring) {. ++proc construct(c: Connection; env: GenodeEnv; label: cstring) {. + importcpp: "#.construct(*#, @)", tags: [IOEffect].} + + proc timestamp*(c: Connection): Timestamp {. +diff --git a/lib/genode/signals.nim b/lib/genode/signals.nim +new file mode 100644 +index 000000000..0e59db59c +--- /dev/null ++++ b/lib/genode/signals.nim +@@ -0,0 +1,93 @@ ++import ./entrypoints, ./constructibles ++ ++type SignalContextCapability* {. ++ importcpp: "Genode::Signal_context_capability", ++ header: "", pure.} = object ++ ## Capability to an asynchronous signal context. ++ ++proc isValid*(cap: SignalContextCapability): bool {. ++ importcpp: "#.valid()", tags: [IOEffect].} ++ ++{.emit: """ ++#include ++#include ++#include ++ ++/* Symbol for calling back into Nim */ ++extern "C" void nimHandleSignal(void *arg); ++ ++namespace Nim { struct SignalDispatcher; } ++ ++struct Nim::SignalDispatcher ++{ ++ /** ++ * Pointer to a Nim type ++ */ ++ void *arg; ++ ++ /** ++ * Call Nim with dispatcher argument ++ */ ++ void handle_signal() { ++ Libc::with_libc([this] () { nimHandleSignal(arg); }); } ++ ++ Genode::Signal_handler handler; ++ ++ SignalDispatcher(Genode::Entrypoint *ep, void *arg) ++ : arg(arg), handler(*ep, *this, &SignalDispatcher::handle_signal) { } ++ ++ Genode::Signal_context_capability cap() { ++ return handler; } ++}; ++ ++""".} ++ ++ ++type ++ HandlerProc = proc () {.closure, gcsafe.} ++ ++ SignalDispatcherBase {. ++ importcpp: "Nim::SignalDispatcher", pure.} = object ++ ++ SignalDispatcherCpp = Constructible[SignalDispatcherBase] ++ ++ SignalDispatcherObj = object ++ cpp: SignalDispatcherCpp ++ cb: HandlerProc ++ ## Signal handling procedure called during dispatch. ++ ++ SignalHandler* = ref SignalDispatcherObj ++ ## Nim object enclosing a Genode signal handler. ++ ++{.deprecated: [SignalDispatcher: SignalHandler].} ++ ++proc construct(cpp: SignalDispatcherCpp; ep: Entrypoint; sh: SignalHandler) {.importcpp.} ++ ++proc cap(cpp: SignalDispatcherCpp): SignalContextCapability {. ++ importcpp: "#->cap()".} ++ ++proc newSignalHandler*(ep: Entrypoint; cb: HandlerProc): SignalHandler = ++ ## Create a new signal handler. A label is recommended for ++ ## debugging purposes. A signal handler will not be garbage ++ ## collected until after it has been dissolved. ++ assert(not cb.isNil) ++ result = SignalHandler(cb: cb) ++ result.cpp.construct(ep, result) ++ GCref result ++ assert(not result.cb.isNil) ++ ++proc dissolve*(sig: SignalHandler) = ++ ## Dissolve signal dispatcher from entrypoint. ++ destruct sig.cpp ++ GCunref sig ++ ++proc cap*(sig: SignalHandler): SignalContextCapability = ++ ## Signal context capability. Can be delegated to external components. ++ assert(not sig.cb.isNil) ++ sig.cpp.cap ++ ++proc nimHandleSignal(p: pointer) {.exportc.} = ++ ## C symbol invoked by entrypoint during signal dispatch. ++ let dispatch = cast[SignalDispatcher](p) ++ doAssert(not dispatch.cb.isNil) ++ dispatch.cb() +diff --git a/lib/genode/timers.nim b/lib/genode/timers.nim +new file mode 100644 +index 000000000..d74643dde +--- /dev/null ++++ b/lib/genode/timers.nim +@@ -0,0 +1,13 @@ ++import ./constructibles, ./signals ++ ++const timerHeader = "" ++ ++type ++ ConnectionBase {.importcpp: "Timer::Connection", header: timerHeader.} = object ++ TimerConnection* = Constructible[ConnectionBase] ++ ++proc construct*(conn: TimerConnection; env: GenodeEnvPtr; label: cstring) {. ++ importcpp: "#.construct(*#, @)", tags: [IOEffect].} ++ ++proc sigh*(conn: TimerConnection; sigCap: SignalContextCapability) {. ++ importcpp: "#->sigh(#)", tags: [IOEffect].} +diff --git a/lib/pure/times.nim b/lib/pure/times.nim +index a18c9be4f..33074cf0c 100644 +--- a/lib/pure/times.nim ++++ b/lib/pure/times.nim +@@ -254,7 +254,7 @@ elif defined(windows): + + elif defined(genode): + # Genode without POSIX +- import ../genode/[env, rtc] ++ import ../genode/rtc + var rtcConn = initRtcConnection(runtimeEnv, "times") + + type +-- +2.37.2 + + +From 8a6ded41fffe06befdd7236b8c87ebb770172239 Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Wed, 12 Oct 2022 16:35:57 -0500 +Subject: [PATCH 6/8] asyncdispatch: milliseconds to Duration + +Use the Duration type to avoid mismatches with schedulers that use +units of time other than the millisecond. +--- + lib/pure/asyncdispatch.nim | 70 ++++++++++++++++++++++---------------- + 1 file changed, 40 insertions(+), 30 deletions(-) + +diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim +index 04dbf4e4a..3d0e374ff 100644 +--- a/lib/pure/asyncdispatch.nim ++++ b/lib/pure/asyncdispatch.nim +@@ -213,7 +213,7 @@ type + + proc processTimers( + p: PDispatcherBase, didSomeWork: var bool +-): Option[int] {.inline.} = ++): Option[Duration] {.inline.} = + # Pop the timers in the order in which they will expire (smaller `finishAt`). + var count = p.timers.len + let t = getMonoTime() +@@ -222,11 +222,11 @@ proc processTimers( + dec count + didSomeWork = true + +- # Return the number of milliseconds in which the next timer will expire. ++ # Return the duration in which the next timer will expire. + if p.timers.len == 0: return + +- let millisecs = (p.timers[0].finishAt - getMonoTime()).inMilliseconds +- return some(millisecs.int + 1) ++ let timeout = p.timers[0].finishAt - getMonoTime() ++ return some(timeout) + + proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) = + while p.callbacks.len > 0: +@@ -235,16 +235,17 @@ proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) = + didSomeWork = true + + proc adjustTimeout( +- p: PDispatcherBase, pollTimeout: int, nextTimer: Option[int] +-): int {.inline.} = +- if p.callbacks.len != 0: +- return 0 ++ p: PDispatcherBase, pollTimeout: Duration, nextTimer: Option[Duration] ++): Duration {.inline.} = ++ if p.callbacks.len == 0: ++ if nextTimer.isNone() or pollTimeout < result: ++ return pollTimeout + +- if nextTimer.isNone() or pollTimeout == -1: +- return pollTimeout ++ result = max(nextTimer.get(), Duration()) ++ result = min(pollTimeout, result) + +- result = max(nextTimer.get(), 0) +- result = min(pollTimeout, result) ++proc roundToMilliseconds(dur: Duration): int64 {.used.} = ++ inMilliseconds(dur + initDuration(microseconds = 500)) + + proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.} + ## Schedule `cbproc` to be called as soon as possible. +@@ -355,7 +356,7 @@ when defined(windows) or defined(nimdoc): + let p = getGlobalDispatcher() + p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0 + +- proc runOnce(timeout = 500): bool = ++ proc runOnce(timeout: Duration): bool = + let p = getGlobalDispatcher() + if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0: + raise newException(ValueError, +@@ -365,8 +366,8 @@ when defined(windows) or defined(nimdoc): + let nextTimer = processTimers(p, result) + let at = adjustTimeout(p, timeout, nextTimer) + var llTimeout = +- if at == -1: winlean.INFINITE +- else: at.int32 ++ if at < Duration(): winlean.INFINITE ++ else: at.roundToMilliseconds.int32 + + var lpNumberOfBytesTransferred: DWORD + var lpCompletionKey: ULONG_PTR +@@ -1337,7 +1338,7 @@ else: + ) + + +- proc runOnce(timeout = 500): bool = ++ proc runOnce(timeout: Duration): bool = + let p = getGlobalDispatcher() + if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0: + raise newException(ValueError, +@@ -1347,7 +1348,7 @@ else: + var keys: array[64, ReadyKey] + let nextTimer = processTimers(p, result) + var count = +- p.selector.selectInto(adjustTimeout(p, timeout, nextTimer), keys) ++ p.selector.selectInto(adjustTimeout(p, timeout, nextTimer).roundToMilliseconds.int, keys) + for i in 0.. +Date: Wed, 12 Oct 2022 15:42:31 -0500 +Subject: [PATCH 7/8] Genode: native (non-POSIX) asyndispatch + +--- + lib/genode/entrypoints.nim | 4 + + lib/genode/timers.nim | 21 +++ + lib/genode_cpp/timeouts.h | 44 +++++ + lib/pure/asyncdispatch.nim | 366 ++++++++++++++++++++++--------------- + lib/pure/selectors.nim | 1 + + 5 files changed, 292 insertions(+), 144 deletions(-) + create mode 100644 lib/genode_cpp/timeouts.h + +diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim +index 360aa274d..cdd90d083 100644 +--- a/lib/genode/entrypoints.nim ++++ b/lib/genode/entrypoints.nim +@@ -8,3 +8,7 @@ type + + proc ep*(env: GenodeEnvPtr): Entrypoint {. + importcpp: "(&#->ep())".} ++ ++proc wait_and_dispatch_one_io_signal*(ep: Entrypoint) {.importcpp.} ++ ++proc dispatch_pending_io_signal*(ep: Entrypoint): bool {.importcpp.} +diff --git a/lib/genode/timers.nim b/lib/genode/timers.nim +index d74643dde..0922a4d2b 100644 +--- a/lib/genode/timers.nim ++++ b/lib/genode/timers.nim +@@ -1,4 +1,5 @@ + import ./constructibles, ./signals ++from times import Duration + + const timerHeader = "" + +@@ -11,3 +12,23 @@ proc construct*(conn: TimerConnection; env: GenodeEnvPtr; label: cstring) {. + + proc sigh*(conn: TimerConnection; sigCap: SignalContextCapability) {. + importcpp: "#->sigh(#)", tags: [IOEffect].} ++ ++type ++ TimeoutCallback*[T] = proc (state: T; us: uint64) {.cdecl.} ++ ## Callback type that accepts at state parameter and the elapsed timout duration in microseconds. ++ TimeoutHandler*[T] {. ++ importcpp: "Nim::Constructible_timeout_handler", ++ header: "genode_cpp/timeouts.h" ++ .} = object ++ ++proc construct*[T](handler: TimeoutHandler[T]; timer: TimerConnection; state: T; cb: TimeoutCallback) {. ++ importcpp: "#.construct(*#, @)".} ++ ## Construct a `TimeoutHandler` with a `TimerConnection` and a callback. ++ ++proc schedule_us[T](handler: TimeoutHandler[T]; timeout: uint64) {.importcpp.} ++ ++proc schedule*[T](handler: TimeoutHandler[T]; timeout: Duration) {.inline.} = ++ ## Schedule the handler to be invoked after a timeout. ++ let us = timeout.inMicroseconds ++ assert(us > 0, "Timeout cannot be less than 1 µs") ++ schedule_us(handler, uint64 us) +diff --git a/lib/genode_cpp/timeouts.h b/lib/genode_cpp/timeouts.h +new file mode 100644 +index 000000000..1af4ca48d +--- /dev/null ++++ b/lib/genode_cpp/timeouts.h +@@ -0,0 +1,44 @@ ++/* ++ * ++ * Nim's Runtime Library ++ * (c) Copyright 2022 Emery Hemingway ++ * ++ * See the file "copying.txt", included in this ++ * distribution, for details about the copyright. ++ * ++ */ ++ ++#ifndef _GENODE_CPP__TIMEOUTS_H_ ++#define _GENODE_CPP__TIMEOUTS_H_ ++ ++/* Genode includes */ ++#include ++ ++namespace Nim { ++ ++ /** ++ * Class for calling a Nim callback from a Genode timer. ++ */ ++ template ++ struct Timeout_handler ++ { ++ Timer::One_shot_timeout _timeout; ++ STATE _state; ++ void(*)(STATE, uint64_t) _callback; ++ ++ Timeout_handler(Timer::Connection *timer, STATE _state, void(*callback)(STATE, uint64_t)) ++ : _timeout(*timer, *this, &Timeout_handler::_handle_timeout) ++ , _state(state), _callback(callback) ++ { } ++ ++ void _handle_timeout(Duration dur) { _callback(_state, dur.trunc_to_plain_us().value); } ++ ++ void schedule_us(uint64_t us) { schedule(Duration(Microseconds(us))); } ++ }; ++ ++ template ++ typedef Constructible> Nim::Constructible_timeout_handler ++ ++} ++ ++#endif +diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim +index 3d0e374ff..2e1d7b4f8 100644 +--- a/lib/pure/asyncdispatch.nim ++++ b/lib/pure/asyncdispatch.nim +@@ -194,13 +194,18 @@ + ## ``none`` can be used when a library supports both a synchronous and + ## asynchronous API, to disable the latter. + +-import os, tables, strutils, times, heapqueue, options, asyncstreams ++import os, tables, strutils, times, heapqueue, options, asyncstreams, deques + import options, math, std/monotimes + import asyncfutures except callSoon + +-import nativesockets, net, deques ++const socketsMissing = defined(genode) and not defined(posix) ++when not socketsMissing: ++ import nativesockets, net ++ ++when not socketsMissing: ++ export Port, SocketFlag ++ + +-export Port, SocketFlag + export asyncfutures except callSoon + export asyncstreams + +@@ -1129,6 +1134,75 @@ when defined(windows) or defined(nimdoc): + ev.hWaiter = pcd.waitFd + + initAll() ++elif defined(genode) and not defined(posix): ++ import genode/[entrypoints, timers] ++ type PDispatcher* = ref object of PDispatcherBase ++ timer: TimerConnection ++ timeoutHandler: TimeoutHandler[PDispatcher] ++ progress: bool ++ ++ proc asyncdispatch_timeout_callback(disp: PDispatcher; us: uint64) {.cdecl.} = ++ let nextTimer = processTimers(disp, disp.progress) ++ if nextTimer.isSome: ++ disp.timeoutHandler.schedule(get(nextTimer)) ++ processPendingCallbacks(disp, disp.progress) ++ ++ var gDisp: owned PDispatcher ## Global dispatcher ++ ++ proc getGlobalDispatcher*(): PDispatcher = ++ if gDisp.isNil: ++ new gDisp ++ construct(gDisp.timer, runtimeEnv, "asyncdispatch") ++ construct(gDisp.timeoutHandler, gDisp.timer, gDisp, asyncdispatch_timeout_callback) ++ gDisp ++ ++ proc hasPendingOperations*(): bool = ++ var p = getGlobalDispatcher() ++ p.timers.len != 0 or p.callbacks.len != 0 ++ ++ proc runOnce(timeout: Duration): bool = ++ var disp = getGlobalDispatcher() ++ if disp.timers.len == 0 and disp.callbacks.len == 0: ++ raise newException(Defect, "No timers registered in dispatcher.") ++ let nextTimeout = processTimers(disp, result) ++ processPendingCallbacks(disp, result) ++ if nextTimeout.isSome: ++ if result: ++ # schedule the next pending timer ++ disp.timeoutHandler.schedule(get nextTimeout) ++ else: ++ # schedule the next pender timer or timeout, whichever is earlier ++ disp.timeoutHandler.schedule(min(timeout, get nextTimeout)) ++ elif not result: ++ # schedule the run timeout and dispatch I/O signals until something happens ++ disp.timeoutHandler.schedule(timeout) ++ let ioDeadline = getMonoTime() + timeout ++ disp.progress = false ++ while true: ++ runtimeEnv.ep.wait_and_dispatch_one_io_signal() ++ # wait for asyncdispatch_timeout_callback to be trigger by a signal ++ if disp.progress or getMonoTime() >= ioDeadline: ++ result = disp.progress ++ break ++ ++ proc runOnce(timeout: int): bool {.inline} = runOnce(initDuration(milliseconds = timeout)) ++ ++ proc addTimer*(timeout: Duration, oneshot: bool, cb: proc() {.gcsafe.}) = ++ var ++ disp = getGlobalDispatcher() ++ fut = newFuture[void]("addTimer") ++ if oneshot: ++ fut.addCallback(cb) ++ else: ++ fut.addCallback: ++ cb() ++ addTimer(timeout, oneshot, cb) ++ # TODO: reuse the future? ++ disp.timers.push((getMonoTime() + timeout, fut)) ++ ++ proc addTimer*(timeout: int, oneshot: bool, cb: proc() {.gcsafe.}) {.inline.} = ++ addTimer(initDuration(milliseconds = timeout), oneshot, cb) ++ + else: + import selectors + from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, +@@ -1668,16 +1742,17 @@ template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped, + result = handle.AsyncFD + register(result) + +-proc createAsyncNativeSocket*(domain: cint, sockType: cint, +- protocol: cint, +- inheritable = defined(nimInheritHandles)): AsyncFD = +- createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) ++when not socketsMissing: ++ proc createAsyncNativeSocket*(domain: cint, sockType: cint, ++ protocol: cint, ++ inheritable = defined(nimInheritHandles)): AsyncFD = ++ createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) + +-proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET, +- sockType: SockType = SOCK_STREAM, +- protocol: Protocol = IPPROTO_TCP, +- inheritable = defined(nimInheritHandles)): AsyncFD = +- createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) ++ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET, ++ sockType: SockType = SOCK_STREAM, ++ protocol: Protocol = IPPROTO_TCP, ++ inheritable = defined(nimInheritHandles)): AsyncFD = ++ createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) + + when defined(windows) or defined(nimdoc): + proc bindToDomain(handle: SocketHandle, domain: Domain) = +@@ -1727,7 +1802,7 @@ when defined(windows) or defined(nimdoc): + # and the future will be completed/failed there, too. + GC_unref(ol) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) +-else: ++elif not socketsMissing: + proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) = + let retFuture = newFuture[void]("doConnect") + result = retFuture +@@ -1759,107 +1834,108 @@ else: + else: + retFuture.fail(newException(OSError, osErrorMsg(lastError))) + +-template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, +- protocol: Protocol = IPPROTO_RAW) = +- ## Iterates through the AddrInfo linked list asynchronously +- ## until the connection can be established. +- const shouldCreateFd = not declared(fd) +- +- when shouldCreateFd: +- let sockType = protocol.toSockType() ++when not socketsMissing: ++ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, ++ protocol: Protocol = IPPROTO_RAW) = ++ ## Iterates through the AddrInfo linked list asynchronously ++ ## until the connection can be established. ++ const shouldCreateFd = not declared(fd) ++ ++ when shouldCreateFd: ++ let sockType = protocol.toSockType() ++ ++ var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD] ++ for i in low(fdPerDomain)..high(fdPerDomain): ++ fdPerDomain[i] = osInvalidSocket.AsyncFD ++ template closeUnusedFds(domainToKeep = -1) {.dirty.} = ++ for i, fd in fdPerDomain: ++ if fd != osInvalidSocket.AsyncFD and i != domainToKeep: ++ fd.closeSocket() ++ ++ var lastException: ref Exception ++ var curAddrInfo = addrInfo ++ var domain: Domain ++ when shouldCreateFd: ++ var curFd: AsyncFD ++ else: ++ var curFd = fd ++ proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} = ++ if fut == nil or fut.failed: ++ if fut != nil: ++ lastException = fut.readError() ++ ++ while curAddrInfo != nil: ++ let domainOpt = curAddrInfo.ai_family.toKnownDomain() ++ if domainOpt.isSome: ++ domain = domainOpt.unsafeGet() ++ break ++ curAddrInfo = curAddrInfo.ai_next ++ ++ if curAddrInfo == nil: ++ freeAddrInfo(addrInfo) ++ when shouldCreateFd: ++ closeUnusedFds() ++ if lastException != nil: ++ retFuture.fail(lastException) ++ else: ++ retFuture.fail(newException( ++ IOError, "Couldn't resolve address: " & address)) ++ return + +- var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD] +- for i in low(fdPerDomain)..high(fdPerDomain): +- fdPerDomain[i] = osInvalidSocket.AsyncFD +- template closeUnusedFds(domainToKeep = -1) {.dirty.} = +- for i, fd in fdPerDomain: +- if fd != osInvalidSocket.AsyncFD and i != domainToKeep: +- fd.closeSocket() +- +- var lastException: ref Exception +- var curAddrInfo = addrInfo +- var domain: Domain +- when shouldCreateFd: +- var curFd: AsyncFD +- else: +- var curFd = fd +- proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} = +- if fut == nil or fut.failed: +- if fut != nil: +- lastException = fut.readError() +- +- while curAddrInfo != nil: +- let domainOpt = curAddrInfo.ai_family.toKnownDomain() +- if domainOpt.isSome: +- domain = domainOpt.unsafeGet() +- break ++ when shouldCreateFd: ++ curFd = fdPerDomain[ord(domain)] ++ if curFd == osInvalidSocket.AsyncFD: ++ try: ++ curFd = createAsyncNativeSocket(domain, sockType, protocol) ++ except: ++ freeAddrInfo(addrInfo) ++ closeUnusedFds() ++ raise getCurrentException() ++ when defined(windows): ++ curFd.SocketHandle.bindToDomain(domain) ++ fdPerDomain[ord(domain)] = curFd ++ ++ doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo + curAddrInfo = curAddrInfo.ai_next +- +- if curAddrInfo == nil: ++ else: + freeAddrInfo(addrInfo) + when shouldCreateFd: +- closeUnusedFds() +- if lastException != nil: +- retFuture.fail(lastException) ++ closeUnusedFds(ord(domain)) ++ retFuture.complete(curFd) + else: +- retFuture.fail(newException( +- IOError, "Couldn't resolve address: " & address)) +- return +- +- when shouldCreateFd: +- curFd = fdPerDomain[ord(domain)] +- if curFd == osInvalidSocket.AsyncFD: +- try: +- curFd = createAsyncNativeSocket(domain, sockType, protocol) +- except: +- freeAddrInfo(addrInfo) +- closeUnusedFds() +- raise getCurrentException() +- when defined(windows): +- curFd.SocketHandle.bindToDomain(domain) +- fdPerDomain[ord(domain)] = curFd +- +- doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo +- curAddrInfo = curAddrInfo.ai_next +- else: +- freeAddrInfo(addrInfo) +- when shouldCreateFd: +- closeUnusedFds(ord(domain)) +- retFuture.complete(curFd) +- else: +- retFuture.complete() +- +- tryNextAddrInfo(nil) ++ retFuture.complete() + +-proc dial*(address: string, port: Port, +- protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) = +- ## Establishes connection to the specified `address`:`port` pair via the +- ## specified protocol. The procedure iterates through possible +- ## resolutions of the `address` until it succeeds, meaning that it +- ## seamlessly works with both IPv4 and IPv6. +- ## Returns the async file descriptor, registered in the dispatcher of +- ## the current thread, ready to send or receive data. +- let retFuture = newFuture[AsyncFD]("dial") +- result = retFuture +- let sockType = protocol.toSockType() ++ tryNextAddrInfo(nil) ++ ++ proc dial*(address: string, port: Port, ++ protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) = ++ ## Establishes connection to the specified `address`:`port` pair via the ++ ## specified protocol. The procedure iterates through possible ++ ## resolutions of the `address` until it succeeds, meaning that it ++ ## seamlessly works with both IPv4 and IPv6. ++ ## Returns the async file descriptor, registered in the dispatcher of ++ ## the current thread, ready to send or receive data. ++ let retFuture = newFuture[AsyncFD]("dial") ++ result = retFuture ++ let sockType = protocol.toSockType() + +- let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol) +- asyncAddrInfoLoop(aiList, noFD, protocol) ++ let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol) ++ asyncAddrInfoLoop(aiList, noFD, protocol) + +-proc connect*(socket: AsyncFD, address: string, port: Port, +- domain = Domain.AF_INET): owned(Future[void]) = +- let retFuture = newFuture[void]("connect") +- result = retFuture ++ proc connect*(socket: AsyncFD, address: string, port: Port, ++ domain = Domain.AF_INET): owned(Future[void]) = ++ let retFuture = newFuture[void]("connect") ++ result = retFuture + +- when defined(windows): +- verifyPresence(socket) +- else: +- assert getSockDomain(socket.SocketHandle) == domain ++ when defined(windows): ++ verifyPresence(socket) ++ else: ++ assert getSockDomain(socket.SocketHandle) == domain + +- let aiList = getAddrInfo(address, port, domain) +- when defined(windows): +- socket.SocketHandle.bindToDomain(domain) +- asyncAddrInfoLoop(aiList, socket) ++ let aiList = getAddrInfo(address, port, domain) ++ when defined(windows): ++ socket.SocketHandle.bindToDomain(domain) ++ asyncAddrInfoLoop(aiList, socket) + + proc sleepAsync*(timeout: Duration): owned(Future[void]) = + ## Suspends the execution of the current async procedure for the given +@@ -1900,48 +1976,49 @@ proc withTimeout*[T](fut: Future[T], timeoutMs: int): owned(Future[bool]) = + if not retFuture.finished: retFuture.complete(false) + return retFuture + +-proc accept*(socket: AsyncFD, +- flags = {SocketFlag.SafeDisconn}, +- inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) = +- ## Accepts a new connection. Returns a future containing the client socket +- ## corresponding to that connection. +- ## +- ## If `inheritable` is false (the default), the resulting client socket +- ## will not be inheritable by child processes. +- ## +- ## The future will complete when the connection is successfully accepted. +- var retFut = newFuture[AsyncFD]("accept") +- var fut = acceptAddr(socket, flags, inheritable) +- fut.callback = +- proc (future: Future[tuple[address: string, client: AsyncFD]]) = +- assert future.finished +- if future.failed: +- retFut.fail(future.error) +- else: +- retFut.complete(future.read.client) +- return retFut +- +-proc keepAlive(x: string) = +- discard "mark 'x' as escaping so that it is put into a closure for us to keep the data alive" +- +-proc send*(socket: AsyncFD, data: string, +- flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = +- ## Sends `data` to `socket`. The returned future will complete once all +- ## data has been sent. +- var retFuture = newFuture[void]("send") +- if data.len > 0: +- let sendFut = socket.send(unsafeAddr data[0], data.len, flags) +- sendFut.callback = +- proc () = +- keepAlive(data) +- if sendFut.failed: +- retFuture.fail(sendFut.error) ++when not socketsMissing: ++ proc accept*(socket: AsyncFD, ++ flags = {SocketFlag.SafeDisconn}, ++ inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) = ++ ## Accepts a new connection. Returns a future containing the client socket ++ ## corresponding to that connection. ++ ## ++ ## If `inheritable` is false (the default), the resulting client socket ++ ## will not be inheritable by child processes. ++ ## ++ ## The future will complete when the connection is successfully accepted. ++ var retFut = newFuture[AsyncFD]("accept") ++ var fut = acceptAddr(socket, flags, inheritable) ++ fut.callback = ++ proc (future: Future[tuple[address: string, client: AsyncFD]]) = ++ assert future.finished ++ if future.failed: ++ retFut.fail(future.error) + else: +- retFuture.complete() +- else: +- retFuture.complete() ++ retFut.complete(future.read.client) ++ return retFut + +- return retFuture ++ proc keepAlive(x: string) = ++ discard "mark 'x' as escaping so that it is put into a closure for us to keep the data alive" ++ ++ proc send*(socket: AsyncFD, data: string, ++ flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = ++ ## Sends `data` to `socket`. The returned future will complete once all ++ ## data has been sent. ++ var retFuture = newFuture[void]("send") ++ if data.len > 0: ++ let sendFut = socket.send(unsafeAddr data[0], data.len, flags) ++ sendFut.callback = ++ proc () = ++ keepAlive(data) ++ if sendFut.failed: ++ retFuture.fail(sendFut.error) ++ else: ++ retFuture.complete() ++ else: ++ retFuture.complete() ++ ++ return retFuture + + # -- Await Macro + include asyncmacro +@@ -1977,6 +2054,7 @@ proc activeDescriptors*(): int {.inline.} = + ## event loop. This is a cheap operation that does not involve a system call. + when defined(windows): + result = getGlobalDispatcher().handles.len ++ elif defined(genode): -1 + elif not defined(nimdoc): + result = getGlobalDispatcher().selector.count + +diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim +index 21bef9187..b1edff636 100644 +--- a/lib/pure/selectors.nim ++++ b/lib/pure/selectors.nim +@@ -350,6 +350,7 @@ elif not defined(nimNoLibc): + elif defined(solaris): + include ioselects/ioselectors_poll # need to replace it with event ports + elif defined(genode): ++ when not defined(posix): {.error: "do not import this module without POSIX support".} + include ioselects/ioselectors_select # TODO: use the native VFS layer + elif defined(nintendoswitch): + include ioselects/ioselectors_select +-- +2.37.2 + + +From 0e45ef5201ba41bfa971d28f859ad7efcdd58549 Mon Sep 17 00:00:00 2001 +From: Emery Hemingway +Date: Wed, 12 Oct 2022 18:49:08 -0500 +Subject: [PATCH 8/8] Genode: do no enter libc when posix not defined + +--- + compiler/ccgexprs.nim | 2 +- + compiler/cgen.nim | 23 ++++++++++++++++++++--- + 2 files changed, 21 insertions(+), 4 deletions(-) + +diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim +index d6057fcfb..cb90c8e85 100644 +--- a/compiler/ccgexprs.nim ++++ b/compiler/ccgexprs.nim +@@ -1155,7 +1155,7 @@ proc genEcho(p: BProc, n: PNode) = + # this unusual way of implementing it ensures that e.g. ``echo("hallo", 45)`` + # is threadsafe. + internalAssert p.config, n.kind == nkBracket +- if p.config.target.targetOS == osGenode: ++ if p.config.target.targetOS == osGenode and not p.config.isDefined("posix"): + # echo directly to the Genode LOG session + var args: Rope = nil + var a: TLoc +diff --git a/compiler/cgen.nim b/compiler/cgen.nim +index 03999db62..03dbaa30b 100644 +--- a/compiler/cgen.nim ++++ b/compiler/cgen.nim +@@ -1464,6 +1464,16 @@ proc genMainProc(m: BModule) = + NimMainBody + + ComponentConstruct = ++ "void Component::construct(Genode::Env &env) {$N" & ++ "\t// Set Env used during runtime initialization$N" & ++ "\tnim_runtime_env = &env;$N" & ++ "\t// Initialize runtime and globals$N" & ++ MainProcs & ++ "\t// Call application construct$N" & ++ "\tnim_component_construct(&env);$N" & ++ "}$N$N" ++ ++ LibcComponentConstruct = + "void Libc::Component::construct(Libc::Env &env) {$N" & + "\t// Set Env used during runtime initialization$N" & + "\tnim_runtime_env = &env;$N" & +@@ -1479,7 +1489,10 @@ proc genMainProc(m: BModule) = + m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: + m.includeHeader("") + elif m.config.target.targetOS == osGenode: +- m.includeHeader("") ++ if m.config.isDefined("posix"): ++ m.includeHeader("") ++ else: ++ m.includeHeader("") + + let initStackBottomCall = + if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope +@@ -1535,8 +1548,12 @@ proc genMainProc(m: BModule) = + const otherMain = WinCDllMain + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + elif m.config.target.targetOS == osGenode: +- const otherMain = ComponentConstruct +- appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) ++ if m.config.isDefined("posix"): ++ const otherMain = LibcComponentConstruct ++ appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) ++ else: ++ const otherMain = ComponentConstruct ++ appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + elif optGenDynLib in m.config.globalOptions: + const otherMain = PosixCDllMain + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) +-- +2.37.2 + diff --git a/tests/nim.nix b/tests/nim.nix index 9c80a0e..289b2e0 100644 --- a/tests/nim.nix +++ b/tests/nim.nix @@ -6,7 +6,6 @@ nimPackages.buildNimPackage { pname = "test_nim"; inherit (nim) version; - nimDefines = [ "posix" ]; unpackPhase = '' mkdir test cd test @@ -19,7 +18,6 @@ }; in { package = testNim; - extraInputs = with pkgs.genodePackages; [ libc stdcxx ]; configFile = builtins.toFile "nim.dhall" '' let Sigil = env:DHALL_SIGIL @@ -27,18 +25,16 @@ let Child = Init.Child - let Libc = Sigil.Libc - in λ(binary : Text) → Child.flat Child.Attributes::{ , binary + , config = Init.Config::{ attributes = toMap { ld_verbose = "true" } } , exitPropagate = True , resources = Sigil.Init.Resources::{ , caps = 500 , ram = Sigil.units.MiB 10 } - , config = Libc.toConfig Libc.default } ''; };