ccgmerge.nim 8.4 KB

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