ccgmerge.nim 8.3 KB

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