Files
Nim/tools/nimresolve.nim
2017-10-01 23:38:23 +02:00

159 lines
4.1 KiB
Nim

#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Standard tool that resolves import paths.
import
os, strutils, parseopt
import "../compiler/nimblecmd"
# You can change these constants to build you own adapted resolver.
const
considerParentDirs = not defined(noParentProjects)
considerNimbleDirs = not defined(noNimbleDirs)
const
Version = "1.0"
Usage = "nimresolve - Nim Resolve Package Path Version " & Version & """
(c) 2017 Andreas Rumpf
Usage:
nimresolve [options] package
Options:
--source:FILE the file that requests to resolve 'package'
--stdlib:PATH the path to use for the standard library
--project:FILE the main '.nim' file that was passed to the Nim compiler
--subdir:EXPR the subdir part in: 'import $pkg / subdir'
--noNimblePath do not search the Nimble path to resolve the package
"""
proc writeHelp() =
stdout.write(Usage)
stdout.flushFile()
quit(0)
proc writeVersion() =
stdout.write(Version & "\n")
stdout.flushFile()
quit(0)
type
Task = object
source, stdlib, subdir, project, pkg: string
noNimblePath: bool
proc findInNimbleDir(t: Task; dir: string): bool =
var best = ""
var bestv = ""
for k, p in os.walkDir(dir, relative=true):
if k == pcDir and p.len > t.pkg.len+1 and
p[t.pkg.len] == '-' and p.startsWith(t.pkg):
let (_, a) = getPathVersion(p)
if bestv.len == 0 or bestv < a:
bestv = a
best = dir / p
if best.len > 0:
var f: File
if open(f, best / changeFileExt(t.pkg, ".nimble-link")):
# the second line contains what we're interested in, see:
# https://github.com/nim-lang/nimble#nimble-link
var override = ""
discard readLine(f, override)
discard readLine(f, override)
close(f)
if not override.isAbsolute():
best = best / override
else:
best = override
let f = if t.subdir.len == 0: t.pkg else: t.subdir
let res = addFileExt(best / f, "nim")
if best.len > 0 and fileExists(res):
echo res
result = true
const stdlibDirs = [
"pure", "core", "arch",
"pure/collections",
"pure/concurrency", "impure",
"wrappers", "wrappers/linenoise",
"windows", "posix", "js"]
proc resolve(t: Task) =
template attempt(a) =
let x = addFileExt(a, "nim")
if fileExists(x):
echo x
return
case t.pkg
of "stdlib":
if t.subdir.len == 0:
echo t.stdlib
return
else:
for candidate in stdlibDirs:
attempt(t.stdlib / candidate / t.subdir)
of "root":
let root = t.project.splitFile.dir
if t.subdir.len == 0:
echo root
return
else:
attempt(root / t.subdir)
else:
when considerParentDirs:
var p = parentDir(t.source.splitFile.dir)
# support 'import $karax':
let f = if t.subdir.len == 0: t.pkg else: t.subdir
while p.len > 0:
let dir = p / t.pkg
if dirExists(dir):
attempt(dir / f)
# 2nd attempt: try to use 'karax/karax'
attempt(dir / t.pkg / f)
# 3rd attempt: try to use 'karax/src/karax'
attempt(dir / "src" / f)
attempt(dir / "src" / t.pkg / f)
p = parentDir(p)
when considerNimbleDirs:
if not t.noNimblePath:
if findInNimbleDir(t, getHomeDir() / ".nimble" / "pkgs"): return
when not defined(windows):
if findInNimbleDir(t, "/opt/nimble/pkgs"): return
quit "cannot resolve: " & (t.pkg / t.subdir)
proc main =
var t: Task
t.subdir = ""
for kind, key, val in getopt():
case kind
of cmdArgument:
t.pkg = key
of cmdLongoption, cmdShortOption:
case normalize(key)
of "source": t.source = val
of "stdlib": t.stdlib = val
of "project": t.project = val
of "subdir": t.subdir = val
of "nonimblepath": t.noNimblePath = true
of "help", "h": writeHelp()
of "version", "v": writeVersion()
else: writeHelp()
of cmdEnd: assert(false) # cannot happen
if t.pkg.len == 0:
quit "[Error] no package to resolve."
resolve(t)
main()