modules.nim 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Implements the module handling, including the caching of modules.
  10. import
  11. ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options,
  12. idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs
  13. when false:
  14. type
  15. TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
  16. THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
  17. TModuleInMemory* = object
  18. hash*: SecureHash
  19. deps*: seq[int32] ## XXX: slurped files are currently not tracked
  20. needsRecompile*: TNeedRecompile
  21. hashStatus*: THashStatus
  22. var
  23. gCompiledModules: seq[PSym] = @[]
  24. gMemCacheData*: seq[TModuleInMemory] = @[]
  25. ## XXX: we should implement recycling of file IDs
  26. ## if the user keeps renaming modules, the file IDs will keep growing
  27. gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why.
  28. proc hashChanged(fileIdx: int32): bool =
  29. internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
  30. template updateStatus =
  31. gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
  32. else: hashNotChanged
  33. # echo "TESTING Hash: ", fileIdx.toFilename, " ", result
  34. case gMemCacheData[fileIdx].hashStatus
  35. of hashHasChanged:
  36. result = true
  37. of hashNotChanged:
  38. result = false
  39. of hashCached:
  40. let newHash = secureHashFile(fileIdx.toFullPath)
  41. result = newHash != gMemCacheData[fileIdx].hash
  42. gMemCacheData[fileIdx].hash = newHash
  43. updateStatus()
  44. of hashNotTaken:
  45. gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
  46. result = true
  47. updateStatus()
  48. proc doHash(fileIdx: int32) =
  49. if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
  50. # echo "FIRST Hash: ", fileIdx.ToFilename
  51. gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
  52. proc resetModule*(fileIdx: int32) =
  53. # echo "HARD RESETTING ", fileIdx.toFilename
  54. if fileIdx <% gMemCacheData.len:
  55. gMemCacheData[fileIdx].needsRecompile = Yes
  56. if fileIdx <% gCompiledModules.len:
  57. gCompiledModules[fileIdx] = nil
  58. if fileIdx <% cgendata.gModules.len:
  59. cgendata.gModules[fileIdx] = nil
  60. proc resetModule*(module: PSym) =
  61. let conflict = getModule(module.position.int32)
  62. if conflict == nil: return
  63. doAssert conflict == module
  64. resetModule(module.position.int32)
  65. initStrTable(module.tab)
  66. proc resetAllModules* =
  67. for i in 0..gCompiledModules.high:
  68. if gCompiledModules[i] != nil:
  69. resetModule(i.int32)
  70. resetPackageCache()
  71. # for m in cgenModules(): echo "CGEN MODULE FOUND"
  72. proc resetAllModulesHard* =
  73. resetPackageCache()
  74. gCompiledModules.setLen 0
  75. gMemCacheData.setLen 0
  76. magicsys.resetSysTypes()
  77. # XXX
  78. #gOwners = @[]
  79. proc checkDepMem(fileIdx: int32): TNeedRecompile =
  80. template markDirty =
  81. resetModule(fileIdx)
  82. return Yes
  83. if gFuzzyGraphChecking:
  84. if gMemCacheData[fileIdx].needsRecompile != Maybe:
  85. return gMemCacheData[fileIdx].needsRecompile
  86. else:
  87. # cycle detection: We claim that a cycle does no harm.
  88. if gMemCacheData[fileIdx].needsRecompile == Probing:
  89. return No
  90. if optForceFullMake in gGlobalOptions or hashChanged(fileIdx):
  91. markDirty()
  92. if gMemCacheData[fileIdx].deps != nil:
  93. gMemCacheData[fileIdx].needsRecompile = Probing
  94. for dep in gMemCacheData[fileIdx].deps:
  95. let d = checkDepMem(dep)
  96. if d in {Yes, Recompiled}:
  97. # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
  98. markDirty()
  99. gMemCacheData[fileIdx].needsRecompile = No
  100. return No
  101. proc resetSystemArtifacts*() =
  102. magicsys.resetSysTypes()
  103. proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
  104. # We cannot call ``newSym`` here, because we have to circumvent the ID
  105. # mechanism, which we do in order to assign each module a persistent ID.
  106. new(result)
  107. result.id = - 1 # for better error checking
  108. result.kind = skModule
  109. let filename = fileIdx.toFullPath
  110. result.name = getIdent(splitFile(filename).name)
  111. if not isNimIdentifier(result.name.s):
  112. rawMessage(errInvalidModuleName, result.name.s)
  113. result.info = newLineInfo(fileIdx, 1, 1)
  114. let
  115. pck = getPackageName(filename)
  116. pck2 = if pck.len > 0: pck else: "unknown"
  117. pack = getIdent(pck2)
  118. var packSym = graph.packageSyms.strTableGet(pack)
  119. if packSym == nil:
  120. packSym = newSym(skPackage, getIdent(pck2), nil, result.info)
  121. initStrTable(packSym.tab)
  122. graph.packageSyms.strTableAdd(packSym)
  123. result.owner = packSym
  124. result.position = fileIdx
  125. growCache graph.modules, fileIdx
  126. graph.modules[result.position] = result
  127. incl(result.flags, sfUsed)
  128. initStrTable(result.tab)
  129. strTableAdd(result.tab, result) # a module knows itself
  130. let existing = strTableGet(packSym.tab, result.name)
  131. if existing != nil and existing.info.fileIndex != result.info.fileIndex:
  132. localError(result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath)
  133. # strTableIncl() for error corrections:
  134. discard strTableIncl(packSym.tab, result)
  135. proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym =
  136. result = graph.getModule(fileIdx)
  137. if result == nil:
  138. #growCache gMemCacheData, fileIdx
  139. #gMemCacheData[fileIdx].needsRecompile = Probing
  140. result = newModule(graph, fileIdx)
  141. var rd: PRodReader
  142. result.flags = result.flags + flags
  143. if sfMainModule in result.flags:
  144. gMainPackageId = result.owner.id
  145. if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
  146. rd = handleSymbolFile(result, cache)
  147. if result.id < 0:
  148. internalError("handleSymbolFile should have set the module's ID")
  149. return
  150. else:
  151. result.id = getID()
  152. discard processModule(graph, result,
  153. if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil,
  154. rd, cache)
  155. #if optCaasEnabled in gGlobalOptions:
  156. # gMemCacheData[fileIdx].needsRecompile = Recompiled
  157. # if validFile: doHash fileIdx
  158. elif graph.isDirty(result):
  159. result.flags.excl sfDirty
  160. # reset module fields:
  161. initStrTable(result.tab)
  162. result.ast = nil
  163. discard processModule(graph, result,
  164. if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil,
  165. nil, cache)
  166. graph.markClientsDirty(fileIdx)
  167. when false:
  168. if checkDepMem(fileIdx) == Yes:
  169. result = compileModule(fileIdx, cache, flags)
  170. else:
  171. result = gCompiledModules[fileIdx]
  172. proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
  173. cache: IdentCache): PSym {.procvar.} =
  174. # this is called by the semantic checking phase
  175. result = compileModule(graph, fileIdx, cache, {})
  176. graph.addDep(s, fileIdx)
  177. #if sfSystemModule in result.flags:
  178. # localError(result.info, errAttemptToRedefine, result.name.s)
  179. # restore the notes for outer module:
  180. gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
  181. else: ForeignPackageNotes
  182. proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
  183. cache: IdentCache): PNode {.procvar.} =
  184. result = syntaxes.parseFile(fileIdx, cache)
  185. graph.addDep(s, fileIdx)
  186. graph.addIncludeDep(s.position.int32, fileIdx)
  187. proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
  188. if magicsys.systemModule == nil:
  189. systemFileIdx = fileInfoIdx(options.libpath/"system.nim")
  190. discard graph.compileModule(systemFileIdx, cache, {sfSystemModule})
  191. proc wantMainModule* =
  192. if gProjectFull.len == 0:
  193. fatal(gCmdLineInfo, errCommandExpectsFilename)
  194. gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
  195. passes.gIncludeFile = includeModule
  196. passes.gImportModule = importModule
  197. proc compileProject*(graph: ModuleGraph; cache: IdentCache;
  198. projectFileIdx = -1'i32) =
  199. wantMainModule()
  200. let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
  201. let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
  202. graph.importStack.add projectFile
  203. if projectFile == systemFileIdx:
  204. discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
  205. else:
  206. graph.compileSystemModule(cache)
  207. discard graph.compileModule(projectFile, cache, {sfMainModule})
  208. proc makeModule*(graph: ModuleGraph; filename: string): PSym =
  209. result = graph.newModule(fileInfoIdx filename)
  210. result.id = getID()
  211. proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule"stdin"