nimconf.nim 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 handles the reading of the config file.
  10. import
  11. llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
  12. options, idents, wordrecg, strtabs
  13. # ---------------- configuration file parser -----------------------------
  14. # we use Nim's scanner here to save space and work
  15. proc ppGetTok(L: var TLexer, tok: var TToken) =
  16. # simple filter
  17. rawGetTok(L, tok)
  18. while tok.tokType in {tkComment}: rawGetTok(L, tok)
  19. proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool
  20. proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
  21. if tok.tokType == tkParLe:
  22. ppGetTok(L, tok)
  23. result = parseExpr(L, tok, config)
  24. if tok.tokType == tkParRi: ppGetTok(L, tok)
  25. else: lexMessage(L, errTokenExpected, "\')\'")
  26. elif tok.ident.id == ord(wNot):
  27. ppGetTok(L, tok)
  28. result = not parseAtom(L, tok, config)
  29. else:
  30. result = isDefined(tok.ident)
  31. ppGetTok(L, tok)
  32. proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
  33. result = parseAtom(L, tok, config)
  34. while tok.ident.id == ord(wAnd):
  35. ppGetTok(L, tok) # skip "and"
  36. var b = parseAtom(L, tok, config)
  37. result = result and b
  38. proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
  39. result = parseAndExpr(L, tok, config)
  40. while tok.ident.id == ord(wOr):
  41. ppGetTok(L, tok) # skip "or"
  42. var b = parseAndExpr(L, tok, config)
  43. result = result or b
  44. proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
  45. ppGetTok(L, tok) # skip 'if' or 'elif'
  46. result = parseExpr(L, tok, config)
  47. if tok.tokType == tkColon: ppGetTok(L, tok)
  48. else: lexMessage(L, errTokenExpected, "\':\'")
  49. var condStack: seq[bool] = @[]
  50. proc doEnd(L: var TLexer, tok: var TToken) =
  51. if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
  52. ppGetTok(L, tok) # skip 'end'
  53. setLen(condStack, high(condStack))
  54. type
  55. TJumpDest = enum
  56. jdEndif, jdElseEndif
  57. proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef)
  58. proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) =
  59. if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
  60. ppGetTok(L, tok)
  61. if tok.tokType == tkColon: ppGetTok(L, tok)
  62. if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config)
  63. proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) =
  64. if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
  65. var res = evalppIf(L, tok, config)
  66. if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config)
  67. else: condStack[high(condStack)] = true
  68. proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) =
  69. var nestedIfs = 0
  70. while true:
  71. if tok.ident != nil and tok.ident.s == "@":
  72. ppGetTok(L, tok)
  73. case whichKeyword(tok.ident)
  74. of wIf:
  75. inc(nestedIfs)
  76. of wElse:
  77. if dest == jdElseEndif and nestedIfs == 0:
  78. doElse(L, tok, config)
  79. break
  80. of wElif:
  81. if dest == jdElseEndif and nestedIfs == 0:
  82. doElif(L, tok, config)
  83. break
  84. of wEnd:
  85. if nestedIfs == 0:
  86. doEnd(L, tok)
  87. break
  88. if nestedIfs > 0: dec(nestedIfs)
  89. else:
  90. discard
  91. ppGetTok(L, tok)
  92. elif tok.tokType == tkEof:
  93. lexMessage(L, errTokenExpected, "@end")
  94. else:
  95. ppGetTok(L, tok)
  96. proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
  97. ppGetTok(L, tok) # skip @
  98. case whichKeyword(tok.ident)
  99. of wIf:
  100. setLen(condStack, len(condStack) + 1)
  101. let res = evalppIf(L, tok, config)
  102. condStack[high(condStack)] = res
  103. if not res: jumpToDirective(L, tok, jdElseEndif, config)
  104. of wElif: doElif(L, tok, config)
  105. of wElse: doElse(L, tok, config)
  106. of wEnd: doEnd(L, tok)
  107. of wWrite:
  108. ppGetTok(L, tok)
  109. msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
  110. {useEnvironment, useKey}))
  111. ppGetTok(L, tok)
  112. else:
  113. case tok.ident.s.normalize
  114. of "putenv":
  115. ppGetTok(L, tok)
  116. var key = tokToStr(tok)
  117. ppGetTok(L, tok)
  118. os.putEnv(key, tokToStr(tok))
  119. ppGetTok(L, tok)
  120. of "prependenv":
  121. ppGetTok(L, tok)
  122. var key = tokToStr(tok)
  123. ppGetTok(L, tok)
  124. os.putEnv(key, tokToStr(tok) & os.getEnv(key))
  125. ppGetTok(L, tok)
  126. of "appendenv":
  127. ppGetTok(L, tok)
  128. var key = tokToStr(tok)
  129. ppGetTok(L, tok)
  130. os.putEnv(key, os.getEnv(key) & tokToStr(tok))
  131. ppGetTok(L, tok)
  132. else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
  133. proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
  134. ppGetTok(L, tok)
  135. while tok.ident != nil and tok.ident.s == "@":
  136. parseDirective(L, tok, config) # else: give the token to the parser
  137. proc checkSymbol(L: TLexer, tok: TToken) =
  138. if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
  139. lexMessage(L, errIdentifierExpected, tokToStr(tok))
  140. proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
  141. if tok.ident.s == "-" or tok.ident.s == "--":
  142. confTok(L, tok, config) # skip unnecessary prefix
  143. var info = getLineInfo(L, tok) # save for later in case of an error
  144. checkSymbol(L, tok)
  145. var s = tokToStr(tok)
  146. confTok(L, tok, config) # skip symbol
  147. var val = ""
  148. while tok.tokType == tkDot:
  149. add(s, '.')
  150. confTok(L, tok, config)
  151. checkSymbol(L, tok)
  152. add(s, tokToStr(tok))
  153. confTok(L, tok, config)
  154. if tok.tokType == tkBracketLe:
  155. # BUGFIX: val, not s!
  156. # BUGFIX: do not copy '['!
  157. confTok(L, tok, config)
  158. checkSymbol(L, tok)
  159. add(val, tokToStr(tok))
  160. confTok(L, tok, config)
  161. if tok.tokType == tkBracketRi: confTok(L, tok, config)
  162. else: lexMessage(L, errTokenExpected, "']'")
  163. add(val, ']')
  164. let percent = tok.ident != nil and tok.ident.s == "%="
  165. if tok.tokType in {tkColon, tkEquals} or percent:
  166. if len(val) > 0: add(val, ':')
  167. confTok(L, tok, config) # skip ':' or '=' or '%'
  168. checkSymbol(L, tok)
  169. add(val, tokToStr(tok))
  170. confTok(L, tok, config) # skip symbol
  171. while tok.ident != nil and tok.ident.s == "&":
  172. confTok(L, tok, config)
  173. checkSymbol(L, tok)
  174. add(val, tokToStr(tok))
  175. confTok(L, tok, config)
  176. if percent:
  177. processSwitch(s, strtabs.`%`(val, options.gConfigVars,
  178. {useEnvironment, useEmpty}), passPP, info, config)
  179. else:
  180. processSwitch(s, val, passPP, info, config)
  181. proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
  182. var
  183. L: TLexer
  184. tok: TToken
  185. stream: PLLStream
  186. stream = llStreamOpen(filename, fmRead)
  187. if stream != nil:
  188. initToken(tok)
  189. openLexer(L, filename, stream, cache)
  190. tok.tokType = tkEof # to avoid a pointless warning
  191. confTok(L, tok, config) # read in the first token
  192. while tok.tokType != tkEof: parseAssignment(L, tok, config)
  193. if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
  194. closeLexer(L)
  195. rawMessage(hintConf, filename)
  196. proc getUserConfigPath(filename: string): string =
  197. result = joinPath(getConfigDir(), filename)
  198. proc getSystemConfigPath(filename: string): string =
  199. # try standard configuration file (installation did not distribute files
  200. # the UNIX way)
  201. let p = getPrefixDir()
  202. result = joinPath([p, "config", filename])
  203. when defined(unix):
  204. if not existsFile(result): result = joinPath([p, "etc", filename])
  205. if not existsFile(result): result = "/etc/" & filename
  206. proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) =
  207. setDefaultLibpath()
  208. if optSkipConfigFile notin gGlobalOptions:
  209. readConfigFile(getSystemConfigPath(cfg), cache, config)
  210. if optSkipUserConfigFile notin gGlobalOptions:
  211. readConfigFile(getUserConfigPath(cfg), cache, config)
  212. var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
  213. if optSkipParentConfigFiles notin gGlobalOptions:
  214. for dir in parentDirs(pd, fromRoot=true, inclusive=false):
  215. readConfigFile(dir / cfg, cache, config)
  216. if optSkipProjConfigFile notin gGlobalOptions:
  217. readConfigFile(pd / cfg, cache, config)
  218. if gProjectName.len != 0:
  219. # new project wide config file:
  220. var projectConfig = changeFileExt(gProjectFull, "nimcfg")
  221. if not fileExists(projectConfig):
  222. projectConfig = changeFileExt(gProjectFull, "nim.cfg")
  223. if not fileExists(projectConfig):
  224. projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
  225. if fileExists(projectConfig):
  226. rawMessage(warnDeprecated, projectConfig)
  227. readConfigFile(projectConfig, cache, config)
  228. proc loadConfigs*(cfg: string; config: ConfigRef = nil) =
  229. # for backwards compatibility only.
  230. loadConfigs(cfg, newIdentCache(), config)