mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 21:40:32 +00:00
* * relativePath(foo) now works * relativePath(rel, abs) and relativePath(abs, rel) now work (fixes #13222) * relativePath, absolutePath, getCurrentDir now available in more targets (eg: vm, nodejs etc) * fix bug: isAbsolutePath now works with -d:js; add tests * workaround https://github.com/nim-lang/Nim/issues/13469 * remove `relativePath(path)` overload for now * add back changelog after rebase
This commit is contained in:
@@ -31,6 +31,12 @@
|
||||
- The file descriptors created for internal bookkeeping by `ioselector_kqueue`
|
||||
and `ioselector_epoll` will no longer be leaked to child processes.
|
||||
|
||||
- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results
|
||||
(see #13222); instead they now use `getCurrentDir` to resolve those cases,
|
||||
and this can now throw in edge cases where `getCurrentDir` throws.
|
||||
`relativePath` also now works for js with `-d:nodejs`.
|
||||
|
||||
|
||||
## Language changes
|
||||
- In newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this:
|
||||
```nim
|
||||
|
||||
158
lib/pure/os.nim
158
lib/pure/os.nim
@@ -98,6 +98,8 @@ type
|
||||
|
||||
include "includes/osseps"
|
||||
|
||||
proc absolutePathInternal(path: string): string {.gcsafe.}
|
||||
|
||||
proc normalizePathEnd(path: var string, trailingSep = false) =
|
||||
## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
|
||||
## ``trailingSep``, and taking care of edge cases: it preservers whether
|
||||
@@ -297,8 +299,12 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise
|
||||
result = path[0] != ':'
|
||||
elif defined(RISCOS):
|
||||
result = path[0] == '$'
|
||||
elif defined(posix):
|
||||
elif defined(posix) or defined(js):
|
||||
# `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469
|
||||
# This works around the problem for posix, but windows is still broken with nim js -d:nodejs
|
||||
result = path[0] == '/'
|
||||
else:
|
||||
doAssert false # if ever hits here, adapt as needed
|
||||
|
||||
when FileSystemCaseSensitive:
|
||||
template `!=?`(a, b: char): bool = a != b
|
||||
@@ -360,8 +366,8 @@ when doslikeFileSystem:
|
||||
else:
|
||||
return false
|
||||
|
||||
proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||
noSideEffect, rtl, extern: "nos$1", raises: [].} =
|
||||
proc relativePath*(path, base: string, sep = DirSep): string {.
|
||||
rtl, extern: "nos$1".} =
|
||||
## Converts `path` to a path relative to `base`.
|
||||
##
|
||||
## The `sep` (default: `DirSep <#DirSep>`_) is used for the path normalizations,
|
||||
@@ -390,6 +396,12 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||
var path = path
|
||||
path.normalizePathAux
|
||||
base.normalizePathAux
|
||||
let a1 = isAbsolute(path)
|
||||
let a2 = isAbsolute(base)
|
||||
if a1 and not a2:
|
||||
base = absolutePathInternal(base)
|
||||
elif a2 and not a1:
|
||||
path = absolutePathInternal(path)
|
||||
|
||||
when doslikeFileSystem:
|
||||
if isAbsolute(path) and isAbsolute(base):
|
||||
@@ -1293,60 +1305,67 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noNimScript.} =
|
||||
else:
|
||||
result = getLastModificationTime(a) > getLastModificationTime(b)
|
||||
|
||||
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [], noNimScript.} =
|
||||
## Returns the `current working directory`:idx: i.e. where the built
|
||||
## binary is run.
|
||||
##
|
||||
## So the path returned by this proc is determined at run time.
|
||||
##
|
||||
## See also:
|
||||
## * `getHomeDir proc <#getHomeDir>`_
|
||||
## * `getConfigDir proc <#getConfigDir>`_
|
||||
## * `getTempDir proc <#getTempDir>`_
|
||||
## * `setCurrentDir proc <#setCurrentDir,string>`_
|
||||
## * `currentSourcePath template <system.html#currentSourcePath.t>`_
|
||||
## * `getProjectPath proc <macros.html#getProjectPath>`_
|
||||
when defined(windows):
|
||||
var bufsize = MAX_PATH.int32
|
||||
when useWinUnicode:
|
||||
var res = newWideCString("", bufsize)
|
||||
while true:
|
||||
var L = getCurrentDirectoryW(bufsize, res)
|
||||
if L == 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
elif L > bufsize:
|
||||
res = newWideCString("", L)
|
||||
bufsize = L
|
||||
else:
|
||||
result = res$L
|
||||
break
|
||||
when not defined(nimscript):
|
||||
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
|
||||
## Returns the `current working directory`:idx: i.e. where the built
|
||||
## binary is run.
|
||||
##
|
||||
## So the path returned by this proc is determined at run time.
|
||||
##
|
||||
## See also:
|
||||
## * `getHomeDir proc <#getHomeDir>`_
|
||||
## * `getConfigDir proc <#getConfigDir>`_
|
||||
## * `getTempDir proc <#getTempDir>`_
|
||||
## * `setCurrentDir proc <#setCurrentDir,string>`_
|
||||
## * `currentSourcePath template <system.html#currentSourcePath.t>`_
|
||||
## * `getProjectPath proc <macros.html#getProjectPath>`_
|
||||
when defined(nodejs):
|
||||
var ret: cstring
|
||||
{.emit: "`ret` = process.cwd();".}
|
||||
return $ret
|
||||
elif defined(js):
|
||||
doAssert false, "use -d:nodejs to have `getCurrentDir` defined"
|
||||
elif defined(windows):
|
||||
var bufsize = MAX_PATH.int32
|
||||
when useWinUnicode:
|
||||
var res = newWideCString("", bufsize)
|
||||
while true:
|
||||
var L = getCurrentDirectoryW(bufsize, res)
|
||||
if L == 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
elif L > bufsize:
|
||||
res = newWideCString("", L)
|
||||
bufsize = L
|
||||
else:
|
||||
result = res$L
|
||||
break
|
||||
else:
|
||||
result = newString(bufsize)
|
||||
while true:
|
||||
var L = getCurrentDirectoryA(bufsize, result)
|
||||
if L == 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
elif L > bufsize:
|
||||
result = newString(L)
|
||||
bufsize = L
|
||||
else:
|
||||
setLen(result, L)
|
||||
break
|
||||
else:
|
||||
var bufsize = 1024 # should be enough
|
||||
result = newString(bufsize)
|
||||
while true:
|
||||
var L = getCurrentDirectoryA(bufsize, result)
|
||||
if L == 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
elif L > bufsize:
|
||||
result = newString(L)
|
||||
bufsize = L
|
||||
else:
|
||||
setLen(result, L)
|
||||
if getcwd(result, bufsize) != nil:
|
||||
setLen(result, c_strlen(result))
|
||||
break
|
||||
else:
|
||||
var bufsize = 1024 # should be enough
|
||||
result = newString(bufsize)
|
||||
while true:
|
||||
if getcwd(result, bufsize) != nil:
|
||||
setLen(result, c_strlen(result))
|
||||
break
|
||||
else:
|
||||
let err = osLastError()
|
||||
if err.int32 == ERANGE:
|
||||
bufsize = bufsize shl 1
|
||||
doAssert(bufsize >= 0)
|
||||
result = newString(bufsize)
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
let err = osLastError()
|
||||
if err.int32 == ERANGE:
|
||||
bufsize = bufsize shl 1
|
||||
doAssert(bufsize >= 0)
|
||||
result = newString(bufsize)
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} =
|
||||
## Sets the `current working directory`:idx:; `OSError`
|
||||
@@ -1366,23 +1385,26 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} =
|
||||
else:
|
||||
if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir)
|
||||
|
||||
when not weirdTarget:
|
||||
proc absolutePath*(path: string, root = getCurrentDir()): string {.noNimScript.} =
|
||||
## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
|
||||
## default: current directory).
|
||||
## If `path` is absolute, return it, ignoring `root`.
|
||||
##
|
||||
## See also:
|
||||
## * `normalizedPath proc <#normalizedPath,string>`_
|
||||
## * `normalizePath proc <#normalizePath,string>`_
|
||||
runnableExamples:
|
||||
assert absolutePath("a") == getCurrentDir() / "a"
|
||||
|
||||
if isAbsolute(path): path
|
||||
else:
|
||||
if not root.isAbsolute:
|
||||
raise newException(ValueError, "The specified root is not absolute: " & root)
|
||||
joinPath(root, path)
|
||||
proc absolutePath*(path: string, root = getCurrentDir()): 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`.
|
||||
##
|
||||
## See also:
|
||||
## * `normalizedPath proc <#normalizedPath,string>`_
|
||||
## * `normalizePath proc <#normalizePath,string>`_
|
||||
runnableExamples:
|
||||
assert absolutePath("a") == getCurrentDir() / "a"
|
||||
|
||||
if isAbsolute(path): path
|
||||
else:
|
||||
if not root.isAbsolute:
|
||||
raise newException(ValueError, "The specified root is not absolute: " & root)
|
||||
joinPath(root, path)
|
||||
|
||||
proc absolutePathInternal(path: string): string =
|
||||
absolutePath(path, getCurrentDir())
|
||||
|
||||
proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} =
|
||||
## Normalize a path.
|
||||
|
||||
@@ -5,3 +5,17 @@ import os
|
||||
block:
|
||||
doAssert "./foo//./bar/".normalizedPath == "foo/bar"
|
||||
doAssert relativePath(".//foo/bar", "foo") == "bar"
|
||||
doAssert "/".isAbsolute
|
||||
doAssert not "".isAbsolute
|
||||
doAssert not ".".isAbsolute
|
||||
doAssert not "foo".isAbsolute
|
||||
doAssert relativePath("", "bar") == ""
|
||||
doAssert normalizedPath(".///foo//./") == "foo"
|
||||
let cwd = getCurrentDir()
|
||||
|
||||
let isWindows = '\\' in cwd
|
||||
# defined(windows) doesn't work with -d:nodejs but should
|
||||
# these actually break because of that (see https://github.com/nim-lang/Nim/issues/13469)
|
||||
if not isWindows:
|
||||
doAssert cwd.isAbsolute
|
||||
doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo"
|
||||
|
||||
@@ -348,6 +348,9 @@ block ospaths:
|
||||
doAssert relativePath("", "foo") == ""
|
||||
doAssert relativePath("././/foo", "foo//./") == "."
|
||||
|
||||
doAssert relativePath(getCurrentDir() / "bar", "foo") == "../bar".unixToNativePath
|
||||
doAssert relativePath("bar", getCurrentDir() / "foo") == "../bar".unixToNativePath
|
||||
|
||||
when doslikeFileSystem:
|
||||
doAssert relativePath(r"c:\foo.nim", r"C:\") == r"foo.nim"
|
||||
doAssert relativePath(r"c:\foo\bar\baz.nim", r"c:\foo") == r"bar\baz.nim"
|
||||
|
||||
Reference in New Issue
Block a user