summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2017-01-27 22:54:31 +0100
committerAndreas Rumpf <rumpf_a@web.de>2017-01-28 08:54:53 +0100
commit03a1c3b077f9f35d1b07b0f5990dececb35f52a2 (patch)
treef4f998b3adbc1ffb3e0306fbc188f51d2299be0f /compiler
parent0e16d43196efdf901f359744c24636ab2b18f325 (diff)
downloadNim-03a1c3b077f9f35d1b07b0f5990dececb35f52a2.tar.gz
.compile pragma supports patterns and actions
Diffstat (limited to 'compiler')
-rw-r--r--compiler/cgen.nim67
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/extccomp.nim142
-rw-r--r--compiler/pragmas.nim32
4 files changed, 142 insertions, 103 deletions
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index e478b07cb..476b1362f 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -849,15 +849,15 @@ proc addIntTypes(result: var Rope) {.inline.} =
   addf(result, "#define NIM_INTBITS $1" & tnl, [
     platform.CPU[targetCPU].intSize.rope])
 
-proc getCopyright(cfile: string): Rope =
+proc getCopyright(cfile: Cfile): Rope =
   if optCompileOnly in gGlobalOptions:
     result = ("/* Generated by Nim Compiler v$1 */$N" &
-        "/*   (c) 2017 Andreas Rumpf */$N" &
+        "/*   (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
         "/* The generated code is subject to the original license. */$N") %
         [rope(VersionAsString)]
   else:
     result = ("/* Generated by Nim Compiler v$1 */$N" &
-        "/*   (c) 2017 Andreas Rumpf */$N" &
+        "/*   (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
         "/* The generated code is subject to the original license. */$N" &
         "/* Compiled for: $2, $3, $4 */$N" &
         "/* Command for C compiler:$n   $5 */$N") %
@@ -867,7 +867,7 @@ proc getCopyright(cfile: string): Rope =
         rope(extccomp.CC[extccomp.cCompiler].name),
         rope(getCompileCFileCmd(cfile))]
 
-proc getFileHeader(cfile: string): Rope =
+proc getFileHeader(cfile: Cfile): Rope =
   result = getCopyright(cfile)
   addIntTypes(result)
 
@@ -1096,7 +1096,7 @@ proc genInitCode(m: BModule) =
         [(i.ord - '0'.ord).rope, el]
       add(m.s[cfsInitProc], ex)
 
-proc genModule(m: BModule, cfile: string): Rope =
+proc genModule(m: BModule, cfile: Cfile): Rope =
   result = getFileHeader(cfile)
   result.add(genMergeInfo(m))
 
@@ -1227,7 +1227,11 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
     incl g.generatedHeader.flags, isHeaderFile
 
 proc writeHeader(m: BModule) =
-  var result = getCopyright(m.filename)
+  var result = ("/* Generated by Nim Compiler v$1 */$N" &
+        "/*   (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
+        "/* The generated code is subject to the original license. */$N") %
+        [rope(VersionAsString)]
+
   var guard = "__$1__" % [m.filename.splitFile.name.rope]
   result.addf("#ifndef $1$n#define $1$n", [guard])
   addIntTypes(result)
@@ -1281,20 +1285,19 @@ proc finishModule(m: BModule) =
   dec(m.g.forwardedProcsCounter, i)
   setLen(m.forwardedProcs, 0)
 
-proc shouldRecompile(code: Rope, cfile: string): bool =
+proc shouldRecompile(code: Rope, cfile: Cfile): bool =
   result = true
   if optForceFullMake notin gGlobalOptions:
-    var objFile = toObjFile(cfile)
-
-    if not equalsFile(code, cfile):
+    if not equalsFile(code, cfile.cname):
       if isDefined("nimdiff"):
-        copyFile(cfile, cfile & ".backup")
-        echo "diff ", cfile, ".backup ", cfile
-      writeRope(code, cfile)
+        copyFile(cfile.cname, cfile.cname & ".backup")
+        echo "diff ", cfile.cname, ".backup ", cfile.cname
+      writeRope(code, cfile.cname)
       return
-    if existsFile(objFile) and os.fileNewer(objFile, cfile): result = false
+    if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
+      result = false
   else:
-    writeRope(code, cfile)
+    writeRope(code, cfile.cname)
 
 # We need 2 different logics here: pending modules (including
 # 'nim__dat') may require file merging for the combination of dead code
@@ -1304,8 +1307,7 @@ proc shouldRecompile(code: Rope, cfile: string): bool =
 
 proc writeModule(m: BModule, pending: bool) =
   # generate code for the init statements of the module:
-  var cfile = getCFile(m)
-  var cfilenoext = changeFileExt(cfile, "")
+  let cfile = getCFile(m)
 
   if not m.fromCache or optForceFullMake in gGlobalOptions:
     genInitCode(m)
@@ -1315,42 +1317,45 @@ proc writeModule(m: BModule, pending: bool) =
       add(m.s[cfsProcHeaders], m.g.mainModProcs)
       generateThreadVarsSize(m)
 
-    var code = genModule(m, cfile)
+    var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
+    var code = genModule(m, cf)
     when hasTinyCBackend:
       if gCmd == cmdRun:
         tccgen.compileCCode($code)
         return
 
-    if shouldRecompile(code, cfile):
-      addFileToCompile(cfile)
+    if not shouldRecompile(code, cf): cf.flags = {CfileFlag.Cached}
+    addFileToCompile(cf)
   elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
+    let cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
     mergeFiles(cfile, m)
     genInitCode(m)
     finishTypeDescriptions(m)
-    var code = genModule(m, cfile)
+    var code = genModule(m, cf)
     writeRope(code, cfile)
-    addFileToCompile(cfile)
-  elif not existsFile(toObjFile(cfilenoext)):
+    addFileToCompile(cf)
+  else:
     # Consider: first compilation compiles ``system.nim`` and produces
     # ``system.c`` but then compilation fails due to an error. This means
     # that ``system.o`` is missing, so we need to call the C compiler for it:
-    addFileToCompile(cfile)
-
-  addFileToLink(cfilenoext)
+    var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
+    if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
+    addFileToCompile(cf)
 
 proc updateCachedModule(m: BModule) =
   let cfile = getCFile(m)
-  let cfilenoext = changeFileExt(cfile, "")
+  var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
 
   if mergeRequired(m) and sfMainModule notin m.module.flags:
     mergeFiles(cfile, m)
     genInitCode(m)
     finishTypeDescriptions(m)
-    var code = genModule(m, cfile)
-    writeRope(code, cfile)
-    addFileToCompile(cfile)
 
-  addFileToLink(cfilenoext)
+    var code = genModule(m, cf)
+    writeRope(code, cfile)
+  else:
+    cf.flags = {CfileFlag.Cached}
+  addFileToCompile(cf)
 
 proc myClose(b: PPassContext, n: PNode): PNode =
   result = n
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 1ddc85b23..61189fba1 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -193,9 +193,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
 proc processCompile(filename: string) =
   var found = findFile(filename)
   if found == "": found = filename
-  var trunc = changeFileExt(found, "")
   extccomp.addExternalFileToCompile(found)
-  extccomp.addFileToLink(completeCFilePath(trunc, false))
 
 proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
   case switch.normalize
@@ -370,7 +368,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     if pass in {passCmd2, passPP}: processCompile(arg)
   of "link":
     expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: addFileToLink(arg)
+    if pass in {passCmd2, passPP}: addExternalFileToLink(arg)
   of "debuginfo":
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optCDebug)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index cdd93f075..36097a172 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -379,11 +379,23 @@ const
 proc libNameTmpl(): string {.inline.} =
   result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
 
+type
+  CfileFlag* {.pure.} = enum
+    Cached,    ## no need to recompile this time
+    External   ## file was introduced via .compile pragma
+
+  Cfile* = object
+    cname*, obj*: string
+    flags*: set[CFileFlag]
+  CfileList = seq[Cfile]
+
 var
-  toLink, toCompile, externalToCompile: TLinkedList
+  externalToLink: TLinkedList # files to link in addition to the file
+                              # we compiled
   linkOptions: string = ""
   compileOptions: string = ""
   ccompilerpath: string = ""
+  toCompile: CfileList = @[]
 
 proc nameToCC*(name: string): TSystemCC =
   ## Returns the kind of compiler referred to by `name`, or ccNone
@@ -452,22 +464,24 @@ proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
 
 proc toObjFile*(filename: string): string =
   # Object file for compilation
+  #if filename.endsWith(".cpp"):
+  #  result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
+  #else:
   result = changeFileExt(filename, CC[cCompiler].objExt)
 
-proc addFileToCompile*(filename: string) =
-  appendStr(toCompile, filename)
+proc addFileToCompile*(cf: Cfile) =
+  toCompile.add(cf)
 
 proc resetCompilationLists* =
-  initLinkedList(toCompile)
+  toCompile.setLen 0
   ## XXX: we must associate these with their originating module
   # when the module is loaded/unloaded it adds/removes its items
   # That's because we still need to hash check the external files
   # Maybe we can do that in checkDep on the other hand?
-  initLinkedList(externalToCompile)
-  initLinkedList(toLink)
+  initLinkedList(externalToLink)
 
-proc addFileToLink*(filename: string) =
-  prependStr(toLink, filename)
+proc addExternalFileToLink*(filename: string) =
+  prependStr(externalToLink, filename)
   # BUGFIX: was ``appendStr``
 
 proc execWithEcho(cmd: string, msg = hintExecuting): int =
@@ -505,8 +519,6 @@ proc noAbsolutePaths: bool {.inline.} =
   # `optGenMapping` is included here for niminst.
   result = gGlobalOptions * {optGenScript, optGenMapping} != {}
 
-var fileCounter: int
-
 proc add(s: var string, many: openArray[string]) =
   s.add many.join
 
@@ -553,9 +565,9 @@ proc getLinkerExe(compiler: TSystemCC): string =
            elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
            else: compiler.getCompilerExe
 
-proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
+proc getCompileCFileCmd*(cfile: Cfile): string =
   var c = cCompiler
-  if cfilename.endswith(".asm"):
+  if cfile.cname.endswith(".asm"):
     var customAssembler = getConfigVar("assembler")
     if customAssembler.len > 0:
       c = nameToCC(customAssembler)
@@ -570,7 +582,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
     elif c notin cValidAssemblers:
       rawMessage(errExternalAssemblerNotValid, customAssembler)
 
-  var options = cFileSpecificOptions(cfilename)
+  var options = cFileSpecificOptions(cfile.cname)
   var exe = getConfigVar(c, ".exe")
   if exe.len == 0: exe = c.getCompilerExe
 
@@ -592,40 +604,48 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
     includeCmd = ""
     compilePattern = c.getCompilerExe
 
-  var cfile = if noAbsolutePaths(): extractFilename(cfilename)
-              else: cfilename
-  var objfile = if not isExternal or noAbsolutePaths():
-                  toObjFile(cfile)
-                else:
-                  completeCFilePath(toObjFile(cfile))
+  var cf = if noAbsolutePaths(): extractFilename(cfile.cname)
+           else: cfile.cname
+
+  var objfile =
+    if cfile.obj.len == 0:
+      if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths():
+        toObjFile(cf)
+      else:
+        completeCFilePath(toObjFile(cf))
+    elif noAbsolutePaths():
+      extractFilename(cfile.obj)
+    else:
+      cfile.obj
+
   objfile = quoteShell(objfile)
-  cfile = quoteShell(cfile)
+  cf = quoteShell(cf)
   result = quoteShell(compilePattern % [
-    "file", cfile, "objfile", objfile, "options", options,
+    "file", cf, "objfile", objfile, "options", options,
     "include", includeCmd, "nim", getPrefixDir(),
     "nim", getPrefixDir(), "lib", libpath])
   add(result, ' ')
   addf(result, CC[c].compileTmpl, [
-    "file", cfile, "objfile", objfile,
+    "file", cf, "objfile", objfile,
     "options", options, "include", includeCmd,
     "nim", quoteShell(getPrefixDir()),
     "nim", quoteShell(getPrefixDir()),
     "lib", quoteShell(libpath)])
 
-proc footprint(filename: string): SecureHash =
+proc footprint(cfile: Cfile): SecureHash =
   result = secureHash(
-    $secureHashFile(filename) &
+    $secureHashFile(cfile.cname) &
     platform.OS[targetOS].name &
     platform.CPU[targetCPU].name &
     extccomp.CC[extccomp.cCompiler].name &
-    getCompileCFileCmd(filename, true))
+    getCompileCFileCmd(cfile))
 
-proc externalFileChanged(filename: string): bool =
+proc externalFileChanged(cfile: Cfile): bool =
   if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
     return false
 
-  var hashFile = toGeneratedFile(filename.withPackageName, "sha1")
-  var currentHash = footprint(filename)
+  var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1")
+  var currentHash = footprint(cfile)
   var f: File
   if open(f, hashFile, fmRead):
     let oldHash = parseSecureHash(f.readLine())
@@ -639,23 +659,29 @@ proc externalFileChanged(filename: string): bool =
       close(f)
 
 proc addExternalFileToCompile*(filename: string) =
-  if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
-    appendStr(externalToCompile, filename)
-
-proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
-                  prettyCmds: var TStringSeq, isExternal: bool) =
-  var it = PStrEntry(list.head)
-  while it != nil:
-    inc(fileCounter)          # call the C compiler for the .c file:
-    var compileCmd = getCompileCFileCmd(it.data, isExternal)
+  let c = Cfile(cname: filename,
+    obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)),
+    flags: {CfileFlag.External})
+  if optForceFullMake in gGlobalOptions or externalFileChanged(c):
+    toCompile.add(c)
+
+proc addExternalFileToCompile*(c: Cfile) =
+  if optForceFullMake in gGlobalOptions or externalFileChanged(c):
+    toCompile.add(c)
+
+proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq,
+                  prettyCmds: var TStringSeq) =
+  for it in list:
+    # call the C compiler for the .c file:
+    if it.flags.contains(CfileFlag.Cached): continue
+    var compileCmd = getCompileCFileCmd(it)
     if optCompileOnly notin gGlobalOptions:
       add(cmds, compileCmd)
-      let (dir, name, ext) = splitFile(it.data)
+      let (_, name, _) = splitFile(it.cname)
       add(prettyCmds, "CC: " & name)
     if optGenScript in gGlobalOptions:
       add(script, compileCmd)
       add(script, tnl)
-    it = PStrEntry(it.next)
 
 proc getLinkCmd(projectfile, objfiles: string): string =
   if optGenStaticLib in gGlobalOptions:
@@ -712,7 +738,6 @@ proc callCCompiler*(projectfile: string) =
   if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
     return # speed up that call if only compiling and no script shall be
            # generated
-  fileCounter = 0
   #var c = cCompiler
   var script: Rope = nil
   var cmds: TStringSeq = @[]
@@ -725,8 +750,7 @@ proc callCCompiler*(projectfile: string) =
       rawMessage(errGenerated, "execution of an external compiler program '" &
         cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
         p.outputStream.readAll.strip)
-  compileCFile(toCompile, script, cmds, prettyCmds, false)
-  compileCFile(externalToCompile, script, cmds, prettyCmds, true)
+  compileCFile(toCompile, script, cmds, prettyCmds)
   if optCompileOnly notin gGlobalOptions:
     if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
     var res = 0
@@ -748,7 +772,7 @@ proc callCCompiler*(projectfile: string) =
         rawMessage(errExecutionOfProgramFailed, cmds.join())
   if optNoLinking notin gGlobalOptions:
     # call the linker:
-    var it = PStrEntry(toLink.head)
+    var it = PStrEntry(externalToLink.head)
     var objfiles = ""
     while it != nil:
       let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
@@ -756,6 +780,9 @@ proc callCCompiler*(projectfile: string) =
       add(objfiles, quoteShell(
           addFileExt(objFile, CC[cCompiler].objExt)))
       it = PStrEntry(it.next)
+    for x in toCompile:
+      add(objfiles, ' ')
+      add(objfiles, x.obj)
 
     linkCmd = getLinkCmd(projectfile, objfiles)
     if optCompileOnly notin gGlobalOptions:
@@ -780,16 +807,17 @@ proc writeJsonBuildInstructions*(projectfile: string) =
     else:
       f.write escapeJson(x)
 
-  proc cfiles(f: File; buf: var string; list: TLinkedList, isExternal: bool) =
-    var it = PStrEntry(list.head)
-    while it != nil:
-      let compileCmd = getCompileCFileCmd(it.data, isExternal)
+  proc cfiles(f: File; buf: var string; list: CfileList, isExternal: bool) =
+    var i = 0
+    for it in list:
+      if CfileFlag.Cached in it.flags: continue
+      let compileCmd = getCompileCFileCmd(it)
       lit "["
-      str it.data
+      str it.cname
       lit ", "
       str compileCmd
-      it = PStrEntry(it.next)
-      if it == nil:
+      inc i
+      if i == list.len:
         lit "]\L"
       else:
         lit "],\L"
@@ -816,28 +844,24 @@ proc writeJsonBuildInstructions*(projectfile: string) =
   if open(f, jsonFile, fmWrite):
     lit "{\"compile\":[\L"
     cfiles(f, buf, toCompile, false)
-    lit "],\L\"extcompile\":[\L"
-    cfiles(f, buf, externalToCompile, true)
     lit "],\L\"link\":[\L"
     var objfiles = ""
-    linkfiles(f, buf, objfiles, toLink)
+    # XXX add every file here that is to link
+    linkfiles(f, buf, objfiles, externalToLink)
 
     lit "],\L\"linkcmd\": "
     str getLinkCmd(projectfile, objfiles)
     lit "\L}\L"
     close(f)
 
-proc genMappingFiles(list: TLinkedList): Rope =
-  var it = PStrEntry(list.head)
-  while it != nil:
-    addf(result, "--file:r\"$1\"$N", [rope(it.data)])
-    it = PStrEntry(it.next)
+proc genMappingFiles(list: CFileList): Rope =
+  for it in list:
+    addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
 
 proc writeMapping*(gSymbolMapping: Rope) =
   if optGenMapping notin gGlobalOptions: return
   var code = rope("[C_Files]\n")
   add(code, genMappingFiles(toCompile))
-  add(code, genMappingFiles(externalToCompile))
   add(code, "\n[C_Compiler]\nFlags=")
   add(code, strutils.escape(getCompileOptions()))
 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 3bed7fa03..3808b8f58 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -402,29 +402,41 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string =
       if result.len == 0: result = s
 
 proc processCompile(c: PContext, n: PNode) =
-  var s = expectStrLit(c, n)
-  var found = parentDir(n.info.toFullPath) / s
-  if '*' in found:
+
+  proc getStrLit(c: PContext, n: PNode; i: int): string =
+    n.sons[i] = c.semConstExpr(c, n.sons[i])
+    case n.sons[i].kind
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      shallowCopy(result, n.sons[i].strVal)
+    else:
+      localError(n.info, errStringLiteralExpected)
+      result = ""
+
+  let it = if n.kind == nkExprColonExpr: n.sons[1] else: n
+  if it.kind == nkPar and it.len == 2:
+    let s = getStrLit(c, it, 0)
+    let dest = getStrLit(c, it, 1)
+    var found = parentDir(n.info.toFullPath) / s
     for f in os.walkFiles(found):
-      let trunc = f.changeFileExt("")
-      extccomp.addExternalFileToCompile(f)
-      extccomp.addFileToLink(completeCFilePath(trunc, false))
+      let nameOnly = extractFilename(f)
+      extccomp.addExternalFileToCompile(Cfile(cname: f,
+          obj: dest % nameOnly, flags: {CfileFlag.External}))
   else:
+    let s = expectStrLit(c, n)
+    var found = parentDir(n.info.toFullPath) / s
     if not fileExists(found):
       if isAbsolute(s): found = s
       else:
         found = findFile(s)
         if found.len == 0: found = s
-    let trunc = found.changeFileExt("")
     extccomp.addExternalFileToCompile(found)
-    extccomp.addFileToLink(completeCFilePath(trunc, false))
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
   let found = relativeFile(c, n, CC[cCompiler].objExt)
   case feature
-  of linkNormal: extccomp.addFileToLink(found)
+  of linkNormal: extccomp.addExternalFileToLink(found)
   of linkSys:
-    extccomp.addFileToLink(libpath / completeCFilePath(found, false))
+    extccomp.addExternalFileToLink(libpath / completeCFilePath(found, false))
   else: internalError(n.info, "processCommonLink")
 
 proc pragmaBreakpoint(c: PContext, n: PNode) =