ccgmerge.nim 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the merge operation of 2 different C files. This
  10. ## is needed for incremental compilation.
  11. import
  12. ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
  13. intsets, platform, llstream, tables, sighashes
  14. # Careful! Section marks need to contain a tabulator so that they cannot
  15. # be part of C string literals.
  16. const
  17. CFileSectionNames: array[TCFileSection, string] = [
  18. cfsMergeInfo: "",
  19. cfsHeaders: "NIM_merge_HEADERS",
  20. cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
  21. cfsTypes: "NIM_merge_TYPES",
  22. cfsSeqTypes: "NIM_merge_SEQ_TYPES",
  23. cfsFieldInfo: "NIM_merge_FIELD_INFO",
  24. cfsTypeInfo: "NIM_merge_TYPE_INFO",
  25. cfsProcHeaders: "NIM_merge_PROC_HEADERS",
  26. cfsVars: "NIM_merge_VARS",
  27. cfsData: "NIM_merge_DATA",
  28. cfsProcs: "NIM_merge_PROCS",
  29. cfsInitProc: "NIM_merge_INIT_PROC",
  30. cfsTypeInit1: "NIM_merge_TYPE_INIT1",
  31. cfsTypeInit2: "NIM_merge_TYPE_INIT2",
  32. cfsTypeInit3: "NIM_merge_TYPE_INIT3",
  33. cfsDebugInit: "NIM_merge_DEBUG_INIT",
  34. cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
  35. cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
  36. ]
  37. CProcSectionNames: array[TCProcSection, string] = [
  38. cpsLocals: "NIM_merge_PROC_LOCALS",
  39. cpsInit: "NIM_merge_PROC_INIT",
  40. cpsStmts: "NIM_merge_PROC_BODY"
  41. ]
  42. NimMergeEndMark = "/*\tNIM_merge_END:*/"
  43. proc genSectionStart*(fs: TCFileSection): Rope =
  44. if compilationCachePresent:
  45. result = rope(tnl)
  46. add(result, "/*\t")
  47. add(result, CFileSectionNames[fs])
  48. add(result, ":*/")
  49. add(result, tnl)
  50. proc genSectionEnd*(fs: TCFileSection): Rope =
  51. if compilationCachePresent:
  52. result = rope(NimMergeEndMark & tnl)
  53. proc genSectionStart*(ps: TCProcSection): Rope =
  54. if compilationCachePresent:
  55. result = rope(tnl)
  56. add(result, "/*\t")
  57. add(result, CProcSectionNames[ps])
  58. add(result, ":*/")
  59. add(result, tnl)
  60. proc genSectionEnd*(ps: TCProcSection): Rope =
  61. if compilationCachePresent:
  62. result = rope(NimMergeEndMark & tnl)
  63. proc writeTypeCache(a: TypeCache, s: var string) =
  64. var i = 0
  65. for id, value in pairs(a):
  66. if i == 10:
  67. i = 0
  68. s.add(tnl)
  69. else:
  70. s.add(' ')
  71. encodeStr($id, s)
  72. s.add(':')
  73. encodeStr($value, s)
  74. inc i
  75. s.add('}')
  76. proc writeIntSet(a: IntSet, s: var string) =
  77. var i = 0
  78. for x in items(a):
  79. if i == 10:
  80. i = 0
  81. s.add(tnl)
  82. else:
  83. s.add(' ')
  84. encodeVInt(x, s)
  85. inc i
  86. s.add('}')
  87. proc genMergeInfo*(m: BModule): Rope =
  88. if optSymbolFiles notin gGlobalOptions: return nil
  89. var s = "/*\tNIM_merge_INFO:"
  90. s.add(tnl)
  91. s.add("typeCache:{")
  92. writeTypeCache(m.typeCache, s)
  93. s.add("declared:{")
  94. writeIntSet(m.declaredThings, s)
  95. when false:
  96. s.add("typeInfo:{")
  97. writeIntSet(m.typeInfoMarker, s)
  98. s.add("labels:")
  99. encodeVInt(m.labels, s)
  100. s.add(" flags:")
  101. encodeVInt(cast[int](m.flags), s)
  102. s.add(tnl)
  103. s.add("*/")
  104. result = s.rope
  105. template `^`(pos: int): untyped = L.buf[pos]
  106. proc skipWhite(L: var TBaseLexer) =
  107. var pos = L.bufpos
  108. while true:
  109. case ^pos
  110. of CR: pos = nimlexbase.handleCR(L, pos)
  111. of LF: pos = nimlexbase.handleLF(L, pos)
  112. of ' ': inc pos
  113. else: break
  114. L.bufpos = pos
  115. proc skipUntilCmd(L: var TBaseLexer) =
  116. var pos = L.bufpos
  117. while true:
  118. case ^pos
  119. of CR: pos = nimlexbase.handleCR(L, pos)
  120. of LF: pos = nimlexbase.handleLF(L, pos)
  121. of '\0': break
  122. of '/':
  123. if ^(pos+1) == '*' and ^(pos+2) == '\t':
  124. inc pos, 3
  125. break
  126. inc pos
  127. else: inc pos
  128. L.bufpos = pos
  129. proc atEndMark(buf: cstring, pos: int): bool =
  130. var s = 0
  131. while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
  132. result = s == NimMergeEndMark.len
  133. proc readVerbatimSection(L: var TBaseLexer): Rope =
  134. var pos = L.bufpos
  135. var buf = L.buf
  136. var r = newStringOfCap(30_000)
  137. while true:
  138. case buf[pos]
  139. of CR:
  140. pos = nimlexbase.handleCR(L, pos)
  141. buf = L.buf
  142. r.add(tnl)
  143. of LF:
  144. pos = nimlexbase.handleLF(L, pos)
  145. buf = L.buf
  146. r.add(tnl)
  147. of '\0':
  148. internalError("ccgmerge: expected: " & NimMergeEndMark)
  149. break
  150. else:
  151. if atEndMark(buf, pos):
  152. inc pos, NimMergeEndMark.len
  153. break
  154. r.add(buf[pos])
  155. inc pos
  156. L.bufpos = pos
  157. result = r.rope
  158. proc readKey(L: var TBaseLexer, result: var string) =
  159. var pos = L.bufpos
  160. var buf = L.buf
  161. setLen(result, 0)
  162. while buf[pos] in IdentChars:
  163. result.add(buf[pos])
  164. inc pos
  165. if buf[pos] != ':': internalError("ccgmerge: ':' expected")
  166. L.bufpos = pos + 1 # skip ':'
  167. proc newFakeType(id: int): PType =
  168. new(result)
  169. result.id = id
  170. proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
  171. if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
  172. inc L.bufpos
  173. while ^L.bufpos != '}':
  174. skipWhite(L)
  175. var key = decodeStr(L.buf, L.bufpos)
  176. if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
  177. inc L.bufpos
  178. var value = decodeStr(L.buf, L.bufpos)
  179. # XXX implement me
  180. when false:
  181. idTablePut(result, newFakeType(key), value.rope)
  182. inc L.bufpos
  183. proc readIntSet(L: var TBaseLexer, result: var IntSet) =
  184. if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
  185. inc L.bufpos
  186. while ^L.bufpos != '}':
  187. skipWhite(L)
  188. var key = decodeVInt(L.buf, L.bufpos)
  189. result.incl(key)
  190. inc L.bufpos
  191. proc processMergeInfo(L: var TBaseLexer, m: BModule) =
  192. var k = newStringOfCap("typeCache".len)
  193. while true:
  194. skipWhite(L)
  195. if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
  196. inc(L.bufpos, 2)
  197. break
  198. readKey(L, k)
  199. case k
  200. of "typeCache": readTypeCache(L, m.typeCache)
  201. of "declared": readIntSet(L, m.declaredThings)
  202. of "typeInfo":
  203. when false: readIntSet(L, m.typeInfoMarker)
  204. of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
  205. of "flags":
  206. m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
  207. else: internalError("ccgmerge: unknown key: " & k)
  208. when not defined(nimhygiene):
  209. {.pragma: inject.}
  210. template withCFile(cfilename: string, body: untyped) =
  211. var s = llStreamOpen(cfilename, fmRead)
  212. if s == nil: return
  213. var L {.inject.}: TBaseLexer
  214. openBaseLexer(L, s)
  215. var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
  216. while true:
  217. skipUntilCmd(L)
  218. if ^L.bufpos == '\0': break
  219. body
  220. closeBaseLexer(L)
  221. proc readMergeInfo*(cfilename: string, m: BModule) =
  222. ## reads the merge meta information into `m`.
  223. withCFile(cfilename):
  224. readKey(L, k)
  225. if k == "NIM_merge_INFO":
  226. processMergeInfo(L, m)
  227. break
  228. type
  229. TMergeSections = object
  230. f: TCFileSections
  231. p: TCProcSections
  232. proc readMergeSections(cfilename: string, m: var TMergeSections) =
  233. ## reads the merge sections into `m`.
  234. withCFile(cfilename):
  235. readKey(L, k)
  236. if k == "NIM_merge_INFO":
  237. discard
  238. elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
  239. inc(L.bufpos, 2)
  240. # read back into section
  241. skipWhite(L)
  242. var verbatim = readVerbatimSection(L)
  243. skipWhite(L)
  244. var sectionA = CFileSectionNames.find(k)
  245. if sectionA > 0 and sectionA <= high(TCFileSection).int:
  246. m.f[TCFileSection(sectionA)] = verbatim
  247. else:
  248. var sectionB = CProcSectionNames.find(k)
  249. if sectionB >= 0 and sectionB <= high(TCProcSection).int:
  250. m.p[TCProcSection(sectionB)] = verbatim
  251. else:
  252. internalError("ccgmerge: unknown section: " & k)
  253. else:
  254. internalError("ccgmerge: '*/' expected")
  255. proc mergeRequired*(m: BModule): bool =
  256. for i in cfsHeaders..cfsProcs:
  257. if m.s[i] != nil:
  258. #echo "not empty: ", i, " ", m.s[i]
  259. return true
  260. for i in low(TCProcSection)..high(TCProcSection):
  261. if m.initProc.s(i) != nil:
  262. #echo "not empty: ", i, " ", m.initProc.s[i]
  263. return true
  264. proc mergeFiles*(cfilename: string, m: BModule) =
  265. ## merges the C file with the old version on hard disc.
  266. var old: TMergeSections
  267. readMergeSections(cfilename, old)
  268. # do the merge; old section before new section:
  269. for i in low(TCFileSection)..high(TCFileSection):
  270. m.s[i] = old.f[i] & m.s[i]
  271. for i in low(TCProcSection)..high(TCProcSection):
  272. m.initProc.s(i) = old.p[i] & m.initProc.s(i)