summary refs log tree commit diff stats
path: root/compiler/extccomp.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/extccomp.nim')
-rw-r--r--compiler/extccomp.nim740
1 files changed, 740 insertions, 0 deletions
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
new file mode 100644
index 000000000..186a3884d
--- /dev/null
+++ b/compiler/extccomp.nim
@@ -0,0 +1,740 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Module providing functions for calling the different external C compilers
+# Uses some hard-wired facts about each C/C++ compiler, plus options read
+# from a configuration file, to provide generalized procedures to compile
+# nim files.
+
+import
+  lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
+
+type
+  TSystemCC* = enum
+    ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
+    ccTcc, ccPcc, ccUcc, ccIcl
+  TInfoCCProp* = enum         # properties of the C compiler:
+    hasSwitchRange,           # CC allows ranges in switch statements (GNU C)
+    hasComputedGoto,          # CC has computed goto (GNU C extension)
+    hasCpp,                   # CC is/contains a C++ compiler
+    hasAssume,                # CC has __assume (Visual C extension)
+    hasGcGuard,               # CC supports GC_GUARD to keep stack roots
+    hasGnuAsm,                # CC's asm uses the absurd GNU assembler syntax
+    hasDeclspec,              # CC has __declspec(X)
+    hasAttribute,             # CC has __attribute__((X))
+  TInfoCCProps* = set[TInfoCCProp]
+  TInfoCC* = tuple[
+    name: string,        # the short name of the compiler
+    objExt: string,      # the compiler's object file extenstion
+    optSpeed: string,    # the options for optimization for speed
+    optSize: string,     # the options for optimization for size
+    compilerExe: string, # the compiler's executable
+    cppCompiler: string, # name of the C++ compiler's executable (if supported)
+    compileTmpl: string, # the compile command template
+    buildGui: string,    # command to build a GUI application
+    buildDll: string,    # command to build a shared library
+    buildLib: string,    # command to build a static library
+    linkerExe: string,   # the linker's executable (if not matching compiler's)
+    linkTmpl: string,    # command to link files to produce an exe
+    includeCmd: string,  # command to add an include dir
+    linkDirCmd: string,  # command to add a lib dir
+    linkLibCmd: string,  # command to link an external library
+    debug: string,       # flags for debug build
+    pic: string,         # command for position independent code
+                         # used on some platforms
+    asmStmtFrmt: string, # format of ASM statement
+    structStmtFmt: string, # Format for struct statement
+    packedPragma: string,  # Attribute/pragma to make struct packed (1-byte aligned)
+    props: TInfoCCProps] # properties of the C compiler
+
+
+# Configuration settings for various compilers.
+# When adding new compilers, the cmake sources could be a good reference:
+# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
+
+template compiler(name: expr, settings: stmt): stmt {.immediate.} =
+  proc name: TInfoCC {.compileTime.} = settings
+
+# GNU C and C++ Compiler
+compiler gcc:
+  result = (
+    name: "gcc",
+    objExt: "o",
+    optSpeed: " -O3 -ffast-math ",
+    optSize: " -Os -ffast-math ",
+    compilerExe: "gcc",
+    cppCompiler: "g++",
+    compileTmpl: "-c $options $include -o $objfile $file",
+    buildGui: " -mwindows",
+    buildDll: " -shared",
+    buildLib: "ar rcs $libfile $objfiles",
+    linkerExe: "",
+    linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
+    includeCmd: " -I",
+    linkDirCmd: " -L",
+    linkLibCmd: " -l$1",
+    debug: "",
+    pic: "-fPIC",
+    asmStmtFrmt: "asm($1);$n",
+    structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
+    packedPragma: "__attribute__((__packed__))",
+    props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
+            hasAttribute})
+
+# LLVM Frontend for GCC/G++
+compiler llvmGcc:
+  result = gcc() # Uses settings from GCC
+
+  result.name = "llvm_gcc"
+  result.compilerExe = "llvm-gcc"
+  result.cppCompiler = "llvm-g++"
+  result.buildLib = "llvm-ar rcs $libfile $objfiles"
+
+# Clang (LLVM) C/C++ Compiler
+compiler clang:
+  result = llvmGcc() # Uses settings from llvmGcc
+
+  result.name = "clang"
+  result.compilerExe = "clang"
+  result.cppCompiler = "clang++"
+
+# Microsoft Visual C/C++ Compiler
+compiler vcc:
+  result = (
+    name: "vcc",
+    objExt: "obj",
+    optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
+    optSize: " /O1 /G7 ",
+    compilerExe: "cl",
+    cppCompiler: "cl",
+    compileTmpl: "/c $options $include /Fo$objfile $file",
+    buildGui: " /link /SUBSYSTEM:WINDOWS ",
+    buildDll: " /LD",
+    buildLib: "lib /OUT:$libfile $objfiles",
+    linkerExe: "cl",
+    linkTmpl: "$options $builddll /Fe$exefile $objfiles $buildgui",
+    includeCmd: " /I",
+    linkDirCmd: " /LIBPATH:",
+    linkLibCmd: " $1.lib",
+    debug: " /GZ /Zi ",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$3$n$1 $2",
+    packedPragma: "#pragma pack(1)",
+    props: {hasCpp, hasAssume, hasDeclspec})
+
+# Intel C/C++ Compiler
+compiler icl:
+  # Intel compilers try to imitate the native ones (gcc and msvc)
+  when defined(windows):
+    result = vcc()
+  else:
+    result = gcc()
+
+  result.name = "icl"
+  result.compilerExe = "icl"
+  result.linkerExe = "icl"
+
+# Local C Compiler
+compiler lcc:
+  result = (
+    name: "lcc",
+    objExt: "obj",
+    optSpeed: " -O -p6 ",
+    optSize: " -O -p6 ",
+    compilerExe: "lcc",
+    cppCompiler: "",
+    compileTmpl: "$options $include -Fo$objfile $file",
+    buildGui: " -subsystem windows",
+    buildDll: " -dll",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "lcclnk",
+    linkTmpl: "$options $buildgui $builddll -O $exefile $objfiles",
+    includeCmd: " -I",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: " -g5 ",
+    pic: "",
+    asmStmtFrmt: "_asm{$n$1$n}$n",
+    structStmtFmt: "$1 $2",
+    packedPragma: "", # XXX: not supported yet
+    props: {})
+
+# Borland C Compiler
+compiler bcc:
+  result = (
+    name: "bcc",
+    objExt: "obj",
+    optSpeed: " -O2 -6 ",
+    optSize: " -O1 -6 ",
+    compilerExe: "bcc32",
+    cppCompiler: "",
+    compileTmpl: "-c $options $include -o$objfile $file",
+    buildGui: " -tW",
+    buildDll: " -tWD",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "bcc32",
+    linkTmpl: "$options $buildgui $builddll -e$exefile $objfiles",
+    includeCmd: " -I",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: "",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$1 $2",
+    packedPragma: "", # XXX: not supported yet
+    props: {hasCpp})
+
+# Digital Mars C Compiler
+compiler dmc:
+  result = (
+    name: "dmc",
+    objExt: "obj",
+    optSpeed: " -ff -o -6 ",
+    optSize: " -ff -o -6 ",
+    compilerExe: "dmc",
+    cppCompiler: "",
+    compileTmpl: "-c $options $include -o$objfile $file",
+    buildGui: " -L/exet:nt/su:windows",
+    buildDll: " -WD",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "dmc",
+    linkTmpl: "$options $buildgui $builddll -o$exefile $objfiles",
+    includeCmd: " -I",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: " -g ",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$3$n$1 $2",
+    packedPragma: "#pragma pack(1)",
+    props: {hasCpp})
+
+# Watcom C Compiler
+compiler wcc:
+  result = (
+    name: "wcc",
+    objExt: "obj",
+    optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
+    optSize: "",
+    compilerExe: "wcl386",
+    cppCompiler: "",
+    compileTmpl: "-c $options $include -fo=$objfile $file",
+    buildGui: " -bw",
+    buildDll: " -bd",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "wcl386",
+    linkTmpl: "$options $buildgui $builddll -fe=$exefile $objfiles ",
+    includeCmd: " -i=",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: " -d2 ",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$1 $2",
+    packedPragma: "", # XXX: not supported yet
+    props: {hasCpp})
+
+# Tiny C Compiler
+compiler tcc:
+  result = (
+    name: "tcc",
+    objExt: "o",
+    optSpeed: "",
+    optSize: "",
+    compilerExe: "tcc",
+    cppCompiler: "",
+    compileTmpl: "-c $options $include -o $objfile $file",
+    buildGui: "UNAVAILABLE!",
+    buildDll: " -shared",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "tcc",
+    linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
+    includeCmd: " -I",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: " -g ",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$1 $2",
+    packedPragma: "", # XXX: not supported yet
+    props: {hasSwitchRange, hasComputedGoto})
+
+# Pelles C Compiler
+compiler pcc:
+  # Pelles C
+  result = (
+    name: "pcc",
+    objExt: "obj",
+    optSpeed: " -Ox ",
+    optSize: " -Os ",
+    compilerExe: "cc",
+    cppCompiler: "",
+    compileTmpl: "-c $options $include -Fo$objfile $file",
+    buildGui: " -SUBSYSTEM:WINDOWS",
+    buildDll: " -DLL",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "cc",
+    linkTmpl: "$options $buildgui $builddll -OUT:$exefile $objfiles",
+    includeCmd: " -I",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: " -Zi ",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$1 $2",
+    packedPragma: "", # XXX: not supported yet
+    props: {})
+
+# Your C Compiler
+compiler ucc:
+  result = (
+    name: "ucc",
+    objExt: "o",
+    optSpeed: " -O3 ",
+    optSize: " -O1 ",
+    compilerExe: "cc",
+    cppCompiler: "",
+    compileTmpl: "-c $options $include -o $objfile $file",
+    buildGui: "",
+    buildDll: " -shared ",
+    buildLib: "", # XXX: not supported yet
+    linkerExe: "cc",
+    linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
+    includeCmd: " -I",
+    linkDirCmd: "", # XXX: not supported yet
+    linkLibCmd: "", # XXX: not supported yet
+    debug: "",
+    pic: "",
+    asmStmtFrmt: "__asm{$n$1$n}$n",
+    structStmtFmt: "$1 $2",
+    packedPragma: "", # XXX: not supported yet
+    props: {})
+
+const
+  CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
+    gcc(),
+    llvmGcc(),
+    clang(),
+    lcc(),
+    bcc(),
+    dmc(),
+    wcc(),
+    vcc(),
+    tcc(),
+    pcc(),
+    ucc(),
+    icl()]
+
+  hExt* = ".h"
+
+var
+  cCompiler* = ccGcc # the used compiler
+  gMixedMode*: bool  # true if some module triggered C++ codegen
+  cIncludes*: seq[string] = @[]   # directories to search for included files
+  cLibs*: seq[string] = @[]       # directories to search for lib files
+  cLinkedLibs*: seq[string] = @[] # libraries to link
+
+# implementation
+
+proc libNameTmpl(): string {.inline.} =
+  result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
+
+var
+  toLink, toCompile, externalToCompile: TLinkedList
+  linkOptions: string = ""
+  compileOptions: string = ""
+  ccompilerpath: string = ""
+
+proc nameToCC*(name: string): TSystemCC =
+  ## Returns the kind of compiler referred to by `name`, or ccNone
+  ## if the name doesn't refer to any known compiler.
+  for i in countup(succ(ccNone), high(TSystemCC)):
+    if cmpIgnoreStyle(name, CC[i].name) == 0:
+      return i
+  result = ccNone
+
+proc getConfigVar(c: TSystemCC, suffix: string): string =
+  # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
+  # for niminst support
+  let fullSuffix =
+    if gCmd == cmdCompileToCpp:
+      ".cpp" & suffix
+    elif gCmd == cmdCompileToOC:
+      ".objc" & suffix
+    elif gCmd == cmdCompileToJS:
+      ".js" & suffix
+    else:
+      suffix
+
+  if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
+      optCompileOnly notin gGlobalOptions:
+    let fullCCname = platform.CPU[targetCPU].name & '.' &
+                     platform.OS[targetOS].name & '.' &
+                     CC[c].name & fullSuffix
+    result = getConfigVar(fullCCname)
+    if result.len == 0:
+      # not overriden for this cross compilation setting?
+      result = getConfigVar(CC[c].name & fullSuffix)
+  else:
+    result = getConfigVar(CC[c].name & fullSuffix)
+
+proc setCC*(ccname: string) =
+  cCompiler = nameToCC(ccname)
+  if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
+  compileOptions = getConfigVar(cCompiler, ".options.always")
+  linkOptions = ""
+  ccompilerpath = getConfigVar(cCompiler, ".path")
+  for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
+  defineSymbol(CC[cCompiler].name)
+
+proc addOpt(dest: var string, src: string) =
+  if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
+  add(dest, src)
+
+proc addLinkOption*(option: string) =
+  addOpt(linkOptions, option)
+
+proc addCompileOption*(option: string) =
+  if strutils.find(compileOptions, option, 0) < 0:
+    addOpt(compileOptions, option)
+
+proc initVars*() =
+  # we need to define the symbol here, because ``CC`` may have never been set!
+  for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
+  defineSymbol(CC[cCompiler].name)
+  addCompileOption(getConfigVar(cCompiler, ".options.always"))
+  #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
+  if len(ccompilerpath) == 0:
+    ccompilerpath = getConfigVar(cCompiler, ".path")
+
+proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
+  result = completeGeneratedFilePath(cfile, createSubDir)
+
+proc toObjFile*(filename: string): string =
+  # Object file for compilation
+  result = changeFileExt(filename, CC[cCompiler].objExt)
+
+proc addFileToCompile*(filename: string) =
+  appendStr(toCompile, filename)
+
+proc resetCompilationLists* =
+  initLinkedList(toCompile)
+  ## 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 CRC check the external files
+  # Maybe we can do that in checkDep on the other hand?
+  initLinkedList(externalToCompile)
+  initLinkedList(toLink)
+
+proc addFileToLink*(filename: string) =
+  prependStr(toLink, filename)
+  # BUGFIX: was ``appendStr``
+
+proc execWithEcho(cmd: string, prettyCmd = ""): int =
+  if optListCmd in gGlobalOptions or gVerbosity > 0:
+    if prettyCmd != "":
+      msgWriteln(prettyCmd)
+    else:
+      msgWriteln(cmd)
+  result = execCmd(cmd)
+
+proc execExternalProgram*(cmd: string, prettyCmd = "") =
+  if execWithEcho(cmd, prettyCmd) != 0:
+    rawMessage(errExecutionOfProgramFailed, "")
+
+proc generateScript(projectFile: string, script: Rope) =
+  let (dir, name, ext) = splitFile(projectFile)
+  writeRope(script, dir / addFileExt("compile_" & name,
+                                     platform.OS[targetOS].scriptExt))
+
+proc getOptSpeed(c: TSystemCC): string =
+  result = getConfigVar(c, ".options.speed")
+  if result == "":
+    result = CC[c].optSpeed   # use default settings from this file
+
+proc getDebug(c: TSystemCC): string =
+  result = getConfigVar(c, ".options.debug")
+  if result == "":
+    result = CC[c].debug      # use default settings from this file
+
+proc getOptSize(c: TSystemCC): string =
+  result = getConfigVar(c, ".options.size")
+  if result == "":
+    result = CC[c].optSize    # use default settings from this file
+
+proc noAbsolutePaths: bool {.inline.} =
+  # We used to check current OS != specified OS, but this makes no sense
+  # really: Cross compilation from Linux to Linux for example is entirely
+  # reasonable.
+  # `optGenMapping` is included here for niminst.
+  result = gGlobalOptions * {optGenScript, optGenMapping} != {}
+
+const
+  specialFileA = 42
+  specialFileB = 42
+
+var fileCounter: int
+
+proc add(s: var string, many: openArray[string]) =
+  s.add many.join
+
+proc cFileSpecificOptions(cfilename: string): string =
+  result = compileOptions
+  var trunk = splitFile(cfilename).name
+  if optCDebug in gGlobalOptions:
+    var key = trunk & ".debug"
+    if existsConfigVar(key): addOpt(result, getConfigVar(key))
+    else: addOpt(result, getDebug(cCompiler))
+  if optOptimizeSpeed in gOptions:
+    var key = trunk & ".speed"
+    if existsConfigVar(key): addOpt(result, getConfigVar(key))
+    else: addOpt(result, getOptSpeed(cCompiler))
+  elif optOptimizeSize in gOptions:
+    var key = trunk & ".size"
+    if existsConfigVar(key): addOpt(result, getConfigVar(key))
+    else: addOpt(result, getOptSize(cCompiler))
+  var key = trunk & ".always"
+  if existsConfigVar(key): addOpt(result, getConfigVar(key))
+
+proc getCompileOptions: string =
+  result = cFileSpecificOptions("__dummy__")
+
+proc getLinkOptions: string =
+  result = linkOptions
+  for linkedLib in items(cLinkedLibs):
+    result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
+  for libDir in items(cLibs):
+    result.add([CC[cCompiler].linkDirCmd, libDir.quoteShell])
+
+proc needsExeExt(): bool {.inline.} =
+  result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
+           (platform.hostOS == osWindows)
+
+proc getCompilerExe(compiler: TSystemCC): string =
+  result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler
+           else: CC[compiler].compilerExe
+  if result.len == 0:
+    rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
+
+proc getLinkerExe(compiler: TSystemCC): string =
+  result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
+           elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
+           else: compiler.getCompilerExe
+
+proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
+  var c = cCompiler
+  var options = cFileSpecificOptions(cfilename)
+  var exe = getConfigVar(c, ".exe")
+  if exe.len == 0: exe = c.getCompilerExe
+
+  if needsExeExt(): exe = addFileExt(exe, "exe")
+  if optGenDynLib in gGlobalOptions and
+      ospNeedsPIC in platform.OS[targetOS].props:
+    add(options, ' ' & CC[c].pic)
+
+  var includeCmd, compilePattern: string
+  if not noAbsolutePaths():
+    # compute include paths:
+    includeCmd = CC[c].includeCmd & quoteShell(libpath)
+
+    for includeDir in items(cIncludes):
+      includeCmd.add([CC[c].includeCmd, includeDir.quoteShell])
+
+    compilePattern = joinPath(ccompilerpath, exe)
+  else:
+    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))
+  objfile = quoteShell(objfile)
+  cfile = quoteShell(cfile)
+  result = quoteShell(compilePattern % [
+    "file", cfile, "objfile", objfile, "options", options,
+    "include", includeCmd, "nim", getPrefixDir(),
+    "nim", getPrefixDir(), "lib", libpath])
+  add(result, ' ')
+  addf(result, CC[c].compileTmpl, [
+    "file", cfile, "objfile", objfile,
+    "options", options, "include", includeCmd,
+    "nim", quoteShell(getPrefixDir()),
+    "nim", quoteShell(getPrefixDir()),
+    "lib", quoteShell(libpath)])
+
+proc footprint(filename: string): TCrc32 =
+  # note, '><' further modifies a crc value with a string.
+  result = crcFromFile(filename) ><
+      platform.OS[targetOS].name ><
+      platform.CPU[targetCPU].name ><
+      extccomp.CC[extccomp.cCompiler].name ><
+      getCompileCFileCmd(filename, true)
+
+proc externalFileChanged(filename: string): bool =
+  if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
+    return false
+
+  var crcFile = toGeneratedFile(filename.withPackageName, "crc")
+  var currentCrc = int(footprint(filename))
+  var f: File
+  if open(f, crcFile, fmRead):
+    var line = newStringOfCap(40)
+    if not f.readLine(line): line = "0"
+    close(f)
+    var oldCrc = parseInt(line)
+    result = oldCrc != currentCrc
+  else:
+    result = true
+  if result:
+    if open(f, crcFile, fmWrite):
+      f.writeln($currentCrc)
+      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)
+    if optCompileOnly notin gGlobalOptions:
+      add(cmds, compileCmd)
+      let (dir, name, ext) = splitFile(it.data)
+      add(prettyCmds, "CC: " & name)
+    if optGenScript in gGlobalOptions:
+      add(script, compileCmd)
+      add(script, tnl)
+    it = PStrEntry(it.next)
+
+proc callCCompiler*(projectfile: string) =
+  var
+    linkCmd, buildgui, builddll: 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 = @[]
+  var prettyCmds: TStringSeq = @[]
+  let prettyCb = proc (idx: int) =
+    echo prettyCmds[idx]
+  compileCFile(toCompile, script, cmds, prettyCmds, false)
+  compileCFile(externalToCompile, script, cmds, prettyCmds, true)
+  if optCompileOnly notin gGlobalOptions:
+    if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
+    var res = 0
+    if gNumberOfProcessors <= 1:
+      for i in countup(0, high(cmds)):
+        res = execWithEcho(cmds[i])
+        if res != 0: rawMessage(errExecutionOfProgramFailed, [])
+    elif optListCmd in gGlobalOptions or gVerbosity > 1:
+      res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams},
+                          gNumberOfProcessors)
+    elif gVerbosity == 1:
+      res = execProcesses(cmds, {poUsePath, poParentStreams},
+                          gNumberOfProcessors, prettyCb)
+    else:
+      res = execProcesses(cmds, {poUsePath, poParentStreams},
+                          gNumberOfProcessors)
+    if res != 0:
+      if gNumberOfProcessors <= 1:
+        rawMessage(errExecutionOfProgramFailed, [])
+      else:
+        rawMessage(errGenerated, " execution of an external program failed; " &
+                   "rerun with --parallelBuild:1 to see the error message")
+  if optNoLinking notin gGlobalOptions:
+    # call the linker:
+    var it = PStrEntry(toLink.head)
+    var objfiles = ""
+    while it != nil:
+      let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
+      add(objfiles, ' ')
+      add(objfiles, quoteShell(
+          addFileExt(objFile, CC[cCompiler].objExt)))
+      it = PStrEntry(it.next)
+
+    if optGenStaticLib in gGlobalOptions:
+      let name = splitFile(gProjectName).name
+      linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
+                                  "objfiles", objfiles]
+    else:
+      var linkerExe = getConfigVar(c, ".linkerexe")
+      if len(linkerExe) == 0: linkerExe = c.getLinkerExe
+      if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
+      if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
+      else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
+      if optGenGuiApp in gGlobalOptions: buildgui = CC[c].buildGui
+      else: buildgui = ""
+      var exefile: string
+      if optGenDynLib in gGlobalOptions:
+        exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
+        builddll = CC[c].buildDll
+      else:
+        exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
+        builddll = ""
+      if options.outFile.len > 0:
+        exefile = options.outFile.expandTilde
+      if not noAbsolutePaths():
+        if not exefile.isAbsolute():
+          exefile = joinPath(splitFile(projectfile).dir, exefile)
+      exefile = quoteShell(exefile)
+      let linkOptions = getLinkOptions() & " " &
+                        getConfigVar(cCompiler, ".options.linker")
+      linkCmd = quoteShell(linkCmd % ["builddll", builddll,
+          "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
+          "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
+      linkCmd.add ' '
+      addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
+          "buildgui", buildgui, "options", linkOptions,
+          "objfiles", objfiles, "exefile", exefile,
+          "nim", quoteShell(getPrefixDir()),
+          "lib", quoteShell(libpath)])
+    if optCompileOnly notin gGlobalOptions:
+      if gVerbosity == 1:
+        execExternalProgram(linkCmd, "[Linking]")
+      else:
+        execExternalProgram(linkCmd)
+  else:
+    linkCmd = ""
+  if optGenScript in gGlobalOptions:
+    add(script, linkCmd)
+    add(script, tnl)
+    generateScript(projectfile, script)
+
+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 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()))
+
+  add(code, "\n[Linker]\nFlags=")
+  add(code, strutils.escape(getLinkOptions() & " " &
+                            getConfigVar(cCompiler, ".options.linker")))
+
+  add(code, "\n[Environment]\nlibpath=")
+  add(code, strutils.escape(libpath))
+
+  addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
+  writeRope(code, joinPath(gProjectPath, "mapping.txt"))