extccomp.nim 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  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 lineinfos file, to provide generalized procedures to compile
  12. # nim files.
  13. import
  14. ropes, os, strutils, osproc, platform, condsyms, options, msgs,
  15. lineinfos, std / sha1, streams, pathutils, sequtils, times
  16. type
  17. TInfoCCProp* = enum # properties of the C compiler:
  18. hasSwitchRange, # CC allows ranges in switch statements (GNU C)
  19. hasComputedGoto, # CC has computed goto (GNU C extension)
  20. hasCpp, # CC is/contains a C++ compiler
  21. hasAssume, # CC has __assume (Visual C extension)
  22. hasGcGuard, # CC supports GC_GUARD to keep stack roots
  23. hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
  24. hasDeclspec, # CC has __declspec(X)
  25. hasAttribute, # CC has __attribute__((X))
  26. TInfoCCProps* = set[TInfoCCProp]
  27. TInfoCC* = tuple[
  28. name: string, # the short name of the compiler
  29. objExt: string, # the compiler's object file extension
  30. optSpeed: string, # the options for optimization for speed
  31. optSize: string, # the options for optimization for size
  32. compilerExe: string, # the compiler's executable
  33. cppCompiler: string, # name of the C++ compiler's executable (if supported)
  34. compileTmpl: string, # the compile command template
  35. buildGui: string, # command to build a GUI application
  36. buildDll: string, # command to build a shared library
  37. buildLib: string, # command to build a static library
  38. linkerExe: string, # the linker's executable (if not matching compiler's)
  39. linkTmpl: string, # command to link files to produce an exe
  40. includeCmd: string, # command to add an include dir
  41. linkDirCmd: string, # command to add a lib dir
  42. linkLibCmd: string, # command to link an external library
  43. debug: string, # flags for debug build
  44. pic: string, # command for position independent code
  45. # used on some platforms
  46. asmStmtFrmt: string, # format of ASM statement
  47. structStmtFmt: string, # Format for struct statement
  48. props: TInfoCCProps] # properties of the C compiler
  49. # Configuration settings for various compilers.
  50. # When adding new compilers, the cmake sources could be a good reference:
  51. # http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
  52. template compiler(name, settings: untyped): untyped =
  53. proc name: TInfoCC {.compileTime.} = settings
  54. # GNU C and C++ Compiler
  55. compiler gcc:
  56. result = (
  57. name: "gcc",
  58. objExt: "o",
  59. optSpeed: " -O3 ",
  60. optSize: " -Os ",
  61. compilerExe: "gcc",
  62. cppCompiler: "g++",
  63. compileTmpl: "-c $options $include -o $objfile $file",
  64. buildGui: " -mwindows",
  65. buildDll: " -shared",
  66. buildLib: "ar rcs $libfile $objfiles",
  67. linkerExe: "",
  68. linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
  69. includeCmd: " -I",
  70. linkDirCmd: " -L",
  71. linkLibCmd: " -l$1",
  72. debug: "",
  73. pic: "-fPIC",
  74. asmStmtFrmt: "asm($1);$n",
  75. structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
  76. props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
  77. hasAttribute})
  78. # GNU C and C++ Compiler
  79. compiler nintendoSwitchGCC:
  80. result = (
  81. name: "switch_gcc",
  82. objExt: "o",
  83. optSpeed: " -O3 ",
  84. optSize: " -Os ",
  85. compilerExe: "aarch64-none-elf-gcc",
  86. cppCompiler: "aarch64-none-elf-g++",
  87. compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
  88. buildGui: " -mwindows",
  89. buildDll: " -shared",
  90. buildLib: "aarch64-none-elf-gcc-ar rcs $libfile $objfiles",
  91. linkerExe: "aarch64-none-elf-gcc",
  92. linkTmpl: "$buildgui $builddll -Wl,-Map,$mapfile -o $exefile $objfiles $options",
  93. includeCmd: " -I",
  94. linkDirCmd: " -L",
  95. linkLibCmd: " -l$1",
  96. debug: "",
  97. pic: "-fPIE",
  98. asmStmtFrmt: "asm($1);$n",
  99. structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
  100. props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
  101. hasAttribute})
  102. # LLVM Frontend for GCC/G++
  103. compiler llvmGcc:
  104. result = gcc() # Uses settings from GCC
  105. result.name = "llvm_gcc"
  106. result.compilerExe = "llvm-gcc"
  107. result.cppCompiler = "llvm-g++"
  108. when defined(macosx):
  109. # OS X has no 'llvm-ar' tool:
  110. result.buildLib = "ar rcs $libfile $objfiles"
  111. else:
  112. result.buildLib = "llvm-ar rcs $libfile $objfiles"
  113. # Clang (LLVM) C/C++ Compiler
  114. compiler clang:
  115. result = llvmGcc() # Uses settings from llvmGcc
  116. result.name = "clang"
  117. result.compilerExe = "clang"
  118. result.cppCompiler = "clang++"
  119. # Microsoft Visual C/C++ Compiler
  120. compiler vcc:
  121. result = (
  122. name: "vcc",
  123. objExt: "obj",
  124. optSpeed: " /Ogityb2 /G7 ",
  125. optSize: " /O1 /G7 ",
  126. compilerExe: "cl",
  127. cppCompiler: "cl",
  128. compileTmpl: "/c$vccplatform$options $include /Fo$objfile $file",
  129. buildGui: " /link /SUBSYSTEM:WINDOWS ",
  130. buildDll: " /LD",
  131. buildLib: "lib /OUT:$libfile $objfiles",
  132. linkerExe: "cl",
  133. linkTmpl: "$options $builddll$vccplatform /Fe$exefile $objfiles $buildgui",
  134. includeCmd: " /I",
  135. linkDirCmd: " /LIBPATH:",
  136. linkLibCmd: " $1.lib",
  137. debug: " /RTC1 /Z7 ",
  138. pic: "",
  139. asmStmtFrmt: "__asm{$n$1$n}$n",
  140. structStmtFmt: "$3$n$1 $2",
  141. props: {hasCpp, hasAssume, hasDeclspec})
  142. compiler clangcl:
  143. result = vcc()
  144. result.name = "clang_cl"
  145. result.compilerExe = "clang-cl"
  146. result.cppCompiler = "clang-cl"
  147. result.linkerExe = "clang-cl"
  148. # Intel C/C++ Compiler
  149. compiler icl:
  150. result = vcc()
  151. result.name = "icl"
  152. result.compilerExe = "icl"
  153. result.linkerExe = "icl"
  154. # Intel compilers try to imitate the native ones (gcc and msvc)
  155. compiler icc:
  156. result = gcc()
  157. result.name = "icc"
  158. result.compilerExe = "icc"
  159. result.linkerExe = "icc"
  160. # Local C Compiler
  161. compiler lcc:
  162. result = (
  163. name: "lcc",
  164. objExt: "obj",
  165. optSpeed: " -O -p6 ",
  166. optSize: " -O -p6 ",
  167. compilerExe: "lcc",
  168. cppCompiler: "",
  169. compileTmpl: "$options $include -Fo$objfile $file",
  170. buildGui: " -subsystem windows",
  171. buildDll: " -dll",
  172. buildLib: "", # XXX: not supported yet
  173. linkerExe: "lcclnk",
  174. linkTmpl: "$options $buildgui $builddll -O $exefile $objfiles",
  175. includeCmd: " -I",
  176. linkDirCmd: "", # XXX: not supported yet
  177. linkLibCmd: "", # XXX: not supported yet
  178. debug: " -g5 ",
  179. pic: "",
  180. asmStmtFrmt: "_asm{$n$1$n}$n",
  181. structStmtFmt: "$1 $2",
  182. props: {})
  183. # Borland C Compiler
  184. compiler bcc:
  185. result = (
  186. name: "bcc",
  187. objExt: "obj",
  188. optSpeed: " -O3 -6 ",
  189. optSize: " -O1 -6 ",
  190. compilerExe: "bcc32c",
  191. cppCompiler: "cpp32c",
  192. compileTmpl: "-c $options $include -o$objfile $file",
  193. buildGui: " -tW",
  194. buildDll: " -tWD",
  195. buildLib: "", # XXX: not supported yet
  196. linkerExe: "bcc32",
  197. linkTmpl: "$options $buildgui $builddll -e$exefile $objfiles",
  198. includeCmd: " -I",
  199. linkDirCmd: "", # XXX: not supported yet
  200. linkLibCmd: "", # XXX: not supported yet
  201. debug: "",
  202. pic: "",
  203. asmStmtFrmt: "__asm{$n$1$n}$n",
  204. structStmtFmt: "$1 $2",
  205. props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
  206. hasAttribute})
  207. # Digital Mars C Compiler
  208. compiler dmc:
  209. result = (
  210. name: "dmc",
  211. objExt: "obj",
  212. optSpeed: " -ff -o -6 ",
  213. optSize: " -ff -o -6 ",
  214. compilerExe: "dmc",
  215. cppCompiler: "",
  216. compileTmpl: "-c $options $include -o$objfile $file",
  217. buildGui: " -L/exet:nt/su:windows",
  218. buildDll: " -WD",
  219. buildLib: "", # XXX: not supported yet
  220. linkerExe: "dmc",
  221. linkTmpl: "$options $buildgui $builddll -o$exefile $objfiles",
  222. includeCmd: " -I",
  223. linkDirCmd: "", # XXX: not supported yet
  224. linkLibCmd: "", # XXX: not supported yet
  225. debug: " -g ",
  226. pic: "",
  227. asmStmtFrmt: "__asm{$n$1$n}$n",
  228. structStmtFmt: "$3$n$1 $2",
  229. props: {hasCpp})
  230. # Watcom C Compiler
  231. compiler wcc:
  232. result = (
  233. name: "wcc",
  234. objExt: "obj",
  235. optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
  236. optSize: "",
  237. compilerExe: "wcl386",
  238. cppCompiler: "",
  239. compileTmpl: "-c $options $include -fo=$objfile $file",
  240. buildGui: " -bw",
  241. buildDll: " -bd",
  242. buildLib: "", # XXX: not supported yet
  243. linkerExe: "wcl386",
  244. linkTmpl: "$options $buildgui $builddll -fe=$exefile $objfiles ",
  245. includeCmd: " -i=",
  246. linkDirCmd: "", # XXX: not supported yet
  247. linkLibCmd: "", # XXX: not supported yet
  248. debug: " -d2 ",
  249. pic: "",
  250. asmStmtFrmt: "__asm{$n$1$n}$n",
  251. structStmtFmt: "$1 $2",
  252. props: {hasCpp})
  253. # Tiny C Compiler
  254. compiler tcc:
  255. result = (
  256. name: "tcc",
  257. objExt: "o",
  258. optSpeed: "",
  259. optSize: "",
  260. compilerExe: "tcc",
  261. cppCompiler: "",
  262. compileTmpl: "-c $options $include -o $objfile $file",
  263. buildGui: "-Wl,-subsystem=gui",
  264. buildDll: " -shared",
  265. buildLib: "", # XXX: not supported yet
  266. linkerExe: "tcc",
  267. linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
  268. includeCmd: " -I",
  269. linkDirCmd: "", # XXX: not supported yet
  270. linkLibCmd: "", # XXX: not supported yet
  271. debug: " -g ",
  272. pic: "",
  273. asmStmtFrmt: "__asm{$n$1$n}$n",
  274. structStmtFmt: "$1 $2",
  275. props: {hasSwitchRange, hasComputedGoto})
  276. # Pelles C Compiler
  277. compiler pcc:
  278. # Pelles C
  279. result = (
  280. name: "pcc",
  281. objExt: "obj",
  282. optSpeed: " -Ox ",
  283. optSize: " -Os ",
  284. compilerExe: "cc",
  285. cppCompiler: "",
  286. compileTmpl: "-c $options $include -Fo$objfile $file",
  287. buildGui: " -SUBSYSTEM:WINDOWS",
  288. buildDll: " -DLL",
  289. buildLib: "", # XXX: not supported yet
  290. linkerExe: "cc",
  291. linkTmpl: "$options $buildgui $builddll -OUT:$exefile $objfiles",
  292. includeCmd: " -I",
  293. linkDirCmd: "", # XXX: not supported yet
  294. linkLibCmd: "", # XXX: not supported yet
  295. debug: " -Zi ",
  296. pic: "",
  297. asmStmtFrmt: "__asm{$n$1$n}$n",
  298. structStmtFmt: "$1 $2",
  299. props: {})
  300. # Your C Compiler
  301. compiler ucc:
  302. result = (
  303. name: "ucc",
  304. objExt: "o",
  305. optSpeed: " -O3 ",
  306. optSize: " -O1 ",
  307. compilerExe: "cc",
  308. cppCompiler: "",
  309. compileTmpl: "-c $options $include -o $objfile $file",
  310. buildGui: "",
  311. buildDll: " -shared ",
  312. buildLib: "", # XXX: not supported yet
  313. linkerExe: "cc",
  314. linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
  315. includeCmd: " -I",
  316. linkDirCmd: "", # XXX: not supported yet
  317. linkLibCmd: "", # XXX: not supported yet
  318. debug: "",
  319. pic: "",
  320. asmStmtFrmt: "__asm{$n$1$n}$n",
  321. structStmtFmt: "$1 $2",
  322. props: {})
  323. const
  324. CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
  325. gcc(),
  326. nintendoSwitchGCC(),
  327. llvmGcc(),
  328. clang(),
  329. lcc(),
  330. bcc(),
  331. dmc(),
  332. wcc(),
  333. vcc(),
  334. tcc(),
  335. pcc(),
  336. ucc(),
  337. icl(),
  338. icc(),
  339. clangcl()]
  340. hExt* = ".h"
  341. proc libNameTmpl(conf: ConfigRef): string {.inline.} =
  342. result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a"
  343. proc nameToCC*(name: string): TSystemCC =
  344. ## Returns the kind of compiler referred to by `name`, or ccNone
  345. ## if the name doesn't refer to any known compiler.
  346. for i in succ(ccNone) .. high(TSystemCC):
  347. if cmpIgnoreStyle(name, CC[i].name) == 0:
  348. return i
  349. result = ccNone
  350. proc listCCnames(): seq[string] =
  351. for i in succ(ccNone) .. high(TSystemCC):
  352. result.add CC[i].name
  353. proc isVSCompatible*(conf: ConfigRef): bool =
  354. return conf.cCompiler == ccVcc or
  355. conf.cCompiler == ccClangCl or
  356. (conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
  357. proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
  358. # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
  359. # for niminst support
  360. let fullSuffix =
  361. if conf.cmd == cmdCompileToCpp:
  362. ".cpp" & suffix
  363. elif conf.cmd == cmdCompileToOC:
  364. ".objc" & suffix
  365. elif conf.cmd == cmdCompileToJS:
  366. ".js" & suffix
  367. else:
  368. suffix
  369. if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and
  370. optCompileOnly notin conf.globalOptions:
  371. let fullCCname = platform.CPU[conf.target.targetCPU].name & '.' &
  372. platform.OS[conf.target.targetOS].name & '.' &
  373. CC[c].name & fullSuffix
  374. result = getConfigVar(conf, fullCCname)
  375. if result.len == 0:
  376. # not overriden for this cross compilation setting?
  377. result = getConfigVar(conf, CC[c].name & fullSuffix)
  378. else:
  379. result = getConfigVar(conf, CC[c].name & fullSuffix)
  380. proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
  381. conf.cCompiler = nameToCC(ccname)
  382. if conf.cCompiler == ccNone:
  383. let ccList = listCCnames().join(", ")
  384. localError(conf, info, "unknown C compiler: '$1'. Available options are: $2" % [ccname, ccList])
  385. conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always")
  386. conf.linkOptions = ""
  387. conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
  388. for i in low(CC) .. high(CC): undefSymbol(conf.symbols, CC[i].name)
  389. defineSymbol(conf.symbols, CC[conf.cCompiler].name)
  390. proc addOpt(dest: var string, src: string) =
  391. if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
  392. add(dest, src)
  393. proc addLinkOption*(conf: ConfigRef; option: string) =
  394. addOpt(conf.linkOptions, option)
  395. proc addCompileOption*(conf: ConfigRef; option: string) =
  396. if strutils.find(conf.compileOptions, option, 0) < 0:
  397. addOpt(conf.compileOptions, option)
  398. proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
  399. addOpt(conf.linkOptionsCmd, option)
  400. proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
  401. conf.compileOptionsCmd.add(option)
  402. proc initVars*(conf: ConfigRef) =
  403. # we need to define the symbol here, because ``CC`` may have never been set!
  404. for i in low(CC) .. high(CC): undefSymbol(conf.symbols, CC[i].name)
  405. defineSymbol(conf.symbols, CC[conf.cCompiler].name)
  406. addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always"))
  407. #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
  408. if len(conf.cCompilerPath) == 0:
  409. conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
  410. proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile,
  411. createSubDir: bool = true): AbsoluteFile =
  412. result = completeGeneratedFilePath(conf, cfile, createSubDir)
  413. proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
  414. # Object file for compilation
  415. result = AbsoluteFile(filename.string & "." & CC[conf.cCompiler].objExt)
  416. proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
  417. conf.toCompile.add(cf)
  418. proc resetCompilationLists*(conf: ConfigRef) =
  419. conf.toCompile.setLen 0
  420. ## XXX: we must associate these with their originating module
  421. # when the module is loaded/unloaded it adds/removes its items
  422. # That's because we still need to hash check the external files
  423. # Maybe we can do that in checkDep on the other hand?
  424. conf.externalToLink.setLen 0
  425. proc addExternalFileToLink*(conf: ConfigRef; filename: AbsoluteFile) =
  426. conf.externalToLink.insert(filename.string, 0)
  427. proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
  428. rawMessage(conf, msg, cmd)
  429. result = execCmd(cmd)
  430. proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
  431. if execWithEcho(conf, cmd, msg) != 0:
  432. rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
  433. cmd)
  434. proc generateScript(conf: ConfigRef; script: Rope) =
  435. let (_, name, _) = splitFile(conf.outFile.string)
  436. let filename = getNimcacheDir(conf) / RelativeFile(addFileExt("compile_" & name,
  437. platform.OS[conf.target.targetOS].scriptExt))
  438. if writeRope(script, filename):
  439. copyFile(conf.libpath / RelativeFile"nimbase.h",
  440. getNimcacheDir(conf) / RelativeFile"nimbase.h")
  441. else:
  442. rawMessage(conf, errGenerated, "could not write to file: " & filename.string)
  443. proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
  444. result = getConfigVar(conf, c, ".options.speed")
  445. if result == "":
  446. result = CC[c].optSpeed # use default settings from this file
  447. proc getDebug(conf: ConfigRef; c: TSystemCC): string =
  448. result = getConfigVar(conf, c, ".options.debug")
  449. if result == "":
  450. result = CC[c].debug # use default settings from this file
  451. proc getOptSize(conf: ConfigRef; c: TSystemCC): string =
  452. result = getConfigVar(conf, c, ".options.size")
  453. if result == "":
  454. result = CC[c].optSize # use default settings from this file
  455. proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
  456. # We used to check current OS != specified OS, but this makes no sense
  457. # really: Cross compilation from Linux to Linux for example is entirely
  458. # reasonable.
  459. # `optGenMapping` is included here for niminst.
  460. result = conf.globalOptions * {optGenScript, optGenMapping} != {}
  461. proc cFileSpecificOptions(conf: ConfigRef; nimname: string): string =
  462. result = conf.compileOptions
  463. for option in conf.compileOptionsCmd:
  464. if strutils.find(result, option, 0) < 0:
  465. addOpt(result, option)
  466. if optCDebug in conf.globalOptions:
  467. let key = nimname & ".debug"
  468. if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
  469. else: addOpt(result, getDebug(conf, conf.cCompiler))
  470. if optOptimizeSpeed in conf.options:
  471. let key = nimname & ".speed"
  472. if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
  473. else: addOpt(result, getOptSpeed(conf, conf.cCompiler))
  474. elif optOptimizeSize in conf.options:
  475. let key = nimname & ".size"
  476. if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
  477. else: addOpt(result, getOptSize(conf, conf.cCompiler))
  478. let key = nimname & ".always"
  479. if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
  480. proc getCompileOptions(conf: ConfigRef): string =
  481. result = cFileSpecificOptions(conf, "__dummy__")
  482. proc vccplatform(conf: ConfigRef): string =
  483. # VCC specific but preferable over the config hacks people
  484. # had to do before, see #11306
  485. case conf.target.targetCPU
  486. of cpuI386:
  487. result = " --platform:x86"
  488. of cpuArm:
  489. result = " --platform:arm"
  490. of cpuAmd64:
  491. result = " --platform:amd64"
  492. else:
  493. result = ""
  494. proc getLinkOptions(conf: ConfigRef): string =
  495. result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
  496. for linkedLib in items(conf.cLinkedLibs):
  497. result.add(CC[conf.cCompiler].linkLibCmd % linkedLib.quoteShell)
  498. for libDir in items(conf.cLibs):
  499. result.add(join([CC[conf.cCompiler].linkDirCmd, libDir.quoteShell]))
  500. proc needsExeExt(conf: ConfigRef): bool {.inline.} =
  501. result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or
  502. (conf.target.hostOS == osWindows)
  503. proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string =
  504. result = if conf.cmd == cmdCompileToCpp and not cfile.string.endsWith(".c"):
  505. CC[compiler].cppCompiler
  506. else:
  507. CC[compiler].compilerExe
  508. if result.len == 0:
  509. rawMessage(conf, errGenerated,
  510. "Compiler '$1' doesn't support the requested target" %
  511. CC[compiler].name)
  512. proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
  513. result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
  514. elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
  515. else: getCompilerExe(conf, compiler, AbsoluteFile"")
  516. proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, isMainFile = false): string =
  517. var c = conf.cCompiler
  518. var options = cFileSpecificOptions(conf, cfile.nimname)
  519. var exe = getConfigVar(conf, c, ".exe")
  520. if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
  521. if needsExeExt(conf): exe = addFileExt(exe, "exe")
  522. if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and
  523. ospNeedsPIC in platform.OS[conf.target.targetOS].props:
  524. add(options, ' ' & CC[c].pic)
  525. var includeCmd, compilePattern: string
  526. if not noAbsolutePaths(conf):
  527. # compute include paths:
  528. includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
  529. for includeDir in items(conf.cIncludes):
  530. includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
  531. compilePattern = joinPath(conf.cCompilerPath, exe)
  532. else:
  533. includeCmd = ""
  534. compilePattern = getCompilerExe(conf, c, cfile.cname)
  535. includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
  536. var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
  537. else: cfile.cname
  538. var objfile =
  539. if cfile.obj.isEmpty:
  540. if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
  541. toObjFile(conf, cf).string
  542. else:
  543. completeCfilePath(conf, toObjFile(conf, cf)).string
  544. elif noAbsolutePaths(conf):
  545. extractFilename(cfile.obj.string)
  546. else:
  547. cfile.obj.string
  548. # D files are required by nintendo switch libs for
  549. # compilation. They are basically a list of all includes.
  550. let dfile = objfile.changeFileExt(".d").quoteShell()
  551. objfile = quoteShell(objfile)
  552. let cfsh = quoteShell(cf)
  553. result = quoteShell(compilePattern % [
  554. "dfile", dfile,
  555. "file", cfsh, "objfile", objfile, "options", options,
  556. "include", includeCmd, "nim", getPrefixDir(conf).string,
  557. "lib", conf.libpath.string])
  558. add(result, ' ')
  559. addf(result, CC[c].compileTmpl, [
  560. "dfile", dfile,
  561. "file", cfsh, "objfile", objfile,
  562. "options", options, "include", includeCmd,
  563. "nim", quoteShell(getPrefixDir(conf)),
  564. "lib", quoteShell(conf.libpath),
  565. "vccplatform", vccplatform(conf)])
  566. proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
  567. result = secureHash(
  568. $secureHashFile(cfile.cname.string) &
  569. platform.OS[conf.target.targetOS].name &
  570. platform.CPU[conf.target.targetCPU].name &
  571. extccomp.CC[conf.cCompiler].name &
  572. getCompileCFileCmd(conf, cfile))
  573. proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
  574. if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
  575. return false
  576. var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
  577. var currentHash = footprint(conf, cfile)
  578. var f: File
  579. if open(f, hashFile.string, fmRead):
  580. let oldHash = parseSecureHash(f.readLine())
  581. close(f)
  582. result = oldHash != currentHash
  583. else:
  584. result = true
  585. if result:
  586. if open(f, hashFile.string, fmWrite):
  587. f.writeLine($currentHash)
  588. close(f)
  589. proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
  590. if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
  591. not externalFileChanged(conf, c):
  592. c.flags.incl CfileFlag.Cached
  593. conf.toCompile.add(c)
  594. proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
  595. var c = Cfile(nimname: splitFile(filename).name, cname: filename,
  596. obj: toObjFile(conf, completeCfilePath(conf, filename, false)),
  597. flags: {CfileFlag.External})
  598. addExternalFileToCompile(conf, c)
  599. proc compileCFiles(conf: ConfigRef; list: CfileList, script: var Rope, cmds: var TStringSeq,
  600. prettyCmds: var TStringSeq) =
  601. var currIdx = 0
  602. for it in list:
  603. # call the C compiler for the .c file:
  604. if it.flags.contains(CfileFlag.Cached): continue
  605. var compileCmd = getCompileCFileCmd(conf, it, currIdx == list.len - 1)
  606. inc currIdx
  607. if optCompileOnly notin conf.globalOptions:
  608. add(cmds, compileCmd)
  609. let (_, name, _) = splitFile(it.cname)
  610. add(prettyCmds, if hintCC in conf.notes: "CC: " & demanglePackageName(name) else: "")
  611. if optGenScript in conf.globalOptions:
  612. add(script, compileCmd)
  613. add(script, "\n")
  614. proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
  615. objfiles: string, isDllBuild: bool): string =
  616. if optGenStaticLib in conf.globalOptions:
  617. var libname: string
  618. if not conf.outFile.isEmpty:
  619. libname = conf.outFile.string.expandTilde
  620. if not libname.isAbsolute():
  621. libname = getCurrentDir() / libname
  622. else:
  623. libname = (libNameTmpl(conf) % splitFile(conf.projectName).name)
  624. result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(libname),
  625. "objfiles", objfiles]
  626. else:
  627. var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
  628. if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
  629. # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
  630. if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
  631. if noAbsolutePaths(conf): result = linkerExe
  632. else: result = joinPath(conf.cCompilerPath, linkerExe)
  633. let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows:
  634. CC[conf.cCompiler].buildGui
  635. else:
  636. ""
  637. let builddll = if isDllBuild: CC[conf.cCompiler].buildDll else: ""
  638. let exefile = quoteShell(output)
  639. when false:
  640. if optCDebug in conf.globalOptions:
  641. writeDebugInfo(exefile.changeFileExt("ndb"))
  642. # Map files are required by Nintendo Switch compilation. They are a list
  643. # of all function calls in the library and where they come from.
  644. let mapfile = quoteShell(getNimcacheDir(conf) / RelativeFile(splitFile(output).name & ".map"))
  645. let linkOptions = getLinkOptions(conf) & " " &
  646. getConfigVar(conf, conf.cCompiler, ".options.linker")
  647. var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl")
  648. if linkTmpl.len == 0:
  649. linkTmpl = CC[conf.cCompiler].linkTmpl
  650. result = quoteShell(result % ["builddll", builddll,
  651. "mapfile", mapfile,
  652. "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
  653. "exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
  654. result.add ' '
  655. addf(result, linkTmpl, ["builddll", builddll,
  656. "mapfile", mapfile,
  657. "buildgui", buildgui, "options", linkOptions,
  658. "objfiles", objfiles, "exefile", exefile,
  659. "nim", quoteShell(getPrefixDir(conf)),
  660. "lib", quoteShell(conf.libpath),
  661. "vccplatform", vccplatform(conf)])
  662. # On windows the debug information for binaries is emitted in a separate .pdb
  663. # file and the binaries (.dll and .exe) contain a full path to that .pdb file.
  664. # This is a problem for hot code reloading because even when we copy the .dll
  665. # and load the copy so the build process may overwrite the original .dll on
  666. # the disk (windows locks the files of running binaries) the copy still points
  667. # to the original .pdb (and a simple copy of the .pdb won't help). This is a
  668. # problem when a debugger is attached to the program we are hot-reloading.
  669. # This problem is nonexistent on Unix since there by default debug symbols
  670. # are embedded in the binaries so loading a copy of a .so will be fine. There
  671. # is the '/Z7' flag for the MSVC compiler to embed the debug info of source
  672. # files into their respective .obj files but the linker still produces a .pdb
  673. # when a final .dll or .exe is linked so the debug info isn't embedded.
  674. # There is also the issue that even when a .dll is unloaded the debugger
  675. # still keeps the .pdb for that .dll locked. This is a major problem and
  676. # because of this we cannot just alternate between 2 names for a .pdb file
  677. # when rebuilding a .dll - instead we need to accumulate differently named
  678. # .pdb files in the nimcache folder - this is the easiest and most reliable
  679. # way of being able to debug and rebuild the program at the same time. This
  680. # is accomplished using the /PDB:<filename> flag (there also exists the
  681. # /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are
  682. # atleast 300kb big (when linking statically to the runtime - or else 5mb+)
  683. # and will quickly accumulate. There is a hacky solution: we could try to
  684. # delete all .pdb files with a pattern and swallow exceptions.
  685. #
  686. # links about .pdb files and hot code reloading:
  687. # https://ourmachinery.com/post/dll-hot-reloading-in-theory-and-practice/
  688. # https://ourmachinery.com/post/little-machines-working-together-part-2/
  689. # https://github.com/fungos/cr
  690. # https://fungos.github.io/blog/2017/11/20/cr.h-a-simple-c-hot-reload-header-only-library/
  691. # on forcing the debugger to unlock a locked .pdb of an unloaded library:
  692. # https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
  693. # and a bit about the .pdb format in case that is ever needed:
  694. # https://github.com/crosire/blink
  695. # http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
  696. if conf.hcrOn and isVSCompatible(conf):
  697. let t = now()
  698. let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
  699. result.add " /link /PDB:" & pdb
  700. if optCDebug in conf.globalOptions and conf.cCompiler == ccVcc:
  701. result.add " /Zi /FS /Od"
  702. template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string): string =
  703. getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions)
  704. template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) =
  705. try:
  706. body
  707. except OSError:
  708. let ose = (ref OSError)(getCurrentException())
  709. if errorPrefix.len > 0:
  710. rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
  711. else:
  712. rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
  713. (ose.msg & " " & $ose.errorCode))
  714. raise
  715. proc execLinkCmd(conf: ConfigRef; linkCmd: string) =
  716. tryExceptOSErrorMessage(conf, "invocation of external linker program failed."):
  717. execExternalProgram(conf, linkCmd,
  718. if optListCmd in conf.globalOptions or conf.verbosity > 1: hintExecuting else: hintLinking)
  719. proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) =
  720. let runCb = proc (idx: int, p: Process) =
  721. let exitCode = p.peekExitCode
  722. if exitCode != 0:
  723. rawMessage(conf, errGenerated, "execution of an external compiler program '" &
  724. cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
  725. p.outputStream.readAll.strip)
  726. if conf.numberOfProcessors == 0: conf.numberOfProcessors = countProcessors()
  727. var res = 0
  728. if conf.numberOfProcessors <= 1:
  729. for i in 0 .. high(cmds):
  730. tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
  731. res = execWithEcho(conf, cmds[i])
  732. if res != 0:
  733. rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
  734. cmds[i])
  735. else:
  736. tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
  737. if optListCmd in conf.globalOptions or conf.verbosity > 1:
  738. res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath},
  739. conf.numberOfProcessors, afterRunEvent=runCb)
  740. elif conf.verbosity == 1:
  741. res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
  742. conf.numberOfProcessors, prettyCb, afterRunEvent=runCb)
  743. else:
  744. res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
  745. conf.numberOfProcessors, afterRunEvent=runCb)
  746. if res != 0:
  747. if conf.numberOfProcessors <= 1:
  748. rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
  749. cmds.join())
  750. proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
  751. # Extracting the linker.exe here is a bit hacky but the best solution
  752. # given ``buildLib``'s design.
  753. var i = 0
  754. var last = 0
  755. if cmd.len > 0 and cmd[0] == '"':
  756. inc i
  757. while i < cmd.len and cmd[i] != '"': inc i
  758. last = i
  759. inc i
  760. else:
  761. while i < cmd.len and cmd[i] != ' ': inc i
  762. last = i
  763. while i < cmd.len and cmd[i] == ' ': inc i
  764. let linkerArgs = conf.projectName & "_" & "linkerArgs.txt"
  765. let args = cmd.substr(i)
  766. # GCC's response files don't support backslashes. Junk.
  767. if conf.cCompiler == ccGcc or conf.cCompiler == ccCLang:
  768. writeFile(linkerArgs, args.replace('\\', '/'))
  769. else:
  770. writeFile(linkerArgs, args)
  771. try:
  772. execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
  773. finally:
  774. removeFile(linkerArgs)
  775. proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
  776. if noAbsolutePaths(conf): f.obj.extractFilename
  777. else: f.obj.string
  778. proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): AbsoluteFile =
  779. let basename = splitFile(objFile).name
  780. let targetName = if isMain: basename & ".exe"
  781. else: platform.OS[conf.target.targetOS].dllFrmt % basename
  782. result = conf.getNimcacheDir / RelativeFile(targetName)
  783. proc callCCompiler*(conf: ConfigRef) =
  784. var
  785. linkCmd: string
  786. if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
  787. return # speed up that call if only compiling and no script shall be
  788. # generated
  789. #var c = cCompiler
  790. var script: Rope = nil
  791. var cmds: TStringSeq = @[]
  792. var prettyCmds: TStringSeq = @[]
  793. let prettyCb = proc (idx: int) =
  794. when declared(echo):
  795. let cmd = prettyCmds[idx]
  796. if cmd != "": echo cmd
  797. compileCFiles(conf, conf.toCompile, script, cmds, prettyCmds)
  798. if optCompileOnly notin conf.globalOptions:
  799. execCmdsInParallel(conf, cmds, prettyCb)
  800. if optNoLinking notin conf.globalOptions:
  801. # call the linker:
  802. var objfiles = ""
  803. for it in conf.externalToLink:
  804. let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
  805. add(objfiles, ' ')
  806. add(objfiles, quoteShell(
  807. addFileExt(objFile, CC[conf.cCompiler].objExt)))
  808. if conf.hcrOn: # lets assume that optCompileOnly isn't on
  809. cmds = @[]
  810. let mainFileIdx = conf.toCompile.len - 1
  811. for idx, x in conf.toCompile:
  812. # don't relink each of the many binaries (one for each source file) if the nim code is
  813. # cached because that would take too much time for small changes - the only downside to
  814. # this is that if an external-to-link file changes the final target wouldn't be relinked
  815. if x.flags.contains(CfileFlag.Cached): continue
  816. # we pass each object file as if it is the project file - a .dll will be created for each such
  817. # object file in the nimcache directory, and only in the case of the main project file will
  818. # there be probably an executable (if the project is such) which will be copied out of the nimcache
  819. let objFile = conf.getObjFilePath(x)
  820. let buildDll = idx != mainFileIdx
  821. let linkTarget = conf.hcrLinkTargetName(objFile, not buildDll)
  822. add(cmds, getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll))
  823. # try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache
  824. # for more info check the comment inside of getLinkCmd() where the /PDB:<filename> MSVC flag is used
  825. if isVSCompatible(conf):
  826. for pdb in walkFiles(objFile & ".*.pdb"):
  827. discard tryRemoveFile(pdb)
  828. # execute link commands in parallel - output will be a bit different
  829. # if it fails than that from execLinkCmd() but that doesn't matter
  830. prettyCmds = map(prettyCmds, proc (curr: string): string = return curr.replace("CC", "Link"))
  831. execCmdsInParallel(conf, cmds, prettyCb)
  832. # only if not cached - copy the resulting main file from the nimcache folder to its originally intended destination
  833. if not conf.toCompile[mainFileIdx].flags.contains(CfileFlag.Cached):
  834. let mainObjFile = getObjFilePath(conf, conf.toCompile[mainFileIdx])
  835. var src = conf.hcrLinkTargetName(mainObjFile, true)
  836. var dst = conf.prepareToWriteOutput
  837. copyFileWithPermissions(src.string, dst.string)
  838. else:
  839. for x in conf.toCompile:
  840. let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj.string
  841. add(objfiles, ' ')
  842. add(objfiles, quoteShell(objFile))
  843. let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput
  844. else: AbsoluteFile(conf.projectName)
  845. linkCmd = getLinkCmd(conf, mainOutput, objfiles)
  846. if optCompileOnly notin conf.globalOptions:
  847. const MaxCmdLen = when defined(windows): 8_000 else: 32_000
  848. if linkCmd.len > MaxCmdLen:
  849. # Windows's command line limit is about 8K (don't laugh...) so C compilers on
  850. # Windows support a feature where the command line can be passed via ``@linkcmd``
  851. # to them.
  852. linkViaResponseFile(conf, linkCmd)
  853. else:
  854. execLinkCmd(conf, linkCmd)
  855. else:
  856. linkCmd = ""
  857. if optGenScript in conf.globalOptions:
  858. add(script, linkCmd)
  859. add(script, "\n")
  860. generateScript(conf, script)
  861. #from json import escapeJson
  862. import json
  863. proc writeJsonBuildInstructions*(conf: ConfigRef) =
  864. template lit(x: untyped) = f.write x
  865. template str(x: untyped) =
  866. when compiles(escapeJson(x, buf)):
  867. buf.setLen 0
  868. escapeJson(x, buf)
  869. f.write buf
  870. else:
  871. f.write escapeJson(x)
  872. proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
  873. var pastStart = false
  874. for it in clist:
  875. if CfileFlag.Cached in it.flags: continue
  876. let compileCmd = getCompileCFileCmd(conf, it)
  877. if pastStart: lit "],\L"
  878. lit "["
  879. str it.cname.string
  880. lit ", "
  881. str compileCmd
  882. pastStart = true
  883. lit "]\L"
  884. proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList;
  885. llist: seq[string]) =
  886. var pastStart = false
  887. for it in llist:
  888. let objfile = if noAbsolutePaths(conf): it.extractFilename
  889. else: it
  890. let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt)
  891. add(objfiles, ' ')
  892. add(objfiles, objstr)
  893. if pastStart: lit ",\L"
  894. str objstr
  895. pastStart = true
  896. for it in clist:
  897. let objstr = quoteShell(it.obj)
  898. add(objfiles, ' ')
  899. add(objfiles, objstr)
  900. if pastStart: lit ",\L"
  901. str objstr
  902. pastStart = true
  903. lit "\L"
  904. var buf = newStringOfCap(50)
  905. let jsonFile = conf.getNimcacheDir / RelativeFile(conf.projectName & ".json")
  906. var f: File
  907. if open(f, jsonFile.string, fmWrite):
  908. lit "{\"compile\":[\L"
  909. cfiles(conf, f, buf, conf.toCompile, false)
  910. lit "],\L\"link\":[\L"
  911. var objfiles = ""
  912. # XXX add every file here that is to link
  913. linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink)
  914. lit "],\L\"linkcmd\": "
  915. str getLinkCmd(conf, conf.absOutFile, objfiles)
  916. lit "\L}\L"
  917. close(f)
  918. proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) =
  919. let jsonFile = toGeneratedFile(conf, projectfile, "json")
  920. try:
  921. let data = json.parseFile(jsonFile.string)
  922. let toCompile = data["compile"]
  923. doAssert toCompile.kind == JArray
  924. var cmds: TStringSeq = @[]
  925. var prettyCmds: TStringSeq = @[]
  926. for c in toCompile:
  927. doAssert c.kind == JArray
  928. doAssert c.len >= 2
  929. add(cmds, c[1].getStr)
  930. let (_, name, _) = splitFile(c[0].getStr)
  931. add(prettyCmds, "CC: " & name)
  932. let prettyCb = proc (idx: int) =
  933. when declared(echo):
  934. echo prettyCmds[idx]
  935. execCmdsInParallel(conf, cmds, prettyCb)
  936. let linkCmd = data["linkcmd"]
  937. doAssert linkCmd.kind == JString
  938. execLinkCmd(conf, linkCmd.getStr)
  939. except:
  940. when declared(echo):
  941. echo getCurrentException().getStackTrace()
  942. quit "error evaluating JSON file: " & jsonFile.string
  943. proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
  944. for it in list:
  945. addf(result, "--file:r\"$1\"$N", [rope(it.cname.string)])
  946. proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
  947. if optGenMapping notin conf.globalOptions: return
  948. var code = rope("[C_Files]\n")
  949. add(code, genMappingFiles(conf, conf.toCompile))
  950. add(code, "\n[C_Compiler]\nFlags=")
  951. add(code, strutils.escape(getCompileOptions(conf)))
  952. add(code, "\n[Linker]\nFlags=")
  953. add(code, strutils.escape(getLinkOptions(conf) & " " &
  954. getConfigVar(conf, conf.cCompiler, ".options.linker")))
  955. add(code, "\n[Environment]\nlibpath=")
  956. add(code, strutils.escape(conf.libpath.string))
  957. addf(code, "\n[Symbols]$n$1", [symbolMapping])
  958. let filename = conf.projectPath / RelativeFile"mapping.txt"
  959. if not writeRope(code, filename):
  960. rawMessage(conf, errGenerated, "could not write to file: " & filename.string)