modulepaths.nim 5.7 KB

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