ccgmerge.nim 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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, modulegraphs, pathutils
  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. cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
  21. cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
  22. cfsTypes: "NIM_merge_TYPES",
  23. cfsSeqTypes: "NIM_merge_SEQ_TYPES",
  24. cfsFieldInfo: "NIM_merge_FIELD_INFO",
  25. cfsTypeInfo: "NIM_merge_TYPE_INFO",
  26. cfsProcHeaders: "NIM_merge_PROC_HEADERS",
  27. cfsVars: "NIM_merge_VARS",
  28. cfsData: "NIM_merge_DATA",
  29. cfsProcs: "NIM_merge_PROCS",
  30. cfsInitProc: "NIM_merge_INIT_PROC",
  31. cfsDatInitProc: "NIM_merge_DATINIT_PROC",
  32. cfsTypeInit1: "NIM_merge_TYPE_INIT1",
  33. cfsTypeInit2: "NIM_merge_TYPE_INIT2",
  34. cfsTypeInit3: "NIM_merge_TYPE_INIT3",
  35. cfsDebugInit: "NIM_merge_DEBUG_INIT",
  36. cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
  37. cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
  38. ]
  39. CProcSectionNames: array[TCProcSection, string] = [
  40. cpsLocals: "NIM_merge_PROC_LOCALS",
  41. cpsInit: "NIM_merge_PROC_INIT",
  42. cpsStmts: "NIM_merge_PROC_BODY"
  43. ]
  44. NimMergeEndMark = "/*\tNIM_merge_END:*/"
  45. proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
  46. if compilationCachePresent(conf):
  47. result = nil
  48. add(result, "\n/*\t")
  49. add(result, CFileSectionNames[fs])
  50. add(result, ":*/\n")
  51. proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
  52. if compilationCachePresent(conf):
  53. result = rope(NimMergeEndMark & "\n")
  54. proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
  55. if compilationCachePresent(conf):
  56. result = rope("")
  57. add(result, "\n/*\t")
  58. add(result, CProcSectionNames[ps])
  59. add(result, ":*/\n")
  60. proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
  61. if compilationCachePresent(conf):
  62. result = rope(NimMergeEndMark & "\n")
  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('\L')
  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('\L')
  82. else:
  83. s.add(' ')
  84. encodeVInt(x, s)
  85. inc i
  86. s.add('}')
  87. proc genMergeInfo*(m: BModule): Rope =
  88. if not compilationCachePresent(m.config): return nil
  89. var s = "/*\tNIM_merge_INFO:\n"
  90. s.add("typeCache:{")
  91. writeTypeCache(m.typeCache, s)
  92. s.add("declared:{")
  93. writeIntSet(m.declaredThings, s)
  94. when false:
  95. s.add("typeInfo:{")
  96. writeIntSet(m.typeInfoMarker, s)
  97. s.add("labels:")
  98. encodeVInt(m.labels, s)
  99. s.add(" flags:")
  100. encodeVInt(cast[int](m.flags), s)
  101. s.add("\n*/")
  102. result = s.rope
  103. template `^`(pos: int): untyped = L.buf[pos]
  104. proc skipWhite(L: var TBaseLexer) =
  105. var pos = L.bufpos
  106. while true:
  107. case ^pos
  108. of CR: pos = nimlexbase.handleCR(L, pos)
  109. of LF: pos = nimlexbase.handleLF(L, pos)
  110. of ' ': inc pos
  111. else: break
  112. L.bufpos = pos
  113. proc skipUntilCmd(L: var TBaseLexer) =
  114. var pos = L.bufpos
  115. while true:
  116. case ^pos
  117. of CR: pos = nimlexbase.handleCR(L, pos)
  118. of LF: pos = nimlexbase.handleLF(L, pos)
  119. of '\0': break
  120. of '/':
  121. if ^(pos+1) == '*' and ^(pos+2) == '\t':
  122. inc pos, 3
  123. break
  124. inc pos
  125. else: inc pos
  126. L.bufpos = pos
  127. proc atEndMark(buf: cstring, pos: int): bool =
  128. var s = 0
  129. while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
  130. result = s == NimMergeEndMark.len
  131. proc readVerbatimSection(L: var TBaseLexer): Rope =
  132. var pos = L.bufpos
  133. var r = newStringOfCap(30_000)
  134. while true:
  135. case L.buf[pos]
  136. of CR:
  137. pos = nimlexbase.handleCR(L, pos)
  138. r.add('\L')
  139. of LF:
  140. pos = nimlexbase.handleLF(L, pos)
  141. r.add('\L')
  142. of '\0':
  143. doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
  144. break
  145. else:
  146. if atEndMark(L.buf, pos):
  147. inc pos, NimMergeEndMark.len
  148. break
  149. r.add(L.buf[pos])
  150. inc pos
  151. L.bufpos = pos
  152. result = r.rope
  153. proc readKey(L: var TBaseLexer, result: var string) =
  154. var pos = L.bufpos
  155. setLen(result, 0)
  156. while L.buf[pos] in IdentChars:
  157. result.add(L.buf[pos])
  158. inc pos
  159. if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
  160. L.bufpos = pos + 1 # skip ':'
  161. proc newFakeType(id: int): PType =
  162. new(result)
  163. result.id = id
  164. proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
  165. if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
  166. inc L.bufpos
  167. while ^L.bufpos != '}':
  168. skipWhite(L)
  169. var key = decodeStr(L.buf, L.bufpos)
  170. if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
  171. inc L.bufpos
  172. var value = decodeStr(L.buf, L.bufpos)
  173. # XXX implement me
  174. when false:
  175. idTablePut(result, newFakeType(key), value.rope)
  176. inc L.bufpos
  177. proc readIntSet(L: var TBaseLexer, result: var IntSet) =
  178. if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
  179. inc L.bufpos
  180. while ^L.bufpos != '}':
  181. skipWhite(L)
  182. var key = decodeVInt(L.buf, L.bufpos)
  183. result.incl(key)
  184. inc L.bufpos
  185. proc processMergeInfo(L: var TBaseLexer, m: BModule) =
  186. var k = newStringOfCap("typeCache".len)
  187. while true:
  188. skipWhite(L)
  189. if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
  190. inc(L.bufpos, 2)
  191. break
  192. readKey(L, k)
  193. case k
  194. of "typeCache": readTypeCache(L, m.typeCache)
  195. of "declared": readIntSet(L, m.declaredThings)
  196. of "typeInfo":
  197. when false: readIntSet(L, m.typeInfoMarker)
  198. of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
  199. of "flags":
  200. m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
  201. else: doAssert(false, "ccgmerge: unknown key: " & k)
  202. when not defined(nimhygiene):
  203. {.pragma: inject.}
  204. template withCFile(cfilename: AbsoluteFile, body: untyped) =
  205. var s = llStreamOpen(cfilename, fmRead)
  206. if s == nil: return
  207. var L {.inject.}: TBaseLexer
  208. openBaseLexer(L, s)
  209. var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
  210. while true:
  211. skipUntilCmd(L)
  212. if ^L.bufpos == '\0': break
  213. body
  214. closeBaseLexer(L)
  215. proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
  216. ## reads the merge meta information into `m`.
  217. withCFile(cfilename):
  218. readKey(L, k)
  219. if k == "NIM_merge_INFO":
  220. processMergeInfo(L, m)
  221. break
  222. type
  223. TMergeSections = object
  224. f: TCFileSections
  225. p: TCProcSections
  226. proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
  227. ## reads the merge sections into `m`.
  228. withCFile(cfilename):
  229. readKey(L, k)
  230. if k == "NIM_merge_INFO":
  231. discard
  232. elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
  233. inc(L.bufpos, 2)
  234. # read back into section
  235. skipWhite(L)
  236. var verbatim = readVerbatimSection(L)
  237. skipWhite(L)
  238. var sectionA = CFileSectionNames.find(k)
  239. if sectionA > 0 and sectionA <= high(TCFileSection).int:
  240. m.f[TCFileSection(sectionA)] = verbatim
  241. else:
  242. var sectionB = CProcSectionNames.find(k)
  243. if sectionB >= 0 and sectionB <= high(TCProcSection).int:
  244. m.p[TCProcSection(sectionB)] = verbatim
  245. else:
  246. doAssert(false, "ccgmerge: unknown section: " & k)
  247. else:
  248. doAssert(false, "ccgmerge: '*/' expected")
  249. proc mergeRequired*(m: BModule): bool =
  250. for i in cfsHeaders..cfsProcs:
  251. if m.s[i] != nil:
  252. #echo "not empty: ", i, " ", m.s[i]
  253. return true
  254. for i in low(TCProcSection)..high(TCProcSection):
  255. if m.initProc.s(i) != nil:
  256. #echo "not empty: ", i, " ", m.initProc.s[i]
  257. return true
  258. proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
  259. ## merges the C file with the old version on hard disc.
  260. var old: TMergeSections
  261. readMergeSections(cfilename, old)
  262. # do the merge; old section before new section:
  263. for i in low(TCFileSection)..high(TCFileSection):
  264. m.s[i] = old.f[i] & m.s[i]
  265. for i in low(TCProcSection)..high(TCProcSection):
  266. m.initProc.s(i) = old.p[i] & m.initProc.s(i)