modulepaths.nim 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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
  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. resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
  86. proc lookupPackage(pkg, subdir: PNode): string =
  87. let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
  88. case pkg.kind
  89. of nkStrLit, nkRStrLit, nkTripleStrLit:
  90. result = scriptableImport(pkg.strVal, sub, pkg.info)
  91. of nkIdent:
  92. result = scriptableImport(pkg.ident.s, sub, pkg.info)
  93. else:
  94. localError(pkg.info, "package name must be an identifier or string literal")
  95. result = ""
  96. proc getModuleName*(conf: ConfigRef; n: PNode): string =
  97. # This returns a short relative module name without the nim extension
  98. # e.g. like "system", "importer" or "somepath/module"
  99. # The proc won't perform any checks that the path is actually valid
  100. case n.kind
  101. of nkStrLit, nkRStrLit, nkTripleStrLit:
  102. try:
  103. result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir)
  104. except ValueError:
  105. localError(conf, n.info, "invalid path: " & n.strVal)
  106. result = n.strVal
  107. of nkIdent:
  108. result = n.ident.s
  109. of nkSym:
  110. result = n.sym.name.s
  111. of nkInfix:
  112. let n0 = n[0]
  113. let n1 = n[1]
  114. when false:
  115. if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
  116. if n0.kind == nkIdent and n0.ident.s == "/":
  117. result = lookupPackage(n1[1], n[2])
  118. else:
  119. localError(n.info, "only '/' supported with $package notation")
  120. result = ""
  121. else:
  122. let modname = getModuleName(conf, n[2])
  123. # hacky way to implement 'x / y /../ z':
  124. result = getModuleName(conf, n1)
  125. result.add renderTree(n0, {renderNoComments}).replace(" ")
  126. result.add modname
  127. of nkPrefix:
  128. when false:
  129. if n[0].kind == nkIdent and n[0].ident.s == "$":
  130. result = lookupPackage(n[1], nil)
  131. else:
  132. discard
  133. # hacky way to implement 'x / y /../ z':
  134. result = renderTree(n, {renderNoComments}).replace(" ")
  135. of nkDotExpr:
  136. localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated")
  137. result = renderTree(n, {renderNoComments}).replace(".", "/")
  138. of nkImportAs:
  139. result = getModuleName(conf, n[0])
  140. else:
  141. localError(conf, n.info, "invalid module name: '$1'" % n.renderTree)
  142. result = ""
  143. proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
  144. # This returns the full canonical path for a given module import
  145. let modulename = getModuleName(conf, n)
  146. let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
  147. if fullPath.isEmpty:
  148. if doLocalError:
  149. let m = if modulename.len > 0: modulename else: $n
  150. localError(conf, n.info, "cannot open file: " & m)
  151. result = InvalidFileIdx
  152. else:
  153. result = fileInfoIdx(conf, fullPath)