summary refs log tree commit diff stats
path: root/compiler/main.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/main.nim')
-rwxr-xr-xcompiler/main.nim152
1 files changed, 107 insertions, 45 deletions
diff --git a/compiler/main.nim b/compiler/main.nim
index 448efbd1d..9cb4c22d9 100755
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -12,11 +12,11 @@
 
 import 
   llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, 
-  os, lists, condsyms, rodread, rodwrite, ropes, trees, 
+  os, lists, condsyms, rodread, rodwrite, ropes, trees, times,
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, ecmasgen,
   platform, nimconf, importer, passaux, depends, evals, types, idgen,
-  tables, docgen2, service, magicsys, parser
+  tables, docgen2, service, magicsys, parser, crc
 
 const
   has_LLVM_Backend = false
@@ -28,60 +28,113 @@ proc MainCommand*()
 
 # ------------------ module handling -----------------------------------------
 
+type
+  TModuleInMemory = object
+    compiledAt: float
+    crc: int
+    deps: seq[int32] ## XXX: slurped files are not currently tracked
+    needsRecompile: bool
+
 var
-  compMods = initTable[string, PSym]() # all compiled modules
+  gCompiledModules: seq[PSym] = @[]
+  gMemCacheData: seq[TModuleInMemory] = @[]
+    ## XXX: we should implement recycling of file IDs
+    ## if the user keeps renaming modules, the file IDs will keep growing
+
+proc getModule(fileIdx: int32): PSym =
+  if fileIdx >= 0 and fileIdx < gCompiledModules.len:
+    result = gCompiledModules[fileIdx]
+  else:
+    result = nil
 
-# This expects a normalized module path
-proc registerModule(filename: string, module: PSym) =
-  compMods[filename] = module
+template compiledAt(x: PSym): expr =
+  gMemCacheData[x.position].compiledAt
 
-# This expects a normalized module path
-proc getModule(filename: string): PSym =
-  result = compMods[filename]
+template crc(x: PSym): expr =
+  gMemCacheData[x.position].crc
 
-var gModulesCount = 0
-proc newModule(filename: string): PSym = 
+template addDep(x: Psym, dep: int32) =
+  gMemCacheData[x.position].deps.add(dep)
+
+proc checkDepMem(fileIdx: int32): bool =
+  template markDirty =
+    gMemCacheData[fileIdx].needsRecompile = true
+    return true
+
+  if optForceFullMake in gGlobalOptions or
+     curCaasCmd != lastCaasCmd: markDirty
+  
+  let crc = crcFromFile(fileIdx.toFilename)
+  if crc != gMemCacheData[fileIdx].crc: markDirty
+
+  for dep in gMemCacheData[fileIdx].deps:
+    if checkDepMem(dep): markDirty
+
+  return false
+
+proc newModule(fileIdx: int32): PSym =
   # We cannot call ``newSym`` here, because we have to circumvent the ID
   # mechanism, which we do in order to assign each module a persistent ID. 
   new(result)
   result.id = - 1             # for better error checking
   result.kind = skModule
+  let filename = fileIdx.toFilename
   result.name = getIdent(splitFile(filename).name)
   if not isNimrodIdentifier(result.name.s):
     rawMessage(errInvalidModuleName, result.name.s)
   
   result.owner = result       # a module belongs to itself
-  result.info = newLineInfo(filename, 1, 1)
-  result.position = gModulesCount
-  inc gModulesCount
+  result.info = newLineInfo(fileIdx, 1, 1)
+  result.position = fileIdx
+  
+  growCache gMemCacheData, fileIdx
+  growCache gCompiledModules, fileIdx
+  gCompiledModules[result.position] = result
+  
   incl(result.flags, sfUsed)
   initStrTable(result.tab)
-  RegisterModule(filename, result)
   StrTableAdd(result.tab, result) # a module knows itself
   
-proc CompileModule(filename: string, flags: TSymFlags): PSym
-proc importModule(filename: string): PSym = 
+proc compileModule(fileIdx: int32, flags: TSymFlags): PSym =
+  result = getModule(fileIdx)
+  if result == nil:
+    result = newModule(fileIdx)
+    var rd = handleSymbolFile(result)
+    result.flags = result.flags + flags
+    if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
+      rd = handleSymbolFile(result)
+      if result.id < 0: 
+        InternalError("handleSymbolFile should have set the module\'s ID")
+        return
+    else:
+      result.id = getID()
+    processModule(result, nil, rd)
+    gMemCacheData[fileIdx].compiledAt = gLastCmdTime
+    gMemCacheData[fileIdx].needsRecompile = false
+  else:
+    InternalAssert optCaasEnabled in gGlobalOptions
+    if checkDepMem(fileIdx):
+      gCompiledModules[fileIdx] = nil
+      result = CompileModule(fileIdx, flags)
+    else:
+      result = gCompiledModules[fileIdx]
+
+proc compileModule(filename: string, flags: TSymFlags): PSym =
+  result = compileModule(filename.fileInfoIdx, flags)
+
+proc importModule(s: PSym, fileIdx: int32): PSym =
   # this is called by the semantic checking phase
-  result = getModule(filename)
+  result = getModule(fileIdx)
   if result == nil: 
     # compile the module
-    result = compileModule(filename, {})
+    result = compileModule(fileIdx, {})
+    if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
   elif sfSystemModule in result.flags: 
     LocalError(result.info, errAttemptToRedefine, result.Name.s)
-  
-proc CompileModule(filename: string, flags: TSymFlags): PSym =
-  var rd: PRodReader = nil
-  var f = addFileExt(filename, nimExt)
-  result = newModule(f)
-  result.flags = result.flags + flags
-  if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: 
-    rd = handleSymbolFile(result, f)
-    if result.id < 0: 
-      InternalError("handleSymbolFile should have set the module\'s ID")
-      return
-  else:
-    result.id = getID()
-  processModule(result, f, nil, rd)
+
+proc includeModule(s: PSym, fileIdx: int32): PNode =
+  result = syntaxes.parseFile(fileIdx)
+  if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
 
 proc `==^`(a, b: string): bool =
   try:
@@ -91,7 +144,8 @@ proc `==^`(a, b: string): bool =
 
 proc compileSystemModule =
   if magicsys.SystemModule == nil:
-    discard CompileModule(options.libpath /"system", {sfSystemModule})
+    SystemFileIdx = fileInfoIdx(options.libpath/"system.nim")
+    discard CompileModule(SystemFileIdx, {sfSystemModule})
 
 proc CompileProject(projectFile = gProjectFull) =
   let systemFile = options.libpath / "system"
@@ -140,6 +194,8 @@ proc CommandCompileToC =
   compileProject()
   if gCmd != cmdRun:
     extccomp.CallCCompiler(changeFileExt(gProjectFull, ""))
+  # caas will keep track only of the compilation commands
+  lastCaasCmd = curCaasCmd
 
 when has_LLVM_Backend:
   proc CommandCompileToLLVM =
@@ -171,7 +227,7 @@ proc InteractivePasses =
 var stdinModule: PSym
 proc makeStdinModule: PSym =
   if stdinModule == nil:
-    stdinModule = newModule("stdin")
+    stdinModule = newModule(gCmdLineInfo.fileIndex)
     stdinModule.id = getID()
   result = stdinModule
 
@@ -184,24 +240,23 @@ proc CommandInteractive =
   else:
     var m = makeStdinModule()
     incl(m.flags, sfMainModule)
-    processModule(m, "stdin", LLStreamOpenStdIn(), nil)
+    processModule(m, LLStreamOpenStdIn(), nil)
 
 const evalPasses = [verbosePass, semPass, evalPass]
 
-proc evalNim(nodes: PNode, module: PSym, filename: string) =
-  # we don't want to mess with gPasses here, because in nimrod serve
-  # scenario, it may be set up properly for normal cgenPass() compilation
-  carryPasses(nodes, module, filename, evalPasses)
+proc evalNim(nodes: PNode, module: PSym) =
+  carryPasses(nodes, module, evalPasses)
 
 proc commandEval(exp: string) =
   if SystemModule == nil:
     InteractivePasses()
     compileSystemModule()
   var echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
-  evalNim(echoExp.parseString, makeStdinModule(), "stdin")
+  evalNim(echoExp.parseString, makeStdinModule())
 
 proc CommandPretty =
-  var module = parseFile(addFileExt(mainCommandArg(), NimExt))
+  var projectFile = addFileExt(mainCommandArg(), NimExt)
+  var module = parseFile(projectFile.fileInfoIdx)
   if module != nil: 
     renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt))
   
@@ -231,16 +286,18 @@ proc CommandSuggest =
 proc wantMainModule =
   if gProjectFull.len == 0:
     Fatal(gCmdLineInfo, errCommandExpectsFilename)
+  gProjectMainIdx = addFileExt(gProjectFull, nimExt).fileInfoIdx
   
 proc MainCommand =
   # In "nimrod serve" scenario, each command must reset the registered passes
   clearPasses()
+  gLastCmdTime = epochTime()
   appendStr(searchPaths, options.libpath)
   if gProjectFull.len != 0:
     # current path is always looked first for modules
     prependStr(searchPaths, gProjectPath)
   setID(100)
-  passes.gIncludeFile = syntaxes.parseFile
+  passes.gIncludeFile = includeModule
   passes.gImportModule = importModule
   case command.normalize
   of "c", "cc", "compile", "compiletoc": 
@@ -323,7 +380,7 @@ proc MainCommand =
   of "parse": 
     gCmd = cmdParse
     wantMainModule()
-    discard parseFile(addFileExt(gProjectFull, nimExt))
+    discard parseFile(gProjectMainIdx)
   of "scan": 
     gCmd = cmdScan
     wantMainModule()
@@ -343,9 +400,14 @@ proc MainCommand =
       wantMainModule()
       CommandSuggest()
   of "serve":
-    gCmd = cmdIdeTools
+    gGlobalOptions.incl(optCaasEnabled)
     msgs.gErrorMax = high(int)  # do not stop after first error     
     serve(MainCommand)
     
   else: rawMessage(errInvalidCommandX, command)
+  
+  if msgs.gErrorCounter == 0 and gCmd notin {cmdInterpret, cmdRun}:
+    rawMessage(hintSuccessX, [$gLinesCompiled,
+               formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
+               formatSize(getTotalMem())])