modulepaths.nim 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. 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. except ValueError:
  107. localError(conf, n.info, "invalid path: " & n.strVal)
  108. result = n.strVal
  109. of nkIdent:
  110. result = n.ident.s
  111. of nkSym:
  112. result = n.sym.name.s
  113. of nkInfix:
  114. let n0 = n[0]
  115. let n1 = n[1]
  116. when false:
  117. if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
  118. if n0.kind == nkIdent and n0.ident.s == "/":
  119. result = lookupPackage(n1[1], n[2])
  120. else:
  121. localError(n.info, "only '/' supported with $package notation")
  122. result = ""
  123. else:
  124. let modname = getModuleName(conf, n[2])
  125. # hacky way to implement 'x / y /../ z':
  126. result = getModuleName(conf, n1)
  127. result.add renderTree(n0, {renderNoComments}).replace(" ")
  128. result.add modname
  129. of nkPrefix:
  130. when false:
  131. if n[0].kind == nkIdent and n[0].ident.s == "$":
  132. result = lookupPackage(n[1], nil)
  133. else:
  134. discard
  135. # hacky way to implement 'x / y /../ z':
  136. result = renderTree(n, {renderNoComments}).replace(" ")
  137. of nkDotExpr:
  138. localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated")
  139. result = renderTree(n, {renderNoComments}).replace(".", "/")
  140. of nkImportAs:
  141. result = getModuleName(conf, n[0])
  142. else:
  143. localError(conf, n.info, "invalid module name: '$1'" % n.renderTree)
  144. result = ""
  145. proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
  146. # This returns the full canonical path for a given module import
  147. let modulename = getModuleName(conf, n)
  148. let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
  149. if fullPath.isEmpty:
  150. if doLocalError:
  151. let m = if modulename.len > 0: modulename else: $n
  152. localError(conf, n.info, "cannot open file: " & m)
  153. result = InvalidFileIdx
  154. else:
  155. result = fileInfoIdx(conf, fullPath)