1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
#
#
# The Nim Compiler
# (c) Copyright 2017 Contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
pathutils
when false:
const
considerParentDirs = not defined(noParentProjects)
considerNimbleDirs = not defined(noNimbleDirs)
proc findInNimbleDir(pkg, subdir, dir: string): string =
var best = ""
var bestv = ""
for k, p in os.walkDir(dir, relative=true):
if k == pcDir and p.len > pkg.len+1 and
p[pkg.len] == '-' and p.startsWith(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(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 subdir.len == 0: pkg else: subdir
let res = addFileExt(best / f, "nim")
if best.len > 0 and fileExists(res):
result = res
when false:
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
template attempt(a) =
let x = addFileExt(a, "nim")
if fileExists(x): return x
case pkg
of "stdlib":
if subdir.len == 0:
return options.libpath
else:
for candidate in stdlibDirs:
attempt(options.libpath / candidate / subdir)
of "root":
let root = project.splitFile.dir
if subdir.len == 0:
return root
else:
attempt(root / subdir)
else:
when considerParentDirs:
var p = parentDir(source.splitFile.dir)
# support 'import $karax':
let f = if subdir.len == 0: pkg else: subdir
while p.len > 0:
let dir = p / pkg
if dirExists(dir):
attempt(dir / f)
# 2nd attempt: try to use 'karax/karax'
attempt(dir / pkg / f)
# 3rd attempt: try to use 'karax/src/karax'
attempt(dir / "src" / f)
attempt(dir / "src" / pkg / f)
p = parentDir(p)
when considerNimbleDirs:
if not options.gNoNimblePath:
var nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
if result.len > 0: return result
when not defined(windows):
result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
if result.len > 0: return result
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
case pkg.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = scriptableImport(pkg.strVal, sub, pkg.info)
of nkIdent:
result = scriptableImport(pkg.ident.s, sub, pkg.info)
else:
localError(pkg.info, "package name must be an identifier or string literal")
result = ""
proc getModuleName*(conf: ConfigRef; n: PNode): string =
# This returns a short relative module name without the nim extension
# e.g. like "system", "importer" or "somepath/module"
# The proc won't perform any checks that the path is actually valid
case n.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
try:
result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir)
except ValueError:
localError(conf, n.info, "invalid path: " & n.strVal)
result = n.strVal
of nkIdent:
result = n.ident.s
of nkSym:
result = n.sym.name.s
of nkInfix:
let n0 = n[0]
let n1 = n[1]
when false:
if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
if n0.kind == nkIdent and n0.ident.s == "/":
result = lookupPackage(n1[1], n[2])
else:
localError(n.info, "only '/' supported with $package notation")
result = ""
else:
let modname = getModuleName(conf, n[2])
# hacky way to implement 'x / y /../ z':
result = getModuleName(conf, n1)
result.add renderTree(n0, {renderNoComments}).replace(" ")
result.add modname
of nkPrefix:
when false:
if n[0].kind == nkIdent and n[0].ident.s == "$":
result = lookupPackage(n[1], nil)
else:
discard
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr:
localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated")
result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs:
result = getModuleName(conf, n[0])
else:
localError(conf, n.info, "invalid module name: '$1'" % n.renderTree)
result = ""
proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
# This returns the full canonical path for a given module import
let modulename = getModuleName(conf, n)
let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
if fullPath.isEmpty:
if doLocalError:
let m = if modulename.len > 0: modulename else: $n
localError(conf, n.info, "cannot open file: " & m)
result = InvalidFileIdx
else:
result = fileInfoIdx(conf, fullPath)
|