Enable macros to use certain things from the OS module when the target OS is not supported (#24639)

Essentially this PR removes the `{.error.}` pragmas littered around in
the OS module and submodules which prevents them from being imported if
the target OS is not supported. This made it impossible to use certain
supported features of the OS module in macros from a supported host OS.
Instead of the `{.error.}` pragmas the `oscommon` module now has a
constant `supportedSystem` which is false in the cases where the
`{.error.}` pragmas where generated. All procedures which can't be run
by macros is also not declared when `supportedSystem` is false.

It would be possible to create dummy versions of the omitted functions
with an `{.error.}` pragma that would trigger upon their use, but this
is currently not done.

This properly fixes #19414
This commit is contained in:
Peter Munch-Ellingsen
2025-01-24 13:02:59 +01:00
committed by GitHub
parent 67f9bc2f4b
commit 1f9cac1f5c
9 changed files with 752 additions and 751 deletions

View File

@@ -263,7 +263,7 @@ proc registerAdditionalOps*(c: PCtx) =
wrap2si(readLines, ioop)
systemop getCurrentExceptionMsg
systemop getCurrentException
registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
setResult(a, dirExists(getString(a, 0)))

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@ include system/inclrtl
when defined(nimPreviewSlimSystem):
import std/widestrs
when defined(nodejs):
from std/private/oscommon import ReadDirEffect
@@ -33,8 +33,6 @@ elif defined(windows):
import std/winlean
elif defined(posix):
import std/posix
else:
{.error: "The cmdline module has not been implemented for the target platform.".}
# Needed by windows in order to obtain the command line for targets

View File

@@ -5,9 +5,13 @@ import std/[oserrors]
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions, widestrs]
from std/staticos import PathComponent
## .. importdoc:: osdirs.nim, os.nim
const weirdTarget* = defined(nimscript) or defined(js)
const
weirdTarget* = defined(nimscript) or defined(js)
supportedSystem* = weirdTarget or defined(windows) or defined(posix)
type
@@ -27,8 +31,6 @@ elif defined(posix):
import std/posix
proc c_rename(oldname, newname: cstring): cint {.
importc: "rename", header: "<stdio.h>".}
else:
{.error: "OS module not ported to your operating system!".}
when weirdTarget:
@@ -65,122 +67,110 @@ when defined(windows) and not weirdTarget:
result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
when supportedSystem:
when defined(posix) and not weirdTarget:
proc getSymlinkFileKind*(path: string):
tuple[pc: PathComponent, isSpecial: bool] =
# Helper function.
var s: Stat
assert(path != "")
result = (pcLinkToFile, false)
if stat(path, s) == 0'i32:
if S_ISDIR(s.st_mode):
result = (pcLinkToDir, false)
elif not S_ISREG(s.st_mode):
result = (pcLinkToFile, true)
type
PathComponent* = enum ## Enumeration specifying a path component.
proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
##
## 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):
let s = newWideCString(source)
let d = newWideCString(dest)
result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
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 not defined(windows):
const maxSymlinkLen* = 1024
proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
tags: [ReadDirEffect], noNimJs, sideEffect.} =
## Returns true if `filename` exists and is a regular file or symlink.
##
## Directories, device files, named pipes and sockets return false.
##
## See also:
## * `walkDirRec iterator`_
## * `FileInfo object`_
pcFile, ## path refers to a file
pcLinkToFile, ## path refers to a symbolic link to a file
pcDir, ## path refers to a directory
pcLinkToDir ## path refers to a symbolic link to a directory
## * `dirExists proc`_
## * `symlinkExists proc`_
when defined(windows):
wrapUnary(a, getFileAttributesW, filename)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
else:
var res: Stat
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
when defined(posix) and not weirdTarget:
proc getSymlinkFileKind*(path: string):
tuple[pc: PathComponent, isSpecial: bool] =
# Helper function.
var s: Stat
assert(path != "")
result = (pcLinkToFile, false)
if stat(path, s) == 0'i32:
if S_ISDIR(s.st_mode):
result = (pcLinkToDir, false)
elif not S_ISREG(s.st_mode):
result = (pcLinkToFile, true)
proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
##
## 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):
let s = newWideCString(source)
let d = newWideCString(dest)
result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
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 not defined(windows):
const maxSymlinkLen* = 1024
proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
tags: [ReadDirEffect], noNimJs, sideEffect.} =
## Returns true if `filename` exists and is a regular file or symlink.
##
## Directories, device files, named pipes and sockets return false.
##
## See also:
## * `dirExists proc`_
## * `symlinkExists proc`_
when defined(windows):
wrapUnary(a, getFileAttributesW, filename)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
else:
var res: Stat
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
noNimJs, sideEffect.} =
## Returns true if the directory `dir` exists. If `dir` is a file, false
## is returned. Follows symlinks.
##
## See also:
## * `fileExists proc`_
## * `symlinkExists proc`_
when defined(windows):
wrapUnary(a, getFileAttributesW, dir)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
else:
var res: Stat
result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
noNimJs, sideEffect.} =
## Returns true if the directory `dir` exists. If `dir` is a file, false
## is returned. Follows symlinks.
##
## See also:
## * `fileExists proc`_
## * `symlinkExists proc`_
when defined(windows):
wrapUnary(a, getFileAttributesW, dir)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
else:
var res: Stat
result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
tags: [ReadDirEffect],
noWeirdTarget, sideEffect.} =
## Returns true if the symlink `link` exists. Will return true
## regardless of whether the link points to a directory or file.
##
## See also:
## * `fileExists proc`_
## * `dirExists proc`_
when defined(windows):
wrapUnary(a, getFileAttributesW, link)
if a != -1'i32:
# xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
# may also be needed.
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
else:
var res: Stat
result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
when defined(windows) and not weirdTarget:
proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle =
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
if not followSymlink:
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
let access = if writeAccess: GENERIC_WRITE else: 0'i32
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
tags: [ReadDirEffect],
noWeirdTarget, sideEffect.} =
## Returns true if the symlink `link` exists. Will return true
## regardless of whether the link points to a directory or file.
##
## See also:
## * `fileExists proc`_
## * `dirExists proc`_
when defined(windows):
wrapUnary(a, getFileAttributesW, link)
if a != -1'i32:
# xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
# may also be needed.
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
else:
var res: Stat
result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
when defined(windows) and not weirdTarget:
proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle =
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
if not followSymlink:
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
let access = if writeAccess: GENERIC_WRITE else: 0'i32
result = createFileW(
newWideCString(path), access,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0
)
result = createFileW(
newWideCString(path), access,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0
)

View File

@@ -6,7 +6,9 @@ import std/oserrors
import ospaths2, osfiles
import oscommon
export dirExists, PathComponent
import std/staticos
when supportedSystem:
export dirExists, PathComponent
when defined(nimPreviewSlimSystem):
@@ -20,9 +22,6 @@ elif defined(windows):
elif defined(posix):
import std/[posix, times]
else:
{.error: "OS module not ported to your operating system!".}
when weirdTarget:
{.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
@@ -152,10 +151,6 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarg
assert "lib/pure/concurrency".unixToNativePath in paths
walkCommon(pattern, isDir)
proc staticWalkDir(dir: string; relative: bool): seq[
tuple[kind: PathComponent, path: string]] =
discard
iterator walkDir*(dir: string; relative = false, checkDir = false,
skipSpecial = false):
tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} =
@@ -325,7 +320,6 @@ iterator walkDirRec*(dir: string,
# continue iteration.
# Future work can provide a way to customize this and do error reporting.
proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
when defined(windows):
wrapUnary(res, removeDirectoryW, dir)

View File

@@ -21,8 +21,6 @@ elif defined(posix):
proc toTime(ts: Timespec): times.Time {.inline.} =
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
else:
{.error: "OS module not ported to your operating system!".}
when weirdTarget:

View File

@@ -20,8 +20,6 @@ elif defined(windows):
import std/winlean
elif defined(posix):
import std/posix, system/ansi_c
else:
{.error: "OS module not ported to your operating system!".}
when weirdTarget:
{.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
@@ -840,7 +838,7 @@ proc unixToNativePath*(path: string, drive=""): string {.
inc(i)
when not defined(nimscript):
when not defined(nimscript) and supportedSystem:
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
## Returns the `current working directory`:idx: i.e. where the built
## binary is run.
@@ -889,7 +887,7 @@ when not defined(nimscript):
else:
raiseOSError(osLastError())
proc absolutePath*(path: string, root = getCurrentDir()): string =
proc absolutePath*(path: string, root = when supportedSystem: getCurrentDir() else: ""): string =
## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
## default: current directory).
## If `path` is absolute, return it, ignoring `root`.
@@ -907,7 +905,7 @@ proc absolutePath*(path: string, root = getCurrentDir()): string =
joinPath(root, path)
proc absolutePathInternal(path: string): string =
absolutePath(path, getCurrentDir())
absolutePath(path)
proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} =
@@ -984,48 +982,49 @@ proc normalizeExe*(file: var string) {.since: (1, 3, 5).} =
if file.len > 0 and DirSep notin file and file != "." and file != "..":
file = "./" & file
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
tags: [ReadDirEffect], noWeirdTarget.} =
## Returns true if both pathname arguments refer to the same physical
## file or directory.
##
## Raises `OSError` if any of the files does not
## exist or information about it can not be obtained.
##
## This proc will return true if given two alternative hard-linked or
## sym-linked paths to the same file or directory.
##
## See also:
## * `sameFileContent proc`_
result = false
when defined(windows):
var success = true
var f1 = openHandle(path1)
var f2 = openHandle(path2)
when supportedSystem:
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
tags: [ReadDirEffect], noWeirdTarget.} =
## Returns true if both pathname arguments refer to the same physical
## file or directory.
##
## Raises `OSError` if any of the files does not
## exist or information about it can not be obtained.
##
## This proc will return true if given two alternative hard-linked or
## sym-linked paths to the same file or directory.
##
## See also:
## * `sameFileContent proc`_
result = false
when defined(windows):
var success = true
var f1 = openHandle(path1)
var f2 = openHandle(path2)
var lastErr: OSErrorCode
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
var fi1, fi2: BY_HANDLE_FILE_INFORMATION
var lastErr: OSErrorCode
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
var fi1, fi2: BY_HANDLE_FILE_INFORMATION
if getFileInformationByHandle(f1, addr(fi1)) != 0 and
getFileInformationByHandle(f2, addr(fi2)) != 0:
result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
fi1.nFileIndexHigh == fi2.nFileIndexHigh and
fi1.nFileIndexLow == fi2.nFileIndexLow
if getFileInformationByHandle(f1, addr(fi1)) != 0 and
getFileInformationByHandle(f2, addr(fi2)) != 0:
result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
fi1.nFileIndexHigh == fi2.nFileIndexHigh and
fi1.nFileIndexLow == fi2.nFileIndexLow
else:
lastErr = osLastError()
success = false
else:
lastErr = osLastError()
success = false
else:
lastErr = osLastError()
success = false
discard closeHandle(f1)
discard closeHandle(f2)
discard closeHandle(f1)
discard closeHandle(f2)
if not success: raiseOSError(lastErr, $(path1, path2))
else:
var a, b: Stat
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
raiseOSError(osLastError(), $(path1, path2))
if not success: raiseOSError(lastErr, $(path1, path2))
else:
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
var a, b: Stat
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
raiseOSError(osLastError(), $(path1, path2))
else:
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino

View File

@@ -2,7 +2,8 @@ include system/inclrtl
import std/oserrors
import oscommon
export symlinkExists
when supportedSystem:
export symlinkExists
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions, widestrs]
@@ -13,8 +14,6 @@ elif defined(windows):
import std/[winlean, times]
elif defined(posix):
import std/posix
else:
{.error: "OS module not ported to your operating system!".}
when weirdTarget:

View File

@@ -14,3 +14,25 @@ proc staticDirExists*(dir: string): bool {.compileTime.} =
## Returns true if the directory `dir` exists. If `dir` is a file, false
## is returned. Follows symlinks.
raiseAssert "implemented in the vmops"
type
PathComponent* = enum ## Enumeration specifying a path component.
##
## See also:
## * `walkDirRec iterator`_
## * `FileInfo object`_
pcFile, ## path refers to a file
pcLinkToFile, ## path refers to a symbolic link to a file
pcDir, ## path refers to a directory
pcLinkToDir ## path refers to a symbolic link to a directory
proc staticWalkDir*(dir: string; relative = false): seq[
tuple[kind: PathComponent, path: string]] {.compileTime.} =
## Walks over the directory `dir` and returns a seq with each directory or
## file in `dir`. The component type and full path for each item are returned.
##
## Walking is not recursive.
## * If `relative` is true (default: false)
## the resulting path is shortened to be relative to ``dir``,
## otherwise the full path is returned.
raiseAssert "implemented in the vmops"