extccomp.nim 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Module providing functions for calling the different external C compilers
  10. # Uses some hard-wired facts about each C/C++ compiler, plus options read
  11. # from a configuration file, to provide generalized procedures to compile
  12. # nim files.
  13. import
  14. ropes, os, strutils, osproc, platform, condsyms, options, msgs,
  15. securehash, streams
  16. #from debuginfo import writeDebugInfo
  17. type
  18. TSystemCC* = enum
  19. ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
  20. ccTcc, ccPcc, ccUcc, ccIcl
  21. TInfoCCProp* = enum # properties of the C compiler:
  22. hasSwitchRange, # CC allows ranges in switch statements (GNU C)
  23. hasComputedGoto, # CC has computed goto (GNU C extension)
  24. hasCpp, # CC is/contains a C++ compiler
  25. hasAssume, # CC has __assume (Visual C extension)
  26. hasGcGuard, # CC supports GC_GUARD to keep stack roots
  27. hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
  28. hasDeclspec, # CC has __declspec(X)
  29. hasAttribute, # CC has __attribute__((X))
  30. TInfoCCProps* = set[TInfoCCProp]
  31. TInfoCC* = tuple[
  32. name: string, # the short name of the compiler
  33. objExt: string, # the compiler's object file extension
  34. optSpeed: string, # the options for optimization for speed
  35. optSize: string, # the options for optimization for size
  36. compilerExe: string, # the compiler's executable
  37. cppCompiler: string, # name of the C++ compiler's executable (if supported)
  38. compileTmpl: string, # the compile command template
  39. buildGui: string, # command to build a GUI application
  40. buildDll: string, # command to build a shared library
  41. buildLib: string, # command to build a static library
  42. linkerExe: string, # the linker's executable (if not matching compiler's)
  43. linkTmpl: string, # command to link files to produce an exe
  44. includeCmd: string, # command to add an include dir
  45. linkDirCmd: string, # command to add a lib dir
  46. linkLibCmd: string, # command to link an external library
  47. debug: string, # flags for debug build
  48. pic: string, # command for position independent code
  49. # used on some platforms
  50. asmStmtFrmt: string, # format of ASM statement
  51. structStmtFmt: string, # Format for struct statement
  52. props: TInfoCCProps] # properties of the C compiler
  53. # Configuration settings for various compilers.
  54. # When adding new compilers, the cmake sources could be a good reference:
  55. # http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
  56. template compiler(name, settings: untyped): untyped =
  57. proc name: TInfoCC {.compileTime.} = settings
  58. # GNU C and C++ Compiler
  59. compiler gcc:
  60. result = (
  61. name: "gcc",
  62. objExt: "o",
  63. optSpeed: " -O3 -ffast-math ",
  64. optSize: " -Os -ffast-math ",
  65. compilerExe: "gcc",
  66. cppCompiler: "g++",
  67. compileTmpl: "-c $options $include -o $objfile $file",
  68. buildGui: " -mwindows",
  69. buildDll: " -shared",
  70. buildLib: "ar rcs $libfile $objfiles",
  71. linkerExe: "",
  72. linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
  73. includeCmd: " -I",
  74. linkDirCmd: " -L",
  75. linkLibCmd: " -l$1",
  76. debug: "",
  77. pic: "-fPIC",
  78. asmStmtFrmt: "asm($1);$n",
  79. structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
  80. props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
  81. hasAttribute})
  82. # LLVM Frontend for GCC/G++
  83. compiler llvmGcc:
  84. result = gcc() # Uses settings from GCC
  85. result.name = "llvm_gcc"
  86. result.compilerExe = "llvm-gcc"
  87. result.cppCompiler = "llvm-g++"
  88. result.buildLib = "llvm-ar rcs $libfile $objfiles"
  89. # Clang (LLVM) C/C++ Compiler
  90. compiler clang:
  91. result = llvmGcc() # Uses settings from llvmGcc
  92. result.name = "clang"
  93. result.compilerExe = "clang"
  94. result.cppCompiler = "clang++"
  95. # Microsoft Visual C/C++ Compiler
  96. compiler vcc:
  97. result = (
  98. name: "vcc",
  99. objExt: "obj",
  100. optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
  101. optSize: " /O1 /G7 ",
  102. compilerExe: "cl",
  103. cppCompiler: "cl",
  104. compileTmpl: "/c $options $include /Fo$objfile $file",
  105. buildGui: " /link /SUBSYSTEM:WINDOWS ",
  106. buildDll: " /LD",
  107. buildLib: "lib /OUT:$libfile $objfiles",
  108. linkerExe: "cl",
  109. linkTmpl: "$options $builddll /Fe$exefile $objfiles $buildgui",
  110. includeCmd: " /I",
  111. linkDirCmd: " /LIBPATH:",
  112. linkLibCmd: " $1.lib",
  113. debug: " /RTC1 /Z7 ",
  114. pic: "",
  115. asmStmtFrmt: "__asm{$n$1$n}$n",
  116. structStmtFmt: "$3$n$1 $2",
  117. props: {hasCpp, hasAssume, hasDeclspec})
  118. # Intel C/C++ Compiler
  119. compiler icl:
  120. # Intel compilers try to imitate the native ones (gcc and msvc)
  121. when defined(windows):
  122. result = vcc()
  123. else:
  124. result = gcc()
  125. result.name = "icl"
  126. result.compilerExe = "icl"
  127. result.linkerExe = "icl"
  128. # Local C Compiler
  129. compiler lcc:
  130. result = (
  131. name: "lcc",
  132. objExt: "obj",
  133. optSpeed: " -O -p6 ",
  134. optSize: " -O -p6 ",
  135. compilerExe: "lcc",
  136. cppCompiler: "",
  137. compileTmpl: "$options $include -Fo$objfile $file",
  138. buildGui: " -subsystem windows",
  139. buildDll: " -dll",
  140. buildLib: "", # XXX: not supported yet
  141. linkerExe: "lcclnk",
  142. linkTmpl: "$options $buildgui $builddll -O $exefile $objfiles",
  143. includeCmd: " -I",
  144. linkDirCmd: "", # XXX: not supported yet
  145. linkLibCmd: "", # XXX: not supported yet
  146. debug: " -g5 ",
  147. pic: "",
  148. asmStmtFrmt: "_asm{$n$1$n}$n",
  149. structStmtFmt: "$1 $2",
  150. props: {})
  151. # Borland C Compiler
  152. compiler bcc:
  153. result = (
  154. name: "bcc",
  155. objExt: "obj",
  156. optSpeed: " -O2 -6 ",
  157. optSize: " -O1 -6 ",
  158. compilerExe: "bcc32",
  159. cppCompiler: "",
  160. compileTmpl: "-c $options $include -o$objfile $file",
  161. buildGui: " -tW",
  162. buildDll: " -tWD",
  163. buildLib: "", # XXX: not supported yet
  164. linkerExe: "bcc32",
  165. linkTmpl: "$options $buildgui $builddll -e$exefile $objfiles",
  166. includeCmd: " -I",
  167. linkDirCmd: "", # XXX: not supported yet
  168. linkLibCmd: "", # XXX: not supported yet
  169. debug: "",
  170. pic: "",
  171. asmStmtFrmt: "__asm{$n$1$n}$n",
  172. structStmtFmt: "$1 $2",
  173. props: {hasCpp})
  174. # Digital Mars C Compiler
  175. compiler dmc:
  176. result = (
  177. name: "dmc",
  178. objExt: "obj",
  179. optSpeed: " -ff -o -6 ",
  180. optSize: " -ff -o -6 ",
  181. compilerExe: "dmc",
  182. cppCompiler: "",
  183. compileTmpl: "-c $options $include -o$objfile $file",
  184. buildGui: " -L/exet:nt/su:windows",
  185. buildDll: " -WD",
  186. buildLib: "", # XXX: not supported yet
  187. linkerExe: "dmc",
  188. linkTmpl: "$options $buildgui $builddll -o$exefile $objfiles",
  189. includeCmd: " -I",
  190. linkDirCmd: "", # XXX: not supported yet
  191. linkLibCmd: "", # XXX: not supported yet
  192. debug: " -g ",
  193. pic: "",
  194. asmStmtFrmt: "__asm{$n$1$n}$n",
  195. structStmtFmt: "$3$n$1 $2",
  196. props: {hasCpp})
  197. # Watcom C Compiler
  198. compiler wcc:
  199. result = (
  200. name: "wcc",
  201. objExt: "obj",
  202. optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
  203. optSize: "",
  204. compilerExe: "wcl386",
  205. cppCompiler: "",
  206. compileTmpl: "-c $options $include -fo=$objfile $file",
  207. buildGui: " -bw",
  208. buildDll: " -bd",
  209. buildLib: "", # XXX: not supported yet
  210. linkerExe: "wcl386",
  211. linkTmpl: "$options $buildgui $builddll -fe=$exefile $objfiles ",
  212. includeCmd: " -i=",
  213. linkDirCmd: "", # XXX: not supported yet
  214. linkLibCmd: "", # XXX: not supported yet
  215. debug: " -d2 ",
  216. pic: "",
  217. asmStmtFrmt: "__asm{$n$1$n}$n",
  218. structStmtFmt: "$1 $2",
  219. props: {hasCpp})
  220. # Tiny C Compiler
  221. compiler tcc:
  222. result = (
  223. name: "tcc",
  224. objExt: "o",
  225. optSpeed: "",
  226. optSize: "",
  227. compilerExe: "tcc",
  228. cppCompiler: "",
  229. compileTmpl: "-c $options $include -o $objfile $file",
  230. buildGui: "UNAVAILABLE!",
  231. buildDll: " -shared",
  232. buildLib: "", # XXX: not supported yet
  233. linkerExe: "tcc",
  234. linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
  235. includeCmd: " -I",
  236. linkDirCmd: "", # XXX: not supported yet
  237. linkLibCmd: "", # XXX: not supported yet
  238. debug: " -g ",
  239. pic: "",
  240. asmStmtFrmt: "__asm{$n$1$n}$n",
  241. structStmtFmt: "$1 $2",
  242. props: {hasSwitchRange, hasComputedGoto})
  243. # Pelles C Compiler
  244. compiler pcc:
  245. # Pelles C
  246. result = (
  247. name: "pcc",
  248. objExt: "obj",
  249. optSpeed: " -Ox ",
  250. optSize: " -Os ",
  251. compilerExe: "cc",
  252. cppCompiler: "",
  253. compileTmpl: "-c $options $include -Fo$objfile $file",
  254. buildGui: " -SUBSYSTEM:WINDOWS",
  255. buildDll: " -DLL",
  256. buildLib: "", # XXX: not supported yet
  257. linkerExe: "cc",
  258. linkTmpl: "$options $buildgui $builddll -OUT:$exefile $objfiles",
  259. includeCmd: " -I",
  260. linkDirCmd: "", # XXX: not supported yet
  261. linkLibCmd: "", # XXX: not supported yet
  262. debug: " -Zi ",
  263. pic: "",
  264. asmStmtFrmt: "__asm{$n$1$n}$n",
  265. structStmtFmt: "$1 $2",
  266. props: {})
  267. # Your C Compiler
  268. compiler ucc:
  269. result = (
  270. name: "ucc",
  271. objExt: "o",
  272. optSpeed: " -O3 ",
  273. optSize: " -O1 ",
  274. compilerExe: "cc",
  275. cppCompiler: "",
  276. compileTmpl: "-c $options $include -o $objfile $file",
  277. buildGui: "",
  278. buildDll: " -shared ",
  279. buildLib: "", # XXX: not supported yet
  280. linkerExe: "cc",
  281. linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
  282. includeCmd: " -I",
  283. linkDirCmd: "", # XXX: not supported yet
  284. linkLibCmd: "", # XXX: not supported yet
  285. debug: "",
  286. pic: "",
  287. asmStmtFrmt: "__asm{$n$1$n}$n",
  288. structStmtFmt: "$1 $2",
  289. props: {})
  290. const
  291. CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
  292. gcc(),
  293. llvmGcc(),
  294. clang(),
  295. lcc(),
  296. bcc(),
  297. dmc(),
  298. wcc(),
  299. vcc(),
  300. tcc(),
  301. pcc(),
  302. ucc(),
  303. icl()]
  304. hExt* = ".h"
  305. var
  306. cCompiler* = ccGcc # the used compiler
  307. gMixedMode*: bool # true if some module triggered C++ codegen
  308. cIncludes*: seq[string] = @[] # directories to search for included files
  309. cLibs*: seq[string] = @[] # directories to search for lib files
  310. cLinkedLibs*: seq[string] = @[] # libraries to link
  311. # implementation
  312. proc libNameTmpl(): string {.inline.} =
  313. result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
  314. type
  315. CfileFlag* {.pure.} = enum
  316. Cached, ## no need to recompile this time
  317. External ## file was introduced via .compile pragma
  318. Cfile* = object
  319. cname*, obj*: string
  320. flags*: set[CFileFlag]
  321. CfileList = seq[Cfile]
  322. var
  323. externalToLink: seq[string] = @[] # files to link in addition to the file
  324. # we compiled
  325. linkOptionsCmd: string = ""
  326. compileOptionsCmd: seq[string] = @[]
  327. linkOptions: string = ""
  328. compileOptions: string = ""
  329. ccompilerpath: string = ""
  330. toCompile: CfileList = @[]
  331. proc nameToCC*(name: string): TSystemCC =
  332. ## Returns the kind of compiler referred to by `name`, or ccNone
  333. ## if the name doesn't refer to any known compiler.
  334. for i in countup(succ(ccNone), high(TSystemCC)):
  335. if cmpIgnoreStyle(name, CC[i].name) == 0:
  336. return i
  337. result = ccNone
  338. proc getConfigVar(c: TSystemCC, suffix: string): string =
  339. # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
  340. # for niminst support
  341. let fullSuffix =
  342. if gCmd == cmdCompileToCpp:
  343. ".cpp" & suffix
  344. elif gCmd == cmdCompileToOC:
  345. ".objc" & suffix
  346. elif gCmd == cmdCompileToJS:
  347. ".js" & suffix
  348. else:
  349. suffix
  350. if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
  351. optCompileOnly notin gGlobalOptions:
  352. let fullCCname = platform.CPU[targetCPU].name & '.' &
  353. platform.OS[targetOS].name & '.' &
  354. CC[c].name & fullSuffix
  355. result = getConfigVar(fullCCname)
  356. if result.len == 0:
  357. # not overriden for this cross compilation setting?
  358. result = getConfigVar(CC[c].name & fullSuffix)
  359. else:
  360. result = getConfigVar(CC[c].name & fullSuffix)
  361. proc setCC*(ccname: string) =
  362. cCompiler = nameToCC(ccname)
  363. if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
  364. compileOptions = getConfigVar(cCompiler, ".options.always")
  365. linkOptions = ""
  366. ccompilerpath = getConfigVar(cCompiler, ".path")
  367. for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
  368. defineSymbol(CC[cCompiler].name)
  369. proc addOpt(dest: var string, src: string) =
  370. if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
  371. add(dest, src)
  372. proc addLinkOption*(option: string) =
  373. addOpt(linkOptions, option)
  374. proc addCompileOption*(option: string) =
  375. if strutils.find(compileOptions, option, 0) < 0:
  376. addOpt(compileOptions, option)
  377. proc addLinkOptionCmd*(option: string) =
  378. addOpt(linkOptionsCmd, option)
  379. proc addCompileOptionCmd*(option: string) =
  380. compileOptionsCmd.add(option)
  381. proc initVars*() =
  382. # we need to define the symbol here, because ``CC`` may have never been set!
  383. for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
  384. defineSymbol(CC[cCompiler].name)
  385. addCompileOption(getConfigVar(cCompiler, ".options.always"))
  386. #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
  387. if len(ccompilerpath) == 0:
  388. ccompilerpath = getConfigVar(cCompiler, ".path")
  389. proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
  390. result = completeGeneratedFilePath(cfile, createSubDir)
  391. proc toObjFile*(filename: string): string =
  392. # Object file for compilation
  393. #if filename.endsWith(".cpp"):
  394. # result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
  395. #else:
  396. result = changeFileExt(filename, CC[cCompiler].objExt)
  397. proc addFileToCompile*(cf: Cfile) =
  398. toCompile.add(cf)
  399. proc resetCompilationLists* =
  400. toCompile.setLen 0
  401. ## XXX: we must associate these with their originating module
  402. # when the module is loaded/unloaded it adds/removes its items
  403. # That's because we still need to hash check the external files
  404. # Maybe we can do that in checkDep on the other hand?
  405. externalToLink.setLen 0
  406. proc addExternalFileToLink*(filename: string) =
  407. externalToLink.insert(filename, 0)
  408. proc execWithEcho(cmd: string, msg = hintExecuting): int =
  409. rawMessage(msg, cmd)
  410. result = execCmd(cmd)
  411. proc execExternalProgram*(cmd: string, msg = hintExecuting) =
  412. if execWithEcho(cmd, msg) != 0:
  413. rawMessage(errExecutionOfProgramFailed, cmd)
  414. proc generateScript(projectFile: string, script: Rope) =
  415. let (dir, name, ext) = splitFile(projectFile)
  416. writeRope(script, dir / addFileExt("compile_" & name,
  417. platform.OS[targetOS].scriptExt))
  418. proc getOptSpeed(c: TSystemCC): string =
  419. result = getConfigVar(c, ".options.speed")
  420. if result == "":
  421. result = CC[c].optSpeed # use default settings from this file
  422. proc getDebug(c: TSystemCC): string =
  423. result = getConfigVar(c, ".options.debug")
  424. if result == "":
  425. result = CC[c].debug # use default settings from this file
  426. proc getOptSize(c: TSystemCC): string =
  427. result = getConfigVar(c, ".options.size")
  428. if result == "":
  429. result = CC[c].optSize # use default settings from this file
  430. proc noAbsolutePaths: bool {.inline.} =
  431. # We used to check current OS != specified OS, but this makes no sense
  432. # really: Cross compilation from Linux to Linux for example is entirely
  433. # reasonable.
  434. # `optGenMapping` is included here for niminst.
  435. result = gGlobalOptions * {optGenScript, optGenMapping} != {}
  436. proc cFileSpecificOptions(cfilename: string): string =
  437. result = compileOptions
  438. for option in compileOptionsCmd:
  439. if strutils.find(result, option, 0) < 0:
  440. addOpt(result, option)
  441. var trunk = splitFile(cfilename).name
  442. if optCDebug in gGlobalOptions:
  443. var key = trunk & ".debug"
  444. if existsConfigVar(key): addOpt(result, getConfigVar(key))
  445. else: addOpt(result, getDebug(cCompiler))
  446. if optOptimizeSpeed in gOptions:
  447. var key = trunk & ".speed"
  448. if existsConfigVar(key): addOpt(result, getConfigVar(key))
  449. else: addOpt(result, getOptSpeed(cCompiler))
  450. elif optOptimizeSize in gOptions:
  451. var key = trunk & ".size"
  452. if existsConfigVar(key): addOpt(result, getConfigVar(key))
  453. else: addOpt(result, getOptSize(cCompiler))
  454. var key = trunk & ".always"
  455. if existsConfigVar(key): addOpt(result, getConfigVar(key))
  456. proc getCompileOptions: string =
  457. result = cFileSpecificOptions("__dummy__")
  458. proc getLinkOptions: string =
  459. result = linkOptions & " " & linkOptionsCmd & " "
  460. for linkedLib in items(cLinkedLibs):
  461. result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
  462. for libDir in items(cLibs):
  463. result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell]))
  464. proc needsExeExt(): bool {.inline.} =
  465. result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
  466. (platform.hostOS == osWindows)
  467. proc getCompilerExe(compiler: TSystemCC; cfile: string): string =
  468. result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"):
  469. CC[compiler].cppCompiler
  470. else:
  471. CC[compiler].compilerExe
  472. if result.len == 0:
  473. rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
  474. proc getLinkerExe(compiler: TSystemCC): string =
  475. result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
  476. elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
  477. else: compiler.getCompilerExe("")
  478. proc getCompileCFileCmd*(cfile: Cfile): string =
  479. var c = cCompiler
  480. var options = cFileSpecificOptions(cfile.cname)
  481. var exe = getConfigVar(c, ".exe")
  482. if exe.len == 0: exe = c.getCompilerExe(cfile.cname)
  483. if needsExeExt(): exe = addFileExt(exe, "exe")
  484. if optGenDynLib in gGlobalOptions and
  485. ospNeedsPIC in platform.OS[targetOS].props:
  486. add(options, ' ' & CC[c].pic)
  487. var includeCmd, compilePattern: string
  488. if not noAbsolutePaths():
  489. # compute include paths:
  490. includeCmd = CC[c].includeCmd & quoteShell(libpath)
  491. for includeDir in items(cIncludes):
  492. includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
  493. compilePattern = joinPath(ccompilerpath, exe)
  494. else:
  495. includeCmd = ""
  496. compilePattern = c.getCompilerExe(cfile.cname)
  497. var cf = if noAbsolutePaths(): extractFilename(cfile.cname)
  498. else: cfile.cname
  499. var objfile =
  500. if cfile.obj.len == 0:
  501. if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths():
  502. toObjFile(cf)
  503. else:
  504. completeCFilePath(toObjFile(cf))
  505. elif noAbsolutePaths():
  506. extractFilename(cfile.obj)
  507. else:
  508. cfile.obj
  509. objfile = quoteShell(objfile)
  510. cf = quoteShell(cf)
  511. result = quoteShell(compilePattern % [
  512. "file", cf, "objfile", objfile, "options", options,
  513. "include", includeCmd, "nim", getPrefixDir(),
  514. "nim", getPrefixDir(), "lib", libpath])
  515. add(result, ' ')
  516. addf(result, CC[c].compileTmpl, [
  517. "file", cf, "objfile", objfile,
  518. "options", options, "include", includeCmd,
  519. "nim", quoteShell(getPrefixDir()),
  520. "nim", quoteShell(getPrefixDir()),
  521. "lib", quoteShell(libpath)])
  522. proc footprint(cfile: Cfile): SecureHash =
  523. result = secureHash(
  524. $secureHashFile(cfile.cname) &
  525. platform.OS[targetOS].name &
  526. platform.CPU[targetCPU].name &
  527. extccomp.CC[extccomp.cCompiler].name &
  528. getCompileCFileCmd(cfile))
  529. proc externalFileChanged(cfile: Cfile): bool =
  530. if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
  531. return false
  532. var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1")
  533. var currentHash = footprint(cfile)
  534. var f: File
  535. if open(f, hashFile, fmRead):
  536. let oldHash = parseSecureHash(f.readLine())
  537. close(f)
  538. result = oldHash != currentHash
  539. else:
  540. result = true
  541. if result:
  542. if open(f, hashFile, fmWrite):
  543. f.writeLine($currentHash)
  544. close(f)
  545. proc addExternalFileToCompile*(c: var Cfile) =
  546. if optForceFullMake notin gGlobalOptions and not externalFileChanged(c):
  547. c.flags.incl CfileFlag.Cached
  548. toCompile.add(c)
  549. proc addExternalFileToCompile*(filename: string) =
  550. var c = Cfile(cname: filename,
  551. obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)),
  552. flags: {CfileFlag.External})
  553. addExternalFileToCompile(c)
  554. proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq,
  555. prettyCmds: var TStringSeq) =
  556. for it in list:
  557. # call the C compiler for the .c file:
  558. if it.flags.contains(CfileFlag.Cached): continue
  559. var compileCmd = getCompileCFileCmd(it)
  560. if optCompileOnly notin gGlobalOptions:
  561. add(cmds, compileCmd)
  562. let (_, name, _) = splitFile(it.cname)
  563. add(prettyCmds, "CC: " & name)
  564. if optGenScript in gGlobalOptions:
  565. add(script, compileCmd)
  566. add(script, tnl)
  567. proc getLinkCmd(projectfile, objfiles: string): string =
  568. if optGenStaticLib in gGlobalOptions:
  569. var libname: string
  570. if options.outFile.len > 0:
  571. libname = options.outFile.expandTilde
  572. if not libname.isAbsolute():
  573. libname = getCurrentDir() / libname
  574. else:
  575. libname = (libNameTmpl() % splitFile(gProjectName).name)
  576. result = CC[cCompiler].buildLib % ["libfile", libname,
  577. "objfiles", objfiles]
  578. else:
  579. var linkerExe = getConfigVar(cCompiler, ".linkerexe")
  580. if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
  581. # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
  582. if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
  583. if noAbsolutePaths(): result = linkerExe
  584. else: result = joinPath(ccompilerpath, linkerExe)
  585. let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
  586. else: ""
  587. var exefile, builddll: string
  588. if optGenDynLib in gGlobalOptions:
  589. exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
  590. builddll = CC[cCompiler].buildDll
  591. else:
  592. exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
  593. builddll = ""
  594. if options.outFile.len > 0:
  595. exefile = options.outFile.expandTilde
  596. if not exefile.isAbsolute():
  597. exefile = getCurrentDir() / exefile
  598. if not noAbsolutePaths():
  599. if not exefile.isAbsolute():
  600. exefile = joinPath(splitFile(projectfile).dir, exefile)
  601. when false:
  602. if optCDebug in gGlobalOptions:
  603. writeDebugInfo(exefile.changeFileExt("ndb"))
  604. exefile = quoteShell(exefile)
  605. let linkOptions = getLinkOptions() & " " &
  606. getConfigVar(cCompiler, ".options.linker")
  607. var linkTmpl = getConfigVar(cCompiler, ".linkTmpl")
  608. if linkTmpl.len == 0:
  609. linkTmpl = CC[cCompiler].linkTmpl
  610. result = quoteShell(result % ["builddll", builddll,
  611. "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
  612. "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
  613. result.add ' '
  614. addf(result, linkTmpl, ["builddll", builddll,
  615. "buildgui", buildgui, "options", linkOptions,
  616. "objfiles", objfiles, "exefile", exefile,
  617. "nim", quoteShell(getPrefixDir()),
  618. "lib", quoteShell(libpath)])
  619. template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed =
  620. try:
  621. body
  622. except OSError:
  623. let ose = (ref OSError)(getCurrentException())
  624. if errorPrefix.len > 0:
  625. rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
  626. else:
  627. rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
  628. raise
  629. proc execLinkCmd(linkCmd: string) =
  630. tryExceptOSErrorMessage("invocation of external linker program failed."):
  631. execExternalProgram(linkCmd,
  632. if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
  633. proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
  634. let runCb = proc (idx: int, p: Process) =
  635. let exitCode = p.peekExitCode
  636. if exitCode != 0:
  637. rawMessage(errGenerated, "execution of an external compiler program '" &
  638. cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
  639. p.outputStream.readAll.strip)
  640. if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
  641. var res = 0
  642. if gNumberOfProcessors <= 1:
  643. for i in countup(0, high(cmds)):
  644. tryExceptOSErrorMessage("invocation of external compiler program failed."):
  645. res = execWithEcho(cmds[i])
  646. if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
  647. else:
  648. tryExceptOSErrorMessage("invocation of external compiler program failed."):
  649. if optListCmd in gGlobalOptions or gVerbosity > 1:
  650. res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
  651. gNumberOfProcessors, afterRunEvent=runCb)
  652. elif gVerbosity == 1:
  653. res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
  654. gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
  655. else:
  656. res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
  657. gNumberOfProcessors, afterRunEvent=runCb)
  658. if res != 0:
  659. if gNumberOfProcessors <= 1:
  660. rawMessage(errExecutionOfProgramFailed, cmds.join())
  661. proc callCCompiler*(projectfile: string) =
  662. var
  663. linkCmd: string
  664. if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
  665. return # speed up that call if only compiling and no script shall be
  666. # generated
  667. #var c = cCompiler
  668. var script: Rope = nil
  669. var cmds: TStringSeq = @[]
  670. var prettyCmds: TStringSeq = @[]
  671. let prettyCb = proc (idx: int) =
  672. echo prettyCmds[idx]
  673. compileCFile(toCompile, script, cmds, prettyCmds)
  674. if optCompileOnly notin gGlobalOptions:
  675. execCmdsInParallel(cmds, prettyCb)
  676. if optNoLinking notin gGlobalOptions:
  677. # call the linker:
  678. var objfiles = ""
  679. for it in externalToLink:
  680. let objFile = if noAbsolutePaths(): it.extractFilename else: it
  681. add(objfiles, ' ')
  682. add(objfiles, quoteShell(
  683. addFileExt(objFile, CC[cCompiler].objExt)))
  684. for x in toCompile:
  685. add(objfiles, ' ')
  686. add(objfiles, quoteShell(x.obj))
  687. linkCmd = getLinkCmd(projectfile, objfiles)
  688. if optCompileOnly notin gGlobalOptions:
  689. execLinkCmd(linkCmd)
  690. else:
  691. linkCmd = ""
  692. if optGenScript in gGlobalOptions:
  693. add(script, linkCmd)
  694. add(script, tnl)
  695. generateScript(projectfile, script)
  696. #from json import escapeJson
  697. import json
  698. proc writeJsonBuildInstructions*(projectfile: string) =
  699. template lit(x: untyped) = f.write x
  700. template str(x: untyped) =
  701. when compiles(escapeJson(x, buf)):
  702. buf.setLen 0
  703. escapeJson(x, buf)
  704. f.write buf
  705. else:
  706. f.write escapeJson(x)
  707. proc cfiles(f: File; buf: var string; list: CfileList, isExternal: bool) =
  708. var i = 0
  709. for it in list:
  710. if CfileFlag.Cached in it.flags: continue
  711. let compileCmd = getCompileCFileCmd(it)
  712. lit "["
  713. str it.cname
  714. lit ", "
  715. str compileCmd
  716. inc i
  717. if i == list.len:
  718. lit "]\L"
  719. else:
  720. lit "],\L"
  721. proc linkfiles(f: File; buf, objfiles: var string) =
  722. for i, it in externalToLink:
  723. let
  724. objFile = if noAbsolutePaths(): it.extractFilename else: it
  725. objStr = addFileExt(objFile, CC[cCompiler].objExt)
  726. add(objfiles, ' ')
  727. add(objfiles, objStr)
  728. str objStr
  729. if toCompile.len == 0 and i == externalToLink.high:
  730. lit "\L"
  731. else:
  732. lit ",\L"
  733. for i, x in toCompile:
  734. let objStr = quoteShell(x.obj)
  735. add(objfiles, ' ')
  736. add(objfiles, objStr)
  737. str objStr
  738. if i == toCompile.high:
  739. lit "\L"
  740. else:
  741. lit ",\L"
  742. var buf = newStringOfCap(50)
  743. let file = projectfile.splitFile.name
  744. let jsonFile = toGeneratedFile(file, "json")
  745. var f: File
  746. if open(f, jsonFile, fmWrite):
  747. lit "{\"compile\":[\L"
  748. cfiles(f, buf, toCompile, false)
  749. lit "],\L\"link\":[\L"
  750. var objfiles = ""
  751. # XXX add every file here that is to link
  752. linkfiles(f, buf, objfiles)
  753. lit "],\L\"linkcmd\": "
  754. str getLinkCmd(projectfile, objfiles)
  755. lit "\L}\L"
  756. close(f)
  757. proc runJsonBuildInstructions*(projectfile: string) =
  758. let file = projectfile.splitFile.name
  759. let jsonFile = toGeneratedFile(file, "json")
  760. try:
  761. let data = json.parseFile(jsonFile)
  762. let toCompile = data["compile"]
  763. doAssert toCompile.kind == JArray
  764. var cmds: TStringSeq = @[]
  765. var prettyCmds: TStringSeq = @[]
  766. for c in toCompile:
  767. doAssert c.kind == JArray
  768. doAssert c.len >= 2
  769. add(cmds, c[1].getStr)
  770. let (_, name, _) = splitFile(c[0].getStr)
  771. add(prettyCmds, "CC: " & name)
  772. let prettyCb = proc (idx: int) =
  773. echo prettyCmds[idx]
  774. execCmdsInParallel(cmds, prettyCb)
  775. let linkCmd = data["linkcmd"]
  776. doAssert linkCmd.kind == JString
  777. execLinkCmd(linkCmd.getStr)
  778. except:
  779. echo getCurrentException().getStackTrace()
  780. quit "error evaluating JSON file: " & jsonFile
  781. proc genMappingFiles(list: CFileList): Rope =
  782. for it in list:
  783. addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
  784. proc writeMapping*(gSymbolMapping: Rope) =
  785. if optGenMapping notin gGlobalOptions: return
  786. var code = rope("[C_Files]\n")
  787. add(code, genMappingFiles(toCompile))
  788. add(code, "\n[C_Compiler]\nFlags=")
  789. add(code, strutils.escape(getCompileOptions()))
  790. add(code, "\n[Linker]\nFlags=")
  791. add(code, strutils.escape(getLinkOptions() & " " &
  792. getConfigVar(cCompiler, ".options.linker")))
  793. add(code, "\n[Environment]\nlibpath=")
  794. add(code, strutils.escape(libpath))
  795. addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
  796. writeRope(code, joinPath(gProjectPath, "mapping.txt"))